The Labs \ Source Viewer \ SSCLI \ System.Globalization \ CultureData

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. namespace System.Globalization
  16. {
  17.     using System;
  18.     using System.IO;
  19.     using System.Runtime.InteropServices;
  20.     using System.Text;
  21.     using System.Threading;
  22.     using System.Runtime.CompilerServices;
  23.     using System.Runtime.ConstrainedExecution;
  24.     using System.Collections;
  25.     using Microsoft.Win32.SafeHandles;
  26.     using System.Runtime.Serialization;
  27.     using System.Runtime.Versioning;
  28.    
  29.     // Enum for the IFLAGS field
  30.     [Flags()]
  31.     internal enum CultureFlags
  32.     {
  33.         IsSpecificCulture = 1
  34.     }
  35.    
  36. /*=============================================================================
  37.     *
  38.     * Data record for CultureInfo classes.  Used by System.Globalization.CultureInfo.
  39.     *
  40.     * NOTE: ALL of the data table/user override/OS version, etc. hacks related to
  41.     *      the data should be in here.  DTFI, CI, etc. should have NO knowledge
  42.     *      of eccentricities of the data store.
  43.     *
  44.     *
  45.     ==============================================================================*/   
  46.     // Only statics, does not need to be marked with the serializable attribute
  47.     internal class CultureTableRecord
  48.     {
  49.         // For spanish sorting
  50.         internal const int SPANISH_TRADITIONAL_SORT = 1034;
  51.         private const int SPANISH_INTERNATIONAL_SORT = 3082;
  52.        
  53.         // Sizes defined by the RFC3066 spec
  54.         private const int MAXSIZE_LANGUAGE = 8;
  55.         private const int MAXSIZE_REGION = MAXSIZE_LANGUAGE;
  56.         private const int MAXSIZE_SUFFIX = 8 * MAXSIZE_LANGUAGE;
  57.         private const int MAXSIZE_FULLTAGNAME = MAXSIZE_LANGUAGE + MAXSIZE_REGION + MAXSIZE_SUFFIX + 4;
  58.         // The 2 is for the tags and the prefix
  59.         private static object s_InternalSyncObject;
  60.         private static object InternalSyncObject {
  61.             get {
  62.                 if (s_InternalSyncObject == null) {
  63.                     object o = new object();
  64.                     Interlocked.CompareExchange(ref s_InternalSyncObject, o, null);
  65.                 }
  66.                 return s_InternalSyncObject;
  67.             }
  68.         }
  69.        
  70.         //
  71.         // CultureTableRecordCache caches all CultureTableRecord created objects except the objects created by
  72.         // RegionInfo constructor which takes region name and instead will be cached in CultureTableRecordRegionCache
  73.         //
  74.        
  75.         private static Hashtable CultureTableRecordCache;
  76.         private static Hashtable CultureTableRecordRegionCache;
  77.        
  78.        
  79.        
  80.         // CultureTable this data refers to.
  81.         private CultureTable m_CultureTable;
  82.         unsafe private CultureTableData* m_pData;
  83.         unsafe private ushort* m_pPool;
  84.         private bool m_bUseUserOverride;
  85.         private int m_CultureID;
  86.         private string m_CultureName;
  87.        
  88.         private int m_ActualCultureID = 0;
  89.         private string m_ActualName = null;
  90.        
  91.         // <SyntheticSupport/>
  92.         // m_synthetic will be true only if we have synthetic culture or synthetic replacement culture.
  93.         private bool m_synthetic = false;
  94.        
  95.         private SafeNativeMemoryHandle nativeMemoryHandle;
  96.         // <SyntheticSupport/>
  97.         private string m_windowsPath = null;
  98.        
  99.         private const int LOCALE_SLANGUAGE = 2;
  100.         // localized name of language
  101.         private const int LOCALE_SCOUNTRY = 6;
  102.         // localized name of country
  103.         private const int LOCALE_SNATIVELANGNAME = 4;
  104.         // native name of language
  105.         private const int LOCALE_SNATIVECTRYNAME = 8;
  106.         // native name of country
  107.         private const int LOCALE_ICALENDARTYPE = 4105;
  108.         // iCalendarType type of calendar
  109.        
  110.         ////////////////////////////////////////////////////////////////////////
  111.         //
  112.         // Create a CultureTable from the given custom/replacement culture name.
  113.         //
  114.         // SECURITY SECURITY SECURITY
  115.         // Before call this function, call ValidateCulturePieceToLower() to verify
  116.         // that the name does not contain illegal characters (such as "." or backslash.
  117.         //
  118.         ////////////////////////////////////////////////////////////////////////
  119.        
  120.         [ResourceExposure(ResourceScope.None)]
  121.         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
  122.         unsafe private CultureTable GetCustomCultureTable(string name)
  123.         {
  124.             CultureTable cultureTable = null;
  125.            
  126.             string customCultureFile = GetCustomCultureFile(name);
  127.             if (customCultureFile == null) {
  128.                 return null;
  129.             }
  130.            
  131.             try {
  132.                 cultureTable = new CultureTable(customCultureFile, false);
  133.                 if (!cultureTable.IsValid) {
  134.                     // If we have invalid culture table then we have custom culture. in that case we'll try
  135.                     // to see if the culture name is one of the framework or synthetic cultures and then
  136.                     // try to create it otherwise we'll throw.
  137.                    
  138.                     string defaultTableActualName;
  139.                     int defaultTableCultureID;
  140.                     int defaultTableDataItem = CultureTable.Default.GetDataItemFromCultureName(name, out defaultTableCultureID, out defaultTableActualName);
  141.                    
  142.                     // not built in framework culture
  143.                     if (defaultTableDataItem < 0) {
  144.                        
  145.                     }
  146.                    
  147.                     return null;
  148.                     // returning null means fallback to framework or synthetic culture.
  149.                 }
  150.             }
  151.             catch (FileNotFoundException) {
  152.                 //
  153.                 // getting here means custom culture file get unregistered/renamed from different AppDomain/Process.
  154.                 // just update the cache to point to the empty string as subsequent calls will not bother trying again.
  155.                 //
  156.                 cultureTable = null;
  157.             }
  158.            
  159.             return cultureTable;
  160.         }
  161.        
  162.         ////////////////////////////////////////////////////////////////////////
  163.         //
  164.         // Using the specified replacementCultureName, check if there is a replacment
  165.         // culture file. If yes, return the CultureTable created from the custom culture file.
  166.         //
  167.         // Parameters
  168.         // replacementCultureName: The culture name to check. Note that alternative sort like de-DE_phoneb should pass de-DE here.
  169.         // [OUT] dataItem: The dataItem for the culture name in the custom culture file.
  170.         // Returns
  171.         // A valid CultureTable from the custom culture file. null, if the custom file can not be found or the dataItem can not be found.
  172.         //
  173.         ////////////////////////////////////////////////////////////////////////
  174.        
  175.         unsafe internal CultureTable TryCreateReplacementCulture(string replacementCultureName, out int dataItem)
  176.         {
  177.             string name = ValidateCulturePieceToLower(replacementCultureName, "cultureName", MAXSIZE_FULLTAGNAME);
  178.            
  179.             // Before call this function, call ValidateCulturePieceToLower() to verify
  180.             // that the name does not contain illegal characters (such as "." or backslash.
  181.             CultureTable cultureTable = GetCustomCultureTable(name);
  182.            
  183.             if (cultureTable == null) {
  184.                 dataItem = -1;
  185.                 return (null);
  186.             }
  187.             // We have a replacement culture. Use it.
  188.             int tempID;
  189.             string tempName;
  190.             dataItem = cultureTable.GetDataItemFromCultureName(name, out tempID, out tempName);
  191.             return (dataItem >= 0 ? cultureTable : null);
  192.         }
  193.        
  194.        
  195.         //
  196.         // GetCultureTableRecord create CultureTableRecord object for specific culture name.
  197.         // This method uses CultureTableRecordCache to make sure we don't have to create this
  198.         // object if it is already created before.
  199.         //
  200.        
  201.         static internal CultureTableRecord GetCultureTableRecord(string name, bool useUserOverride)
  202.         {
  203.             BCLDebug.Assert(name != null, "[CultureTableRecord::GetCultureTableRecord] name should be valid.");
  204.            
  205.             // Make sure the cache is valid.
  206.             if (CultureTableRecordCache == null) {
  207.                 // Invariant culture
  208.                 if (name.Length == 0) {
  209.                     // First time Invariant culture get created we ignore creating the cache
  210.                     return new CultureTableRecord(name, useUserOverride);
  211.                 }
  212.                
  213.                 lock (InternalSyncObject) {
  214.                     if (CultureTableRecordCache == null)
  215.                         CultureTableRecordCache = new Hashtable();
  216.                 }
  217.             }
  218.            
  219.             name = ValidateCulturePieceToLower(name, "name", MAXSIZE_FULLTAGNAME);
  220.            
  221.             CultureTableRecord[] cultureRecordArray = (CultureTableRecord[])CultureTableRecordCache[name];
  222.             if (cultureRecordArray != null) {
  223.                 int index = useUserOverride ? 0 : 1;
  224.                
  225.                 if (cultureRecordArray[index] == null) {
  226.                     int filled = index == 0 ? 1 : 0;
  227.                     cultureRecordArray[index] = (CultureTableRecord)cultureRecordArray[filled].CloneWithUserOverride(useUserOverride);
  228.                 }
  229.                
  230.                 return cultureRecordArray[index];
  231.             }
  232.            
  233.             CultureTableRecord cultureRecord = new CultureTableRecord(name, useUserOverride);
  234.             lock (InternalSyncObject) {
  235.                 if (CultureTableRecordCache[name] == null) {
  236.                     cultureRecordArray = new CultureTableRecord[2];
  237.                     cultureRecordArray[useUserOverride ? 0 : 1] = cultureRecord;
  238.                     CultureTableRecordCache[name] = cultureRecordArray;
  239.                 }
  240.             }
  241.            
  242.             return cultureRecord;
  243.         }
  244.        
  245.         //
  246.         // GetCultureTableRecord create CultureTableRecord object for specific culture Id.
  247.         // This method convert the culture Id to culture name and then uses GetCultureTableRecord
  248.         // to get the CultureTableRecord object.
  249.         //
  250.        
  251.         static internal CultureTableRecord GetCultureTableRecord(int cultureId, bool useUserOverride)
  252.         {
  253.             if (cultureId == CultureInfo.LOCALE_INVARIANT)
  254.                 return GetCultureTableRecord("", false);
  255.            
  256.             string name = null;
  257.             if (CultureTable.Default.GetDataItemFromCultureID(cultureId, out name) < 0) {
  258.             }
  259.            
  260.             // GetDataItemFromCultureID can set the name to empty string.
  261.             if (name != null && name.Length > 0) {
  262.                 return GetCultureTableRecord(name, useUserOverride);
  263.             }
  264.            
  265.             throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Argument_CultureNotSupported"), cultureId), "culture");
  266.         }
  267.        
  268.         //
  269.         // GetCultureTableRecordForRegion create CultureTableRecord object for specific region name.
  270.         // this method do the following
  271.         // o try to get the object from the cache. if found then return otherwise try to create it.
  272.         // o it try to get the record from the framework culture table. if found it then create
  273.         // the CultureTableRecord object and store it in the cache then return.
  274.         // o call GetCultureTableRecord to get the object. if found it then store it in the cache
  275.         // and return. notice that GetCultureTableRecord will try the custom culture then synthetic
  276.         // culture.
  277.         // o otherwise we'll throw ArgumentException.
  278.         //
  279.        
  280.         static internal CultureTableRecord GetCultureTableRecordForRegion(string regionName, bool useUserOverride)
  281.         {
  282.             BCLDebug.Assert(regionName != null, "[CultureTableRecord::GetCultureTableRecordForRegion] regionName should be valid.");
  283.            
  284.             // Make sure the cache is valid.
  285.             if (CultureTableRecordRegionCache == null) {
  286.                 lock (InternalSyncObject) {
  287.                     if (CultureTableRecordRegionCache == null)
  288.                         CultureTableRecordRegionCache = new Hashtable();
  289.                 }
  290.             }
  291.            
  292.             regionName = ValidateCulturePieceToLower(regionName, "regionName", MAXSIZE_FULLTAGNAME);
  293.            
  294.             CultureTableRecord[] cultureRecordArray = (CultureTableRecord[])CultureTableRecordRegionCache[regionName];
  295.             if (cultureRecordArray != null) {
  296.                 int index = useUserOverride ? 0 : 1;
  297.                 if (cultureRecordArray[index] == null) {
  298.                     cultureRecordArray[index] = cultureRecordArray[index == 0 ? 1 : 0].CloneWithUserOverride(useUserOverride);
  299.                 }
  300.                 return cultureRecordArray[index];
  301.             }
  302.            
  303.             int dataItem = CultureTable.Default.GetDataItemFromRegionName(regionName);
  304.            
  305.             CultureTableRecord cultureRecord = null;
  306.            
  307.             if (dataItem > 0) {
  308.                 cultureRecord = new CultureTableRecord(regionName, dataItem, useUserOverride);
  309.             }
  310.             else {
  311.                 try {
  312.                     cultureRecord = GetCultureTableRecord(regionName, useUserOverride);
  313.                 }
  314.                 catch (ArgumentException) {
  315.                     throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Argument_InvalidRegionName"), regionName), "name");
  316.                 }
  317.             }
  318.            
  319.             BCLDebug.Assert(cultureRecord != null, "[CultureTableRecord::GetCultureTableRecordForRegion] cultureRecord should be valid.");
  320.             lock (InternalSyncObject) {
  321.                 if (CultureTableRecordRegionCache[regionName] == null) {
  322.                     cultureRecordArray = new CultureTableRecord[2];
  323.                     cultureRecordArray[useUserOverride ? 0 : 1] = cultureRecord.CloneWithUserOverride(useUserOverride);
  324.                     CultureTableRecordRegionCache[regionName] = cultureRecordArray;
  325.                 }
  326.             }
  327.            
  328.             return cultureRecord;
  329.         }
  330.        
  331.         //
  332.         // This constructor used only to create a Framework culture. it doesn't create custom
  333.         // culture nor synthetic culture.
  334.         // This is used when requesting the native calendar name for a custom culture with
  335.         // empty string native calendar name.
  336.         //
  337.         unsafe internal CultureTableRecord(int cultureId, bool useUserOverride)
  338.         {
  339.             this.m_bUseUserOverride = useUserOverride;
  340.            
  341.             int defaultTableDataItem = CultureTable.Default.GetDataItemFromCultureID(cultureId, out m_ActualName);
  342.             if (defaultTableDataItem < 0) {
  343.                 throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Argument_CultureNotSupported"), cultureId), "culture");
  344.             }
  345.            
  346.             m_ActualCultureID = cultureId;
  347.             m_CultureTable = CultureTable.Default;
  348.            
  349.             m_pData = (CultureTableData*)(m_CultureTable.m_pItemData + m_CultureTable.m_itemSize * defaultTableDataItem);
  350.             m_pPool = m_CultureTable.m_pDataPool;
  351.            
  352.             m_CultureName = SNAME;
  353.             m_CultureID = (cultureId == SPANISH_TRADITIONAL_SORT) ? cultureId : ILANGUAGE;
  354.            
  355.             BCLDebug.Assert(!IsCustomCulture, "[CultureTableRecord::ctor] we shouldn't have custom culture.");
  356.             BCLDebug.Assert(!IsSynthetic, "[CultureTableRecord::ctor] we shouldn't have synthetic culture.");
  357.         }
  358.        
  359.         //
  360.         // m_bUseUserOverride indicates that if we need to check for user-override values for this CultureInfo instance.
  361.         // For the user default culture of the system, user can choose to override some of the values
  362.         // associated with that culture. For example, the default short-date format for en-US is
  363.         // "M/d/yyyy", however, one may change it to "dd/MM/yyyy" from the Regional Option in
  364.         // the control panel.
  365.         // So when a CultureInfo is created, one can specify if the create CultureInfo should check
  366.         // for user-override values, or should always get the default values.
  367.         //
  368.         //
  369.         // AVOID USING P/INVOKE IN THIS CODEPATH.
  370.         // AVOID USING P/INVOKE IN THIS CODEPATH.
  371.         // AVOID USING P/INVOKE IN THIS CODEPATH.
  372.         //
  373.         // P/Invoke will cause COM to be initialized and sets the thread apartment model. Since CultureInfo is used early in the CLR process,
  374.         // this will prevent Loader from having a chance to look at the executable and setting the apartment model to the one the application wants.
  375.        
  376.        
  377.        
  378.         //
  379.         // Search order for creating a culture.
  380.         //
  381.        
  382. /*
  383.           First, search by name
  384.                 if this is a known culture name from culture.nlp, and it is an alternative sort name (such as de-DE_phoneb)
  385.                 {
  386.                     Get the name from the LANGID by removing the sort ID (so the name becomes de-DE).
  387.                     This is the name used for search replacment culture.
  388.                 }
  389.                
  390.                 Check if this specified name has a custom/replacement culture file.
  391.                 if there is a custom/replacement culture file
  392.                 {
  393.                     // This is a custom culture, or a replacement culture.
  394.                     return; [CUSTOM/REPLACEMENT CULTURE (.NET CULTURE/SYNTHETIC CULTURE) FOUND BY NAME]
  395.                 }
  396.                 From culture.nlp, check if tihs is a vlid culture name
  397.                 if this is a valid culture name
  398.                 {
  399.                     // This is a .NET culture.
  400.                     return; [NON-REPLACEMENT .NET CULTURE FOUND BY NAME]
  401.                 }
  402.                 Check if this is a valid name from synthetic culture
  403.                 if this is a valid synthetic culture name
  404.                 {
  405.                     // This is a synthetic culture. Set the cultureID, so we will
  406.                     // create it when we search by LCID later.
  407.                     // [SYNTHETIC CULTURE FOUND BY NAME]
  408.                 } else
  409.                 {
  410.                     throw exception;    [INVALID NAME]
  411.                 }
  412.           Then Search by LCID
  413.                 we'll come here only if the lcid is filled with synthetic culture Id.
  414.                     // This is synthetic culture.
  415.                     Get the name for this LANGID of this synthetic LCID.
  416.                     if there is a replacement culture for this LCID by checking name.
  417.                     {
  418.                         Use it and return the replacement culture for synthetic culture.
  419.                         return;  [REPLACEMENT SYNTHETIC CULTURE]
  420.                     }
  421.                     Init and return the synthetic culture. 
  422.                     return;  [NON-REPLACEMENT SYNTHETIC CULTURE]
  423.                    
  424.                 }
  425.                 otherwise throw exception
  426.                
  427.         */       
  428.         //
  429.         // * IMPORTANT * cultureName should be in lower case.
  430.         //
  431.         unsafe private CultureTableRecord(string cultureName, bool useUserOverride)
  432.         {
  433.             BCLDebug.Assert(cultureName != null, "[CultureTableRecord::ctor] cultureName should be valid.");
  434.            
  435.             int cultureID = 0;
  436.            
  437.             // Special case for invariant name
  438.             if (cultureName.Length == 0) {
  439.                 useUserOverride = false;
  440.                 cultureID = CultureInfo.LOCALE_INVARIANT;
  441.             }
  442.            
  443.             this.m_bUseUserOverride = useUserOverride;
  444.            
  445.             // We prefer to look up by name (if available)
  446.             int iDataItem = -1;
  447.             if (cultureName.Length > 0) {
  448.                 // Check if this is an alternative sort name.
  449.                 string defaultTableActualName;
  450.                 int defaultTableCultureID;
  451.                 string name = cultureName;
  452.                 int defaultTableDataItem = CultureTable.Default.GetDataItemFromCultureName(name, out defaultTableCultureID, out defaultTableActualName);
  453.                 if (defaultTableDataItem >= 0 && (CultureInfo.GetSortID(defaultTableCultureID) > 0 || defaultTableCultureID == SPANISH_TRADITIONAL_SORT)) {
  454.                     string replacmentCultureName;
  455.                    
  456.                     int nonSortId;
  457.                     if (defaultTableCultureID == SPANISH_TRADITIONAL_SORT)
  458.                         nonSortId = SPANISH_INTERNATIONAL_SORT;
  459.                     else
  460.                         nonSortId = CultureInfo.GetLangID(defaultTableCultureID);
  461.                    
  462.                     // This is an alternative sort culture.
  463.                     if (CultureTable.Default.GetDataItemFromCultureID(nonSortId, out replacmentCultureName) >= 0) {
  464.                         // This is the replacement culture name for an alternative sort.
  465.                         name = ValidateCulturePieceToLower(replacmentCultureName, "cultureName", MAXSIZE_FULLTAGNAME);
  466.                     }
  467.                 }
  468.                
  469.                 // If the compatibility flag is defined and culture is replacemet culture then we don't
  470.                 // open the custom culture file. instead we'll try to get framework/OS culture.
  471.                 if (!Environment.GetCompatibilityFlag(CompatibilityFlag.DisableReplacementCustomCulture) || IsCustomCultureId(defaultTableCultureID)) {
  472.                    
  473.                     // we always try the replacement custom cultures first.
  474.                     // Before call this function, call ValidateCulturePieceToLower() to verify
  475.                     // that the name does not contain illegal characters (such as "." or backslash.
  476.                     m_CultureTable = GetCustomCultureTable(name);
  477.                 }
  478.                
  479.                 if (m_CultureTable != null) {
  480.                     //
  481.                     // [CUSTOM/REPLACEMENT CULTURE (.NET CULTURE/SYNTHETIC CULTURE) FOUND BY NAME]
  482.                     //
  483.                     iDataItem = this.m_CultureTable.GetDataItemFromCultureName(name, out this.m_ActualCultureID, out this.m_ActualName);
  484.                     if (defaultTableDataItem >= 0) {
  485.                         // This is a replacment culture (since defaultTableDataItem >= 0), use the default ID/Name from the table.
  486.                         // For de-DE_phoneb, this will set the the actualCultureID to be 0x10407, instead of the LCID for replacment cutlure 0x0407.
  487.                         this.m_ActualCultureID = defaultTableCultureID;
  488.                         this.m_ActualName = defaultTableActualName;
  489.                     }
  490.                 }
  491.                
  492.                 if (iDataItem < 0 && defaultTableDataItem >= 0) {
  493.                     //
  494.                     // [NON-REPLACEMENT .NET CULTURE FOUND BY NAME]
  495.                     //
  496.                     this.m_CultureTable = CultureTable.Default;
  497.                     this.m_ActualCultureID = defaultTableCultureID;
  498.                     this.m_ActualName = defaultTableActualName;
  499.                     iDataItem = defaultTableDataItem;
  500.                 }
  501.                
  502.             }
  503.            
  504.             // If we couldn't get it by name, try culture ID.
  505.             if (iDataItem < 0 && cultureID > 0) {
  506.                 if (cultureID == CultureInfo.LOCALE_INVARIANT) {
  507.                     // Special case for the Invariant culture.
  508.                     iDataItem = CultureTable.Default.GetDataItemFromCultureID(cultureID, out this.m_ActualName);
  509.                     if (iDataItem > 0) {
  510.                         m_ActualCultureID = cultureID;
  511.                         m_CultureTable = CultureTable.Default;
  512.                     }
  513.                 }
  514.             }
  515.            
  516.             // If we found one, use it and return
  517.             if (iDataItem >= 0) {
  518.                 // Found it, use it
  519.                 this.m_pData = (CultureTableData*)(this.m_CultureTable.m_pItemData + this.m_CultureTable.m_itemSize * iDataItem);
  520.                 this.m_pPool = this.m_CultureTable.m_pDataPool;
  521.                 // Use name & ID from the file ('cept spanish traditional, which has to stay the same)
  522.                 this.m_CultureName = this.SNAME;
  523.                 this.m_CultureID = (m_ActualCultureID == SPANISH_TRADITIONAL_SORT) ? m_ActualCultureID : this.ILANGUAGE;
  524.                
  525.                
  526.                 return;
  527.             }
  528.            
  529.             // Error, if we have a name throw that name
  530.             if (cultureName != null)
  531.                 throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Argument_InvalidCultureName"), cultureName), "name");
  532.            
  533.             // No name, throw the LCID
  534.             throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Argument_CultureNotSupported"), cultureID), "culture");
  535.         }
  536.        
  537.         //
  538.         // this constructor will create the CultureTableRecord object and point to the
  539.         // culture table at the slot dataItem.
  540.         //
  541.        
  542.         unsafe private CultureTableRecord(string regionName, int dataItem, bool useUserOverride)
  543.         {
  544.             BCLDebug.Assert(regionName != null && regionName.Length > 0, "[CultureTableRecord.CultureTableRecord(regionName,bool)]Expected non-null/empty name");
  545.            
  546.             BCLDebug.Assert(dataItem > 0, "[CultureTableRecord.CultureTableRecord(regionName, dataItem, bool)] dataItem > 0 should be true.");
  547.            
  548.             // Assuming it works we'll want these
  549.             this.m_bUseUserOverride = useUserOverride;
  550.             this.m_CultureName = regionName;
  551.             this.m_CultureTable = CultureTable.Default;
  552.            
  553.             // Found it, use it
  554.             this.m_pData = (CultureTableData*)(this.m_CultureTable.m_pItemData + this.m_CultureTable.m_itemSize * dataItem);
  555.             this.m_pPool = this.m_CultureTable.m_pDataPool;
  556.            
  557.             // Use ID from the file
  558.             this.m_CultureID = this.ILANGUAGE;
  559.         }
  560.        
  561.        
  562.        
  563.        
  564.         static internal void ResetCustomCulturesCache()
  565.         {
  566.         }
  567.        
  568.        
  569.        
  570.         private string WindowsPath {
  571.             get {
  572.                 if (m_windowsPath == null) {
  573.                     m_windowsPath = CultureInfo.nativeGetWindowsDirectory();
  574.                 }
  575.                 return (m_windowsPath);
  576.             }
  577.         }
  578.        
  579. /*---------------------------------------------------------
  580.         *
  581.         * Gets a filename of a custom culture covered by the given string.
  582.         *
  583.         * Builds a cache of items found (adding String.Empty for files that are not present)
  584.         * to avoid hitting the disk repeatedly.
  585.         *
  586.         *--------------------------------------------------------*/       
  587.         private string GetCustomCultureFile(string name)
  588.         {
  589.             return (null);
  590.         }
  591.        
  592. /*---------------------------------------------------------
  593.         *
  594.         * Validate a culture name -- throws if it is not valid. If it is valid, return
  595.         * the lowercase version of it, suitable for later caching.
  596.         *
  597.         *--------------------------------------------------------*/       
  598.         private static string ValidateCulturePieceToLower(string testString, string paramName, int maxLength)
  599.         {
  600.             if (testString.Length > maxLength) {
  601.                 throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Argument_NameTooLong"), testString, maxLength), paramName);
  602.             }
  603.            
  604.             StringBuilder sb = new StringBuilder(testString.Length);
  605.            
  606.             for (int ich = 0; ich < testString.Length; ich++) {
  607.                 char ch = testString[ich];
  608.                
  609.                 if (ch <= 'Z' && ch >= 'A') {
  610.                     sb.Append((char)(ch - 'A' + 'a'));
  611.                 }
  612.                 else if (((ch <= 'z' && ch >= 'a') || (ch <= '9' && ch >= '0') || (ch == '_') || (ch == '-'))) {
  613.                     sb.Append(ch);
  614.                 }
  615.                 else {
  616.                     throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Argument_NameContainsInvalidCharacters"), testString), paramName);
  617.                 }
  618.             }
  619.            
  620.             return (sb.ToString());
  621.            
  622.         }
  623.        
  624.         static internal string AnsiToLower(string testString)
  625.         {
  626.             StringBuilder sb = new StringBuilder(testString.Length);
  627.            
  628.             for (int ich = 0; ich < testString.Length; ich++) {
  629.                 char ch = testString[ich];
  630.                 sb.Append(ch <= 'Z' && ch >= 'A' ? (char)(ch - 'A' + 'a') : ch);
  631.             }
  632.            
  633.             return (sb.ToString());
  634.         }
  635.        
  636.         //========================GetCultureNameFromIetfName============================
  637.         //Action: Given an ietf name, get the corresponding culture name.
  638.         //Returns: The culture name or null if not valid.
  639.         //Arguments:
  640.         // name culture name
  641.         //Note: The passed name should be in lower case.
  642.         //Exceptions:
  643.         //==============================================================================
  644.         unsafe static internal string GetCultureNameFromIetfName(string name)
  645.         {
  646.             BCLDebug.Assert(name != null, "CultureTable.GetCultureNameFromIetfName(): name!=null");
  647.             string actualName = null;
  648.            
  649.             // First check our default table to see if it exists there
  650.             actualName = CultureTable.Default.LookupIetfName(name);
  651.            
  652.             // Next check the registry
  653.            
  654.             return actualName;
  655.         }
  656.        
  657.         static internal string LookupIetfNameFromSynthetic(string name)
  658.         {
  659.             string cultureName = null;
  660.             return cultureName;
  661.         }
  662.        
  663.         // <SyntheticSupport/>
  664.         internal bool IsSynthetic {
  665.             get { return m_synthetic; }
  666.         }
  667.        
  668.         internal bool IsCustomCulture {
  669. // If we came from the assembly we aren't custom
  670.             get { return !(this.m_CultureTable.fromAssembly); }
  671.         }
  672.        
  673.         internal bool IsReplacementCulture {
  674.             get { return (this.IsCustomCulture && !IsCustomCultureId(m_CultureID)); }
  675.         }
  676.        
  677.         internal int CultureID {
  678.             get {
  679.                 BCLDebug.Assert(this.m_CultureID > 0, "[CultureTableRecord.CultureID]unexpected m_CultureId");
  680.                 return this.m_CultureID;
  681.             }
  682.         }
  683.        
  684.         internal string CultureName {
  685.             get {
  686.                 BCLDebug.Assert(this.m_CultureName != null, "[CultureTableRecord.CultureName]unexpected m_CultureName");
  687.                 return this.m_CultureName;
  688.             }
  689.            
  690.             set {
  691.                 BCLDebug.Assert(value != null, "[CultureTableRecord.CultureName]Expected non-null value for culture name");
  692.                 this.m_CultureName = value;
  693.             }
  694.         }
  695.        
  696.         internal bool UseUserOverride {
  697.             get { return this.m_bUseUserOverride; }
  698.         }
  699.        
  700.         // A property to indicate if we should retrieve information by calling the Win32 GetLocaleInfo().
  701.        
  702.         unsafe internal bool UseGetLocaleInfo {
  703.             get {
  704.                 if (!this.m_bUseUserOverride) {
  705.                     return (false);
  706.                 }
  707.                 int lcid;
  708.                 CultureInfo.nativeGetUserDefaultLCID(&lcid, CultureInfo.LOCALE_USER_DEFAULT);
  709.                
  710.                 if (ActualCultureID == CultureInfo.LOCALE_CUSTOM_UNSPECIFIED && lcid == CultureInfo.LOCALE_CUSTOM_DEFAULT) {
  711.                     if (SNAME.Equals(CultureInfo.nativeGetCultureName(lcid, true, false))) {
  712.                         return true;
  713.                     }
  714.                     return false;
  715.                 }
  716.                
  717.                 return (this.ActualCultureID == lcid);
  718.             }
  719.         }
  720.        
  721.         // A method to check if we can use the Win32 GetLocaleInfo() for the specified locale and the specified calenar in this CultureTableRecord.
  722.         // It will be true when all of the following are true:
  723.         // * UseUserOverride is true
  724.         // * UseGetLocaleInfo is true (which means the specified locale is the current user default locale.
  725.         // * The specified calendar is the current calendar in the user default locale.
  726.         // Parameters:
  727.         // calID: The calendar ID to be checked.
  728.         unsafe internal bool UseCurrentCalendar(int calID)
  729.         {
  730.             return (UseGetLocaleInfo && CultureInfo.nativeGetCurrentCalendar() == calID);
  731.         }
  732.        
  733.        
  734.         internal bool IsValidSortID(int sortID)
  735.         {
  736.             BCLDebug.Assert(sortID >= 0 && sortID <= 65535, "sortID is invalid");
  737.             // SortID is 16-bit positive integer.
  738.             if (sortID == 0 || (this.SALTSORTID != null && this.SALTSORTID.Length >= sortID && this.SALTSORTID[sortID - 1].Length != 0)) {
  739.                 return true;
  740.             }
  741.             else {
  742.                 return false;
  743.             }
  744.         }
  745.        
  746.         internal CultureTableRecord CloneWithUserOverride(bool userOverride)
  747.         {
  748.             if (m_bUseUserOverride == userOverride)
  749.                 return this;
  750.            
  751.             CultureTableRecord cultureTableRecord = (CultureTableRecord)this.MemberwiseClone();
  752.             cultureTableRecord.m_bUseUserOverride = userOverride;
  753.            
  754.             return cultureTableRecord;
  755.         }
  756.        
  757.         //
  758.         // CultureNativeDisplayName called when we need to get the native display name for the culture
  759.         // from Win32 side. we need to do that in cases like synthetic cultures.
  760.         //
  761.        
  762.         unsafe internal string CultureNativeDisplayName {
  763.             get {
  764.                 int lcid;
  765.                 CultureInfo.nativeGetUserDefaultUILanguage(&lcid);
  766.                
  767.                 if (CultureInfo.GetLangID(lcid) == CultureInfo.GetLangID(CultureInfo.CurrentUICulture.LCID)) {
  768.                     string localizedLanguageName = CultureInfo.nativeGetLocaleInfo(m_ActualCultureID, LOCALE_SLANGUAGE);
  769.                     if (localizedLanguageName != null) {
  770.                         // check for null terminated character.
  771.                         if (localizedLanguageName[localizedLanguageName.Length - 1] == '\0')
  772.                             return localizedLanguageName.Substring(0, localizedLanguageName.Length - 1);
  773.                         else
  774.                             return localizedLanguageName;
  775.                     }
  776.                 }
  777.                
  778.                 return this.SNATIVEDISPLAYNAME;
  779.             }
  780.         }
  781.        
  782.         //
  783.         // RegionNativeDisplayName called when we need to get the native display name for the region
  784.         // from Win32 side. we need to do that in cases like synthetic cultures.
  785.         //
  786.        
  787.         unsafe internal string RegionNativeDisplayName {
  788.             get {
  789.                 int lcid;
  790.                 CultureInfo.nativeGetUserDefaultUILanguage(&lcid);
  791.                
  792.                 if (CultureInfo.GetLangID(lcid) == CultureInfo.GetLangID(CultureInfo.CurrentUICulture.LCID)) {
  793.                     string localizedCountryName = CultureInfo.nativeGetLocaleInfo(m_ActualCultureID, LOCALE_SCOUNTRY);
  794.                     if (localizedCountryName != null) {
  795.                         if (localizedCountryName[localizedCountryName.Length - 1] == '\0')
  796.                             return localizedCountryName.Substring(0, localizedCountryName.Length - 1);
  797.                         else
  798.                             return localizedCountryName;
  799.                     }
  800.                 }
  801.                
  802.                 return this.SNATIVECOUNTRY;
  803.             }
  804.         }
  805.        
  806.         ////////////////////////////////////////////////////////////////////////
  807.         //
  808.         // Equals
  809.         //
  810.         // Implements Object.Equals(). Returns a boolean indicating whether
  811.         // or not object refers to the same CultureTableRecord as the current instance.
  812.         //
  813.         ////////////////////////////////////////////////////////////////////////
  814.         unsafe public override bool Equals(object value)
  815.         {
  816.             CultureTableRecord that = value as CultureTableRecord;
  817.             return (that != null) && (this.m_pData == that.m_pData && this.m_bUseUserOverride == that.m_bUseUserOverride && this.m_CultureID == that.m_CultureID && CultureInfo.InvariantCulture.CompareInfo.Compare(this.m_CultureName, that.m_CultureName, CompareOptions.IgnoreCase) == 0 && this.m_CultureTable.Equals(that.m_CultureTable));
  818.         }
  819.        
  820.         ////////////////////////////////////////////////////////////////////////
  821.         //
  822.         // GetHashCode
  823.         //
  824.         // Implements Object.GetHashCode(). Returns the hash code for the
  825.         // CultureInfo. The hash code is guaranteed to be the same for RegionInfo
  826.         // A and B where A.Equals(B) is true.
  827.         //
  828.         ////////////////////////////////////////////////////////////////////////
  829.         public override int GetHashCode()
  830.         {
  831.             //This doesn't tell apart user override from non-user override
  832.             if (!IsCustomCultureId(m_CultureID))
  833.                 return (this.m_CultureID);
  834.            
  835.             return (this.m_CultureName.GetHashCode());
  836.         }
  837.        
  838.         // Get a String
  839.         unsafe private string GetString(uint iOffset)
  840.         {
  841.             char* pCharValues = unchecked((char*)(this.m_pPool + iOffset));
  842.             if (pCharValues[1] == 0) {
  843.                 BCLDebug.Assert(iOffset == 0, "[CultureTableRecord.GetString]Expected empty strings to have 0 offset");
  844.                 return String.Empty;
  845.             }
  846.             return new string(pCharValues + 1, 0, (int)pCharValues[0]);
  847.         }
  848.        
  849.         private int InteropLCID {
  850.             get { return ActualCultureID == CultureInfo.LOCALE_CUSTOM_UNSPECIFIED ? CultureInfo.LOCALE_CUSTOM_DEFAULT : ActualCultureID; }
  851.         }
  852.        
  853.         private string GetOverrideString(uint iOffset, int iWindowsFlag)
  854.         {
  855.             return GetString(iOffset);
  856.         }
  857.        
  858.         unsafe private string[] GetStringArray(uint iOffset)
  859.         {
  860.             if (iOffset == 0)
  861.                 return new string[0];
  862.            
  863.             // The offset value is in char, and is related to the begining of string pool.
  864.             ushort* pCount = m_pPool + iOffset;
  865.             int count = (int)pCount[0];
  866.             // The number of strings in the array
  867.             BCLDebug.Assert(count != 0, "[CultureTableRecord.GetStringArray]Expected non-zero length array");
  868.             string[] values = new string[count];
  869.            
  870.             // Get past count and cast to uint
  871.             uint* pStringArray = (uint*)(pCount + 1);
  872.            
  873.             // Get our strings
  874.             for (int i = 0; i < count; i++)
  875.                 values[i] = GetString(pStringArray[i]);
  876.            
  877.             return (values);
  878.         }
  879.        
  880.         // Get first string in this array of strings
  881.         unsafe private string GetStringArrayDefault(uint iOffset)
  882.         {
  883.             if (iOffset == 0)
  884.                 return String.Empty;
  885.            
  886.             // The offset value is in char, and is related to the begining of string pool.
  887.             ushort* pCount = m_pPool + iOffset;
  888.             BCLDebug.Assert(pCount[0] != 0, "[CultureTableRecord.GetStringArrayDefault]Expected non-zero length array");
  889.            
  890.             // Get past count and cast to uint
  891.             uint* pStringArray = (uint*)(pCount + 1);
  892.            
  893.             // We had strings, return the first one
  894.             return GetString(pStringArray[0]);
  895.         }
  896.        
  897.         // Get the user override or the first array of this string array
  898.         private string GetOverrideStringArrayDefault(uint iOffset, int iWindowsFlag)
  899.         {
  900.             // If override wasn't available, return the table version
  901.             return GetStringArrayDefault(iOffset);
  902.         }
  903.        
  904.         private ushort GetOverrideUSHORT(ushort iData, int iWindowsFlag)
  905.         {
  906.             return iData;
  907.         }
  908.        
  909.         unsafe private int[] GetWordArray(uint iData)
  910.         {
  911.             if (iData == 0)
  912.                 return new int[0];
  913.            
  914.             ushort* pWord = this.m_pPool + iData;
  915.             int count = (int)pWord[0];
  916.             // The number of words in the array
  917.             BCLDebug.Assert(count != 0, "[CultureTableRecord.GetWordArray]Expected non-zero length array");
  918.            
  919.             int[] values = new int[count];
  920.             pWord++;
  921.             // Get past count
  922.             for (int i = 0; i < count; i++) {
  923.                 values[i] = pWord[i];
  924.             }
  925.             return (values);
  926.         }
  927.        
  928.         private int[] GetOverrideGrouping(uint iData, int iWindowsFlag)
  929.         {
  930.             // No Override, use it from the tables.
  931.             return GetWordArray(iData);
  932.         }
  933.        
  934.         // The actual LCID, used when a name lookup leads to a custom sort (thus
  935.         // 'de-DE-deudi' will be 0x10407 rather than the plain old 0x0407 of 'de-DE').
  936.         internal int ActualCultureID {
  937.             get {
  938.                 if (0 == this.m_ActualCultureID) {
  939.                     this.m_ActualCultureID = this.ILANGUAGE;
  940.                 }
  941.                
  942.                 return (this.m_ActualCultureID);
  943.             }
  944.         }
  945.        
  946.         // The actual name, used when an LCID lookup leads to a custom sort (thus will be
  947.         // 0x10407 will be 'de-DE-deudi' rather than the plain old 'de-DE' of 0x0407).
  948.         internal string ActualName {
  949.             get {
  950.                 if (null == this.m_ActualName) {
  951.                     this.m_ActualName = this.SNAME;
  952.                 }
  953.                
  954.                 return (this.m_ActualName);
  955.             }
  956.         }
  957.        
  958.         internal bool IsNeutralCulture {
  959.             get { return ((IFLAGS & (ushort)CultureFlags.IsSpecificCulture) == 0); }
  960.         }
  961.        
  962.         ////////////////////////////////////////////////////////////////////////
  963.         //
  964.         // All the accessors
  965.         //
  966.         // Accessors for our data object items
  967.         //
  968.         ////////////////////////////////////////////////////////////////////////
  969.        
  970.         // These ones allow user override
  971.         // Integers
  972.         unsafe internal ushort IDIGITS {
  973.             get { return (this.m_pData->iDigits); }
  974.         }
  975.         // (user can override) number of fractional digits
  976.         unsafe internal ushort INEGNUMBER {
  977.             get { return (this.m_pData->iNegativeNumber); }
  978.         }
  979.         // (user can override) negative number format
  980.         unsafe internal ushort ICURRDIGITS {
  981.             get { return (this.m_pData->iCurrencyDigits); }
  982.         }
  983.         // (user can override) # local monetary fractional digits
  984.         unsafe internal ushort ICURRENCY {
  985.             get { return (this.m_pData->iCurrency); }
  986.         }
  987.         // (user can override) positive currency format
  988.         unsafe internal ushort INEGCURR {
  989.             get { return (this.m_pData->iNegativeCurrency); }
  990.         }
  991.         // (user can override) negative currency format
  992.         // internal unsafe ushort ILEADINGZEROS { get { return GetOverrideUSHORT(this.m_pData->iLEADINGZEROS, CultureTableData.LOCALE_ILEADINGZEROS); } } // (user can override) leading zeros 0 leading zeros, 1 ading zeros
  993.         unsafe internal ushort ICALENDARTYPE {
  994.             get { return ((ushort)IOPTIONALCALENDARS[0]); }
  995.         }
  996.         unsafe internal ushort IFIRSTWEEKOFYEAR {
  997.             get { return GetOverrideUSHORT(this.m_pData->iFirstWeekOfYear, CultureTableData.LOCALE_IFIRSTWEEKOFYEAR); }
  998.         }
  999.         // (user can override) first week of year
  1000.         // internal unsafe ushort ICOUNTRY { get { return GetOverrideUSHORT(this.m_pData->iCOUNTRY, CultureTableData.LOCALE_ICOUNTRY); } } // (user can override) country code (RegionInfo)
  1001.         unsafe internal ushort IMEASURE {
  1002.             get { return GetOverrideUSHORT(this.m_pData->iMeasure, CultureTableData.LOCALE_IMEASURE); }
  1003.         }
  1004.         // (user can override) system of measurement 0ric, 1(RegionInfo)
  1005.         unsafe internal ushort IDIGITSUBSTITUTION {
  1006.             get { return GetOverrideUSHORT(this.m_pData->iDigitSubstitution, CultureTableData.LOCALE_IDIGITSUBSTITUTION); }
  1007.         }
  1008.         // (user can override) Digit substitution 0text, 1e/arabic, 2ive/national (2 seems to be unused)
  1009.         // Grouping
  1010.         unsafe internal int[] SGROUPING {
  1011.             get { return GetOverrideGrouping(this.m_pData->waGrouping, CultureTableData.LOCALE_SGROUPING); }
  1012.         }
  1013.         // (user can override) grouping of digits
  1014.         unsafe internal int[] SMONGROUPING {
  1015.             get { return GetOverrideGrouping(this.m_pData->waMonetaryGrouping, CultureTableData.LOCALE_SMONGROUPING); }
  1016.         }
  1017.         // (user can override) monetary grouping of digits
  1018.         // Strings
  1019.         unsafe internal string SLIST {
  1020.             get { return GetOverrideString(this.m_pData->sListSeparator, CultureTableData.LOCALE_SLIST); }
  1021.         }
  1022.         // (user can override) list Separator
  1023.         unsafe internal string SDECIMAL {
  1024.             get { return GetString(this.m_pData->sDecimalSeparator); }
  1025.         }
  1026.         // (user can override) decimal Separator
  1027.         unsafe internal string STHOUSAND {
  1028.             get { return GetString(this.m_pData->sThousandSeparator); }
  1029.         }
  1030.         // (user can override) thousands Separator
  1031.         unsafe internal string SCURRENCY {
  1032.             get { return GetString(this.m_pData->sCurrency); }
  1033.         }
  1034.         // (user can override) local monetary symbol
  1035.         unsafe internal string SMONDECIMALSEP {
  1036.             get { return GetString(this.m_pData->sMonetaryDecimal); }
  1037.         }
  1038.         // (user can override) monetary decimal Separator
  1039.         unsafe internal string SMONTHOUSANDSEP {
  1040.             get { return GetString(this.m_pData->sMonetaryThousand); }
  1041.         }
  1042.         // (user can override) monetary thousands separator
  1043.         unsafe internal string SNEGATIVESIGN {
  1044.             get { return GetString(this.m_pData->sNegativeSign); }
  1045.         }
  1046.         // (user can override) negative sign
  1047.         unsafe internal string S1159 {
  1048.             get { return GetString(this.m_pData->sAM1159); }
  1049.         }
  1050.         // (user can override) AM designator
  1051.         unsafe internal string S2359 {
  1052.             get { return GetString(this.m_pData->sPM2359); }
  1053.         }
  1054.         // (user can override) PM designator
  1055.         // String array DEFAULTS
  1056.         // Note: GetDTFIOverrideValues does the user overrides for these, so we don't have to.
  1057.         unsafe internal string STIMEFORMAT {
  1058.             get { return ReescapeWin32String(GetStringArrayDefault(this.m_pData->saTimeFormat)); }
  1059.         }
  1060.         // (user can override) time format
  1061.         unsafe internal string SSHORTTIME {
  1062.             get { return ReescapeWin32String(GetStringArrayDefault(this.m_pData->saShortTime)); }
  1063.         }
  1064.         // short time format
  1065.         unsafe internal string SSHORTDATE {
  1066.             get { return ReescapeWin32String(GetStringArrayDefault(this.m_pData->saShortDate)); }
  1067.         }
  1068.         // (user can override) short date format
  1069.         unsafe internal string SLONGDATE {
  1070.             get { return ReescapeWin32String(GetStringArrayDefault(this.m_pData->saLongDate)); }
  1071.         }
  1072.         // (user can override) long date format
  1073.         unsafe internal string SYEARMONTH {
  1074.             get { return ReescapeWin32String(GetStringArrayDefault(this.m_pData->saYearMonth)); }
  1075.         }
  1076.         // (user can override) year/month format
  1077.         unsafe internal string SMONTHDAY {
  1078.             get { return ReescapeWin32String(GetString(this.m_pData->sMonthDay)); }
  1079.         }
  1080.         // month/day format (single string, no override)
  1081.         // String arrays
  1082.         unsafe internal string[] STIMEFORMATS {
  1083.             get { return ReescapeWin32Strings(GetStringArray(this.m_pData->saTimeFormat)); }
  1084.         }
  1085.         // (user can override) time format
  1086.         unsafe internal string[] SSHORTTIMES {
  1087.             get { return ReescapeWin32Strings(GetStringArray(this.m_pData->saShortTime)); }
  1088.         }
  1089.         // short time format
  1090.         unsafe internal string[] SSHORTDATES {
  1091.             get { return ReescapeWin32Strings(GetStringArray(this.m_pData->saShortDate)); }
  1092.         }
  1093.         // (user can override default only) short date format
  1094.         unsafe internal string[] SLONGDATES {
  1095.             get { return ReescapeWin32Strings(GetStringArray(this.m_pData->saLongDate)); }
  1096.         }
  1097.         // (user can override default only) long date format
  1098.         unsafe internal string[] SYEARMONTHS {
  1099.             get { return ReescapeWin32Strings(GetStringArray(this.m_pData->saYearMonth)); }
  1100.         }
  1101.         // (user can override) date year/month format. (9x doesn't support override)
  1102.         unsafe internal string[] SNATIVEDIGITS {
  1103.             // (user can override) native characters for digits 0-9
  1104.             get { return GetStringArray(this.m_pData->saNativeDigits); }
  1105.         }
  1106.        
  1107.         // Integer ones are all pretty trivial
  1108.         unsafe internal ushort ILANGUAGE {
  1109.             get { return this.m_pData->iLanguage; }
  1110.         }
  1111.         //
  1112.         // internal unsafe ushort IDEFAULTLANGUAGE { get { return this.m_pData->iDEFAULTLANGUAGE; } } // Default language if this is a rare lcid (Windows Only)
  1113.         unsafe internal ushort IDEFAULTANSICODEPAGE {
  1114.             get { return this.m_pData->iDefaultAnsiCodePage; }
  1115.         }
  1116.         // default ansi code page ID (ACP)
  1117.         unsafe internal ushort IDEFAULTOEMCODEPAGE {
  1118.             get { return this.m_pData->iDefaultOemCodePage; }
  1119.         }
  1120.         // default oem code page ID (OCP or OEM)
  1121.         unsafe internal ushort IDEFAULTMACCODEPAGE {
  1122.             get { return this.m_pData->iDefaultMacCodePage; }
  1123.         }
  1124.         // default macintosh code page
  1125.         unsafe internal ushort IDEFAULTEBCDICCODEPAGE {
  1126.             get { return this.m_pData->iDefaultEbcdicCodePage; }
  1127.         }
  1128.         // default EBCDIC code page
  1129.         unsafe internal ushort IGEOID {
  1130.             get { return this.m_pData->iGeoId; }
  1131.         }
  1132.         // GeoId (RegionInfo)
  1133.         // internal unsafe ushort IPAPERSIZE { get { return this.m_pData->iPAPERSIZE; } } // default paper size (RegionInfo)
  1134.         // internal unsafe ushort IINTLCURRENCYDIGITS { get { return this.m_pData->iINTLCURRENCYDIGITS; } } // # of digits after decimal in intl currency format (Windows Only)
  1135.         unsafe internal ushort INEGATIVEPERCENT {
  1136.             get { return this.m_pData->iNegativePercent; }
  1137.         }
  1138.         //
  1139.         unsafe internal ushort IPOSITIVEPERCENT {
  1140.             get { return this.m_pData->iPositivePercent; }
  1141.         }
  1142.         //
  1143.         unsafe internal ushort IPARENT {
  1144.             get { return this.m_pData->iParent; }
  1145.         }
  1146.         //
  1147.         unsafe internal ushort ILINEORIENTATIONS {
  1148.             get { return this.m_pData->iLineOrientations; }
  1149.         }
  1150.         //
  1151.         unsafe internal uint ICOMPAREINFO {
  1152.             get { return this.m_pData->iCompareInfo; }
  1153.         }
  1154.         //
  1155.         unsafe internal uint IFLAGS {
  1156.             get { return this.m_pData->iFlags; }
  1157.         }
  1158.         // Flags for culture
  1159.         // OptionalCalendars
  1160.         unsafe internal int[] IOPTIONALCALENDARS {
  1161.             get { return GetWordArray(this.m_pData->waCalendars); }
  1162.         }
  1163.         // additional calendar type(s), semicolon seperated, ie: '1;6'
  1164.         // Strings
  1165.         unsafe internal string SNAME {
  1166.             get { return GetString(this.m_pData->sName); }
  1167.         }
  1168.         //
  1169.         unsafe internal string SABBREVLANGNAME {
  1170.             get { return GetString(this.m_pData->sAbbrevLang); }
  1171.         }
  1172.         // abbreviated language name
  1173.         unsafe internal string SISO639LANGNAME {
  1174.             get { return GetString(this.m_pData->sISO639Language); }
  1175.         }
  1176.         //
  1177.         // internal unsafe String SENGLISHLANGUAGE { get { return GetString(this.m_pData->sENGLISHLANGUAGE); } } // English name for this language (Windows Only)
  1178.         // internal unsafe String SNATIVELANGUAGE { get { return GetString(this.m_pData->sNATIVELANGUAGE); } } // Native name of this language (Windows Only)
  1179.         unsafe internal string SENGCOUNTRY {
  1180.             get { return GetString(this.m_pData->sEnglishCountry); }
  1181.         }
  1182.         // english country name (RegionInfo)
  1183.         unsafe internal string SNATIVECOUNTRY {
  1184.             get { return GetString(this.m_pData->sNativeCountry); }
  1185.         }
  1186.         // native country name (RegionInfo)
  1187.         unsafe internal string SABBREVCTRYNAME {
  1188.             get { return GetString(this.m_pData->sAbbrevCountry); }
  1189.         }
  1190.         // abbreviated country name (RegionInfo)
  1191.         unsafe internal string SISO3166CTRYNAME {
  1192.             get { return GetString(this.m_pData->sISO3166CountryName); }
  1193.         }
  1194.         // (RegionInfo)
  1195.         unsafe internal string SINTLSYMBOL {
  1196.             get { return GetString(this.m_pData->sIntlMonetarySymbol); }
  1197.         }
  1198.         // international monetary symbol (RegionInfo)
  1199.         unsafe internal string SENGLISHCURRENCY {
  1200.             get { return GetString(this.m_pData->sEnglishCurrency); }
  1201.         }
  1202.         // English name for this currency (RegionInfo)
  1203.         unsafe internal string SNATIVECURRENCY {
  1204.             get { return GetString(this.m_pData->sNativeCurrency); }
  1205.         }
  1206.         // Native name for this currency (RegionInfo)
  1207.         unsafe internal string SENGDISPLAYNAME {
  1208.             get { return GetString(this.m_pData->sEnglishDisplayName); }
  1209.         }
  1210.         //
  1211.         unsafe internal string SISO639LANGNAME2 {
  1212.             get { return GetString(this.m_pData->sISO639Language2); }
  1213.         }
  1214.         //
  1215.         unsafe internal string SNATIVEDISPLAYNAME {
  1216.             get {
  1217.                 // Special case for Taiwan.
  1218.                 if (CultureInfo.GetLangID(ActualCultureID) == 1028 && CultureInfo.GetLangID(CultureInfo.InstalledUICulture.LCID) == 1028 && !IsCustomCulture) {
  1219.                     return (CultureInfo.nativeGetLocaleInfo(1028, LOCALE_SNATIVELANGNAME) + " (" + CultureInfo.nativeGetLocaleInfo(1028, LOCALE_SNATIVECTRYNAME) + ")");
  1220.                 }
  1221.                 return GetString(this.m_pData->sNativeDisplayName);
  1222.             }
  1223.         }
  1224.         //
  1225.         unsafe internal string SPERCENT {
  1226.             get { return GetString(this.m_pData->sPercent); }
  1227.         }
  1228.         //
  1229.         unsafe internal string SNAN {
  1230.             get { return GetString(this.m_pData->sNaN); }
  1231.         }
  1232.         //
  1233.         unsafe internal string SPOSINFINITY {
  1234.             get { return GetString(this.m_pData->sPositiveInfinity); }
  1235.         }
  1236.         //
  1237.         unsafe internal string SNEGINFINITY {
  1238.             get { return GetString(this.m_pData->sNegativeInfinity); }
  1239.         }
  1240.         //
  1241.         unsafe internal string SADERA {
  1242.             get { return GetString(this.m_pData->sAdEra); }
  1243.         }
  1244.         // localized names for the A.D. Era
  1245.         unsafe internal string SABBREVADERA {
  1246.             get { return GetString(this.m_pData->sAbbrevAdEra); }
  1247.         }
  1248.         // abbreviated localized names for the A.D. Era
  1249.         unsafe internal string SISO3166CTRYNAME2 {
  1250.             get { return GetString(this.m_pData->sISO3166CountryName2); }
  1251.         }
  1252.         // (RegionInfo)
  1253.         unsafe internal string SREGIONNAME {
  1254.             get { return GetString(this.m_pData->sRegionName); }
  1255.         }
  1256.         // (RegionInfo)
  1257.         unsafe internal string SPARENT {
  1258.             get { return GetString(this.m_pData->sParent); }
  1259.         }
  1260.         //
  1261.         unsafe internal string SCONSOLEFALLBACKNAME {
  1262.             get { return GetString(this.m_pData->sConsoleFallbackName); }
  1263.         }
  1264.         unsafe internal string SSPECIFICCULTURE {
  1265.             get { return GetString(this.m_pData->sSpecificCulture); }
  1266.         }
  1267.         unsafe internal string SIETFTAG {
  1268.             get { return GetString(this.m_pData->sIetfLanguage); }
  1269.         }
  1270.         // RFC 3066 name
  1271.         // String Arrays
  1272.         unsafe internal string[] SDAYNAMES {
  1273.             get { return GetStringArray(this.m_pData->saDayNames); }
  1274.         }
  1275.         // day names
  1276.         unsafe internal string[] SABBREVDAYNAMES {
  1277.             get { return GetStringArray(this.m_pData->saAbbrevDayNames); }
  1278.         }
  1279.         // abbreviated day names
  1280.         unsafe internal string[] SSUPERSHORTDAYNAMES {
  1281.             get { return GetStringArray(this.m_pData->saSuperShortDayNames); }
  1282.         }
  1283.         // one letter day names
  1284.         unsafe internal string[] SMONTHNAMES {
  1285.             get { return GetStringArray(this.m_pData->saMonthNames); }
  1286.         }
  1287.         // month names
  1288.         unsafe internal string[] SABBREVMONTHNAMES {
  1289.             get { return GetStringArray(this.m_pData->saAbbrevMonthNames); }
  1290.         }
  1291.         // abbreviated month names
  1292.         unsafe internal string[] SMONTHGENITIVENAMES {
  1293.             get { return GetStringArray(this.m_pData->saMonthGenitiveNames); }
  1294.         }
  1295.         //
  1296.         unsafe internal string[] SABBREVMONTHGENITIVENAMES {
  1297.             get { return GetStringArray(this.m_pData->saAbbrevMonthGenitiveNames); }
  1298.         }
  1299.         //
  1300.         unsafe internal string[] SNATIVECALNAMES {
  1301.             get { return GetStringArray(this.m_pData->saNativeCalendarNames); }
  1302.         }
  1303.         // Native calendar names. index of optional calendar - 1, empty if no optional calendar at that number
  1304.         unsafe internal string[] SDATEWORDS {
  1305.             get { return GetStringArray(this.m_pData->saDateWords); }
  1306.         }
  1307.         //
  1308.         unsafe internal string[] SALTSORTID {
  1309.             get { return GetStringArray(this.m_pData->saAltSortID); }
  1310.         }
  1311.         // The array of alternate sort names
  1312.         // Fontsignature
  1313.         // internal unsafe ushort FONTSIGNATURE { get { return this.m_pData->waFONTSIGNATURE; } } // Font signature (16 WORDS) (Windows Only)
  1314.        
  1315.         // DateTimeFormatFlags
  1316.         unsafe internal DateTimeFormatFlags IFORMATFLAGS {
  1317.             get { return (DateTimeFormatFlags)this.m_pData->iFormatFlags; }
  1318.         }
  1319.         //
  1320.         // Special handling required for these fields
  1321.         // (user can override) positive sign. We use "+" if empty (windows data is usually empty)
  1322.         unsafe internal string SPOSITIVESIGN {
  1323.             get {
  1324.                 string strTemp = GetString(this.m_pData->sPositiveSign);
  1325.                 if (strTemp == null || strTemp.Length == 0)
  1326.                     strTemp = "+";
  1327.                 return strTemp;
  1328.             }
  1329.         }
  1330.        
  1331.         static internal bool IsCustomCultureId(int cultureId)
  1332.         {
  1333.             if (cultureId == CultureInfo.LOCALE_CUSTOM_DEFAULT || cultureId == CultureInfo.LOCALE_CUSTOM_UNSPECIFIED)
  1334.                 return true;
  1335.            
  1336.             return false;
  1337.         }
  1338.        
  1339.         unsafe private ushort ConvertFirstDayOfWeekMonToSun(int iTemp)
  1340.         {
  1341.             // Convert Mon-Sun to Sun-Sat format
  1342.             if (iTemp < 0 || iTemp > 6) {
  1343.                 // If invalid data exist in registry, assume
  1344.                 // the first day of week is Monday.
  1345.                 iTemp = 1;
  1346.             }
  1347.             else {
  1348.                 if (iTemp == 6) {
  1349.                     iTemp = 0;
  1350.                 }
  1351.                 else {
  1352.                     iTemp++;
  1353.                 }
  1354.             }
  1355.             return unchecked((ushort)iTemp);
  1356.         }
  1357.        
  1358.         // (user can override) first day of week (0 is Sunday)
  1359.         unsafe internal ushort IFIRSTDAYOFWEEK {
  1360.             get { return this.m_pData->iFirstDayOfWeek; }
  1361.         }
  1362.        
  1363.         unsafe internal ushort IINPUTLANGUAGEHANDLE {
  1364. // Remember this returns SPANISH_INTERNATIONAL_SORT even
  1365. // in the deprecated case.
  1366.             get { return (this.m_pData->iInputLanguageHandle); }
  1367.         }
  1368.        
  1369.         unsafe internal ushort ITEXTINFO {
  1370.             get {
  1371.                 ushort textInfo = this.m_pData->iTextInfo;
  1372.                
  1373.                 if (this.CultureID == (ushort)SPANISH_TRADITIONAL_SORT)
  1374.                     textInfo = (ushort)SPANISH_TRADITIONAL_SORT;
  1375.                
  1376.                 // Make sure custom culture and unknown get something
  1377.                 if (textInfo == CultureInfo.LOCALE_CUSTOM_DEFAULT || textInfo == 0)
  1378.                     textInfo = CultureInfo.LOCALE_INVARIANT;
  1379.                 return textInfo;
  1380.             }
  1381.         }
  1382.        
  1383.        
  1384.         ////////////////////////////////////////////////////////////////////////////
  1385.         //
  1386.         // Unescape a Win32 style quote string
  1387.         //
  1388.         // This is also the escaping style used by custom culture data files
  1389.         //
  1390.         // This removes the 'fred' and 'fred''s' windows quoted formatting from a string.
  1391.         // The output string will NOT have ANY escaping. Currently its used for
  1392.         // separators, where the output string has no characters with special meaning
  1393.         //
  1394.         // We don't build the stringbuilder unless we find a '. If we find a ', we
  1395.         // always build a stringbuilder because we need to remove the '.
  1396.         //
  1397.         ////////////////////////////////////////////////////////////////////////////
  1398.         private static string UnescapeWin32String(string str, int start, int end)
  1399.         {
  1400.             StringBuilder result = null;
  1401.            
  1402.             bool inQuote = false;
  1403.             for (int i = start; i < str.Length && i <= end; i++) {
  1404.                 // Look for quote
  1405.                 if (str[i] == '\'') {
  1406.                     // Already in quote?
  1407.                     if (inQuote) {
  1408.                         BCLDebug.Assert(result != null, "[CultureTable.UnescapeWin32String]Expect result to be non-null");
  1409.                         // See another single quote. Is this '' of 'fred''s' or ending quote?
  1410.                         if (i + 1 < str.Length) {
  1411.                             if (str[i + 1] == '\'') {
  1412.                                 // Append a ' and keep going (so we don't turn off quote mode)
  1413.                                 result.Append('\'');
  1414.                                 i++;
  1415.                                 continue;
  1416.                             }
  1417.                         }
  1418.                        
  1419.                         inQuote = false;
  1420.                     }
  1421.                     else {
  1422.                         // Found beginning quote, remove it.
  1423.                         inQuote = true;
  1424.                         if (result == null)
  1425.                             result = new StringBuilder(str, start, i - start, str.Length);
  1426.                     }
  1427.                 }
  1428.                 else {
  1429.                     // If we have a builder we need to add our non-quote char
  1430.                     if (result != null)
  1431.                         result.Append(str[i]);
  1432.                 }
  1433.             }
  1434.            
  1435.             // No ', just return input string substring
  1436.             if (result == null)
  1437.                 return (str.Substring(start, end - start + 1));
  1438.            
  1439.             // Had ', need to use the builder
  1440.             return (result.ToString());
  1441.         }
  1442.        
  1443.        
  1444.         ////////////////////////////////////////////////////////////////////////////
  1445.         //
  1446.         // Reescape a Win32 style quote string as a NLS+ style quoted string
  1447.         //
  1448.         // This is also the escaping style used by custom culture data files
  1449.         //
  1450.         // NLS+ uses \ to escape the next character, whether in a quoted string or
  1451.         // not, so we always have to change \ to \\.
  1452.         //
  1453.         // NLS+ uses \' to escape a quote inside a quoted string so we have to change
  1454.         // '' to \' (if inside a quoted string)
  1455.         //
  1456.         // We don't build the stringbuilder unless we find something to change
  1457.         ////////////////////////////////////////////////////////////////////////////
  1458.         private static string ReescapeWin32String(string str)
  1459.         {
  1460.             // If we don't have data, then don't try anything
  1461.             if (str == null)
  1462.                 return null;
  1463.            
  1464.             StringBuilder result = null;
  1465.            
  1466.             bool inQuote = false;
  1467.             for (int i = 0; i < str.Length; i++) {
  1468.                 // Look for quote
  1469.                 if (str[i] == '\'') {
  1470.                     // Already in quote?
  1471.                     if (inQuote) {
  1472.                         // See another single quote. Is this '' of 'fred''s' or '''', or is it an ending quote?
  1473.                         if (i + 1 < str.Length && str[i + 1] == '\'') {
  1474.                             // Found another ', so we have ''. Need to add \' instead.
  1475.                             // 1st make sure we have our stringbuilder
  1476.                             if (result == null)
  1477.                                 result = new StringBuilder(str, 0, i, str.Length * 2);
  1478.                            
  1479.                             // Append a \' and keep going (so we don't turn off quote mode)
  1480.                             result.Append("\\'");
  1481.                             i++;
  1482.                             continue;
  1483.                         }
  1484.                        
  1485.                         // Turning off quote mode, fall through to add it
  1486.                         inQuote = false;
  1487.                     }
  1488.                     else {
  1489.                         // Found beginning quote, fall through to add it
  1490.                         inQuote = true;
  1491.                     }
  1492.                 }
  1493.                 // Is there a single \ character?
  1494.                 else if (str[i] == '\\') {
  1495.                     // Found a \, need to change it to \\
  1496.                     // 1st make sure we have our stringbuilder
  1497.                     if (result == null)
  1498.                         result = new StringBuilder(str, 0, i, str.Length * 2);
  1499.                    
  1500.                     // Append our \\ to the string & continue
  1501.                     result.Append("\\\\");
  1502.                     continue;
  1503.                 }
  1504.                
  1505.                 // If we have a builder we need to add our character
  1506.                 if (result != null)
  1507.                     result.Append(str[i]);
  1508.             }
  1509.            
  1510.             // Unchanged string? , just return input string
  1511.             if (result == null)
  1512.                 return str;
  1513.            
  1514.             // String changed, need to use the builder
  1515.             return result.ToString();
  1516.         }
  1517.        
  1518.         private static string[] ReescapeWin32Strings(string[] array)
  1519.         {
  1520.             if (array != null) {
  1521.                 for (int i = 0; i < array.Length; i++) {
  1522.                     array[i] = ReescapeWin32String(array[i]);
  1523.                 }
  1524.             }
  1525.            
  1526.             return array;
  1527.         }
  1528.        
  1529.         unsafe internal string STIME {
  1530.             get {
  1531.                 // Compute SDATE from STIMEFORMAT
  1532.                 string timeFormat = GetOverrideStringArrayDefault(this.m_pData->saTimeFormat, CultureTableData.LOCALE_STIMEFORMAT);
  1533.                 return GetTimeSeparator(timeFormat);
  1534.             }
  1535.         }
  1536.        
  1537.         unsafe internal string SDATE {
  1538.             get {
  1539.                 // Compute SDATE from SSHORTDATE
  1540.                 string shortDate = GetOverrideStringArrayDefault(this.m_pData->saShortDate, CultureTableData.LOCALE_SSHORTDATE);
  1541.                 return GetDateSeparator(shortDate);
  1542.             }
  1543.         }
  1544.        
  1545.         private static string GetTimeSeparator(string format)
  1546.         {
  1547.             // Time format separator (ie: : in 12:39:00)
  1548.             //
  1549.             // We calculate this from the provided time format
  1550.             //
  1551.            
  1552.             //
  1553.             // Find the time separator so that we can pretend we know STIME.
  1554.             //
  1555.             string strUse = String.Empty;
  1556.             int count = 0;
  1557.             int separatorStart = -1;
  1558.            
  1559.             // Look through the whole string
  1560.             for (count = 0; count < format.Length; count++) {
  1561.                 // See if we have Hhms
  1562.                 if (format[count] == 'H' || format[count] == 'h' || format[count] == 'm' || format[count] == 's') {
  1563.                     // Found a time part, find out when it changes
  1564.                     char cFound = format[count];
  1565.                    
  1566.                     for (count++; count < format.Length && format[count] == cFound; count++) {
  1567.                         // Done
  1568.                     }
  1569.                    
  1570.                     // Did we find anything?
  1571.                     if (count < format.Length) {
  1572.                         // We found start of separator
  1573.                         separatorStart = count;
  1574.                         break;
  1575.                     }
  1576.                 }
  1577.                
  1578.                 // If it was quotes, ignore quoted stuff
  1579.                 if (format[count] == '\'') {
  1580.                     //
  1581.                     // Ignore quotes.
  1582.                     //
  1583.                    
  1584.                     for (count++; count < format.Length && (format[count] != '\''); count++) {
  1585.                         // Done
  1586.                     }
  1587.                    
  1588.                     // Don't go past end of string
  1589.                 }
  1590.                
  1591.                 // Advance to next char (skipping unknown char or last quote)
  1592.             }
  1593.            
  1594.             // Now we need to find the end of the separator
  1595.             if (separatorStart != -1) {
  1596.                 for (count = separatorStart; count < format.Length; count++) {
  1597.                     // See if we have Hhms
  1598.                     if (format[count] == 'H' || format[count] == 'h' || format[count] == 'm' || format[count] == 's') {
  1599.                         // Found a time part, stop, we can look for our separator
  1600.                         // From [separatorStart, count) is our string, except we don't want ''s
  1601.                         strUse = UnescapeWin32String(format, separatorStart, count - 1);
  1602.                         break;
  1603.                     }
  1604.                    
  1605.                     // If it was quotes, ignore quoted stuff
  1606.                     if (format[count] == '\'') {
  1607.                         //
  1608.                         // Ignore quotes.
  1609.                         //
  1610.                         for (count++; count < format.Length && (format[count] != '\''); count++) {
  1611.                             // Done
  1612.                         }
  1613.                        
  1614.                         // Don't go past end of string
  1615.                     }
  1616.                    
  1617.                     // Advance to next char (skipping unknown char or last quote)
  1618.                 }
  1619.             }
  1620.            
  1621.             // Return the one we're using
  1622.             return strUse;
  1623.         }
  1624.        
  1625.         private static string GetDateSeparator(string format)
  1626.         {
  1627.             // Date format separator (ie: / in 9/1/03)
  1628.             //
  1629.             // We calculate this from the provided short date
  1630.             //
  1631.            
  1632.             //
  1633.             // Find the date separator so that we can pretend we know SDATE.
  1634.             //
  1635.             string strUse = String.Empty;
  1636.             int count = 0;
  1637.             int separatorStart = -1;
  1638.            
  1639.             // Look through the whole string
  1640.             for (count = 0; count < format.Length; count++) {
  1641.                 // See if we have dyM
  1642.                 if (format[count] == 'd' || format[count] == 'y' || format[count] == 'M') {
  1643.                     // Found a time part, find out when it changes
  1644.                     char cFound = format[count];
  1645.                    
  1646.                     for (count++; count < format.Length && format[count] == cFound; count++) {
  1647.                         // Done
  1648.                     }
  1649.                    
  1650.                     // Did we find anything?
  1651.                     if (count < format.Length) {
  1652.                         // We found start of separator
  1653.                         separatorStart = count;
  1654.                         break;
  1655.                     }
  1656.                 }
  1657.                
  1658.                 // If it was quotes, ignore quoted stuff
  1659.                 if (format[count] == '\'') {
  1660.                     //
  1661.                     // Ignore quotes.
  1662.                     //
  1663.                    
  1664.                     for (count++; count < format.Length && (format[count] != '\''); count++) {
  1665.                         // Done
  1666.                     }
  1667.                    
  1668.                     // Don't go past end of string
  1669.                 }
  1670.                
  1671.                 // Advance to next char (skipping unknown char or last quote)
  1672.             }
  1673.            
  1674.             // Now we need to find the end of the separator
  1675.             if (separatorStart != -1) {
  1676.                 for (count = separatorStart; count < format.Length; count++) {
  1677.                     // See if we have yMd
  1678.                     if (format[count] == 'y' || format[count] == 'M' || format[count] == 'd') {
  1679.                         // Found a time part, stop, we can look for our separator
  1680.                         // From [separatorStart, count) is our string, except we don't want ''s
  1681.                         strUse = UnescapeWin32String(format, separatorStart, count - 1);
  1682.                         break;
  1683.                     }
  1684.                    
  1685.                     // If it was quotes, ignore quoted stuff
  1686.                     if (format[count] == '\'') {
  1687.                         //
  1688.                         // Ignore quotes.
  1689.                         //
  1690.                         for (count++; count < format.Length && (format[count] != '\''); count++) {
  1691.                             // Done
  1692.                         }
  1693.                        
  1694.                         // Don't go past end of string
  1695.                     }
  1696.                    
  1697.                     // Advance to next char (skipping unknown char or last quote)
  1698.                 }
  1699.             }
  1700.            
  1701.             // Return the one we're using
  1702.             return strUse;
  1703.         }
  1704.        
  1705.         ////////////////////////////////////////////////////////////////////////////
  1706.         //
  1707.         // Parameters:
  1708.         // calendarValueOnly Retrieve the values which are affected by the calendar change of DTFI.
  1709.         // This will cause values like longTimePattern not be retrieved since it is
  1710.         // not affected by the Calendar property in DTFI.
  1711.         //
  1712.         ////////////////////////////////////////////////////////////////////////////
  1713.         unsafe internal void GetDTFIOverrideValues(ref DTFIUserOverrideValues values)
  1714.         {
  1715.             BCLDebug.Assert(UseUserOverride, "CultureTableRecord.GetDTFIOverrideValues(): Call this only when UseUserOverride is true.");
  1716.             bool result = false;
  1717.             if (UseGetLocaleInfo)
  1718.                 result = CultureInfo.nativeGetDTFIUserValues(InteropLCID, ref values);
  1719.            
  1720.             if (result) {
  1721.                
  1722.                 // if we got values.yearMonthPattern = null this means the data is not located in the registry and
  1723.                 // we couldn't call GetLocaleInfo. we leave yearMonthPattern as null here so the caller (DTFI)
  1724.                 // will initialize it properly.
  1725.                
  1726.                 values.firstDayOfWeek = ConvertFirstDayOfWeekMonToSun((int)values.firstDayOfWeek);
  1727.                
  1728.                 // Need to do escaping of win32/file type patterns to NLS type patterns
  1729.                 values.shortDatePattern = ReescapeWin32String(values.shortDatePattern);
  1730.                 values.longDatePattern = ReescapeWin32String(values.longDatePattern);
  1731.                 values.longTimePattern = ReescapeWin32String(values.longTimePattern);
  1732.                 values.yearMonthPattern = ReescapeWin32String(values.yearMonthPattern);
  1733.             }
  1734.             else {
  1735.                 //
  1736.                 // We do not use user-override values or something failed during the call to GetLocaleInfo(). Use the information in culture.nlp.
  1737.                 //
  1738.                 values.firstDayOfWeek = IFIRSTDAYOFWEEK;
  1739.                 values.calendarWeekRule = IFIRSTWEEKOFYEAR;
  1740.                 values.shortDatePattern = SSHORTDATE;
  1741.                 values.longDatePattern = SLONGDATE;
  1742.                 values.yearMonthPattern = SYEARMONTH;
  1743.                 values.amDesignator = S1159;
  1744.                 values.pmDesignator = S2359;
  1745.                 values.longTimePattern = STIMEFORMAT;
  1746.             }
  1747.         }
  1748.        
  1749.         unsafe internal void GetNFIOverrideValues(NumberFormatInfo nfi)
  1750.         {
  1751.             bool result = false;
  1752.             if (UseGetLocaleInfo) {
  1753.                 result = CultureInfo.nativeGetNFIUserValues(InteropLCID, nfi);
  1754.             }
  1755.            
  1756.             if (!result) {
  1757.                 // Something failed during the call to GetLocaleInfo(). Use the information in culture.nlp.
  1758.                 nfi.numberDecimalDigits = IDIGITS;
  1759.                 nfi.numberNegativePattern = INEGNUMBER;
  1760.                 nfi.currencyDecimalDigits = ICURRDIGITS;
  1761.                 nfi.currencyPositivePattern = ICURRENCY;
  1762.                 nfi.currencyNegativePattern = INEGCURR;
  1763.                 nfi.negativeSign = SNEGATIVESIGN;
  1764.                 nfi.numberDecimalSeparator = SDECIMAL;
  1765.                 nfi.numberGroupSeparator = STHOUSAND;
  1766.                 nfi.positiveSign = SPOSITIVESIGN;
  1767.                 nfi.currencyDecimalSeparator = SMONDECIMALSEP;
  1768.                 nfi.currencySymbol = SCURRENCY;
  1769.                 nfi.currencyGroupSeparator = SMONTHOUSANDSEP;
  1770.                 nfi.nativeDigits = SNATIVEDIGITS;
  1771.                 nfi.digitSubstitution = IDIGITSUBSTITUTION;
  1772.             }
  1773.             else if (-1 == nfi.digitSubstitution) {
  1774.                 // This is a Win2000 and above property, so when it is marked as -1
  1775.                 // (an invalid value) we know it failed for Win9x reasons and that
  1776.                 // we should fall back to getting this infotmation from culture.nlp.
  1777.                 nfi.digitSubstitution = IDIGITSUBSTITUTION;
  1778.             }
  1779.            
  1780.             nfi.numberGroupSizes = SGROUPING;
  1781.             nfi.currencyGroupSizes = SMONGROUPING;
  1782.            
  1783.             nfi.percentDecimalDigits = nfi.numberDecimalDigits;
  1784.             nfi.percentDecimalSeparator = nfi.numberDecimalSeparator;
  1785.             nfi.percentGroupSizes = nfi.numberGroupSizes;
  1786.             nfi.percentGroupSeparator = nfi.numberGroupSeparator;
  1787.             nfi.percentNegativePattern = INEGATIVEPERCENT;
  1788.             nfi.percentPositivePattern = IPOSITIVEPERCENT;
  1789.             nfi.percentSymbol = SPERCENT;
  1790.            
  1791.             if (nfi.positiveSign == null || nfi.positiveSign.Length == 0)
  1792.                 nfi.positiveSign = "+";
  1793.            
  1794.             if (nfi.currencyDecimalSeparator.Length == 0) {
  1795.                 nfi.currencyDecimalSeparator = SMONDECIMALSEP;
  1796.             }
  1797.            
  1798.            
  1799.         }
  1800.        
  1801.        
  1802.        
  1803.         //
  1804.         unsafe internal int EverettDataItem()
  1805.         {
  1806.             // See if its a custom culture
  1807.             if (this.IsCustomCulture) {
  1808.                 return 0;
  1809.             }
  1810.            
  1811.             InitEverettCultureDataItemMapping();
  1812.             // Normal culture, look up its data item from our LCID
  1813.             // Do a binary search
  1814.             int left = 0;
  1815.             int right = (m_EverettCultureDataItemMappingsSize / 2) - 1;
  1816.            
  1817.             while (left <= right) {
  1818.                 int mid = (left + right) / 2;
  1819.                 int result = this.m_CultureID - m_EverettCultureDataItemMappings[mid * 2];
  1820.                 if (result == 0) {
  1821.                     // Found it, return the index
  1822.                     return m_EverettCultureDataItemMappings[mid * 2 + 1];
  1823.                 }
  1824.                 if (result < 0)
  1825.                     right = mid - 1;
  1826.                 else
  1827.                     left = mid + 1;
  1828.             }
  1829.            
  1830.             return 0;
  1831.         }
  1832.        
  1833.         unsafe internal int EverettRegionDataItem()
  1834.         {
  1835.             // See if its a custom culture
  1836.             if (this.IsCustomCulture) {
  1837.                 return 0;
  1838.             }
  1839.            
  1840.             InitEverettRegionDataItemMapping();
  1841.             // Normal culture, look up its data item from our LCID
  1842.             // Do a binary search
  1843.             int left = 0;
  1844.             int right = (m_EverettRegionDataItemMappingsSize / 2) - 1;
  1845.            
  1846.             while (left <= right) {
  1847.                 int mid = (left + right) / 2;
  1848.                 int result = this.m_CultureID - m_EverettRegionDataItemMappings[mid * 2];
  1849.                 if (result == 0) {
  1850.                     // Found it, return the index
  1851.                     return m_EverettRegionDataItemMappings[mid * 2 + 1];
  1852.                 }
  1853.                 if (result < 0)
  1854.                     right = mid - 1;
  1855.                 else
  1856.                     left = mid + 1;
  1857.             }
  1858.            
  1859.             return 0;
  1860.         }
  1861.        
  1862.         unsafe static internal int IdFromEverettDataItem(int iDataItem)
  1863.         {
  1864.             InitEverettDataItemToLCIDMappings();
  1865.            
  1866.             // Assert that it exists
  1867.             BCLDebug.Assert(iDataItem >= 0 && iDataItem < m_EverettDataItemToLCIDMappingsSize, String.Format(CultureInfo.CurrentCulture, "[CultureTableRecord.IdFromEverettDataItem]Expected Everett data item in range of data table {0}", iDataItem));
  1868.             if (iDataItem < 0 || iDataItem >= m_EverettDataItemToLCIDMappingsSize) {
  1869.                 // If the dataItem is not valid, throw.
  1870.                 throw new SerializationException(Environment.GetResourceString("Serialization_InvalidFieldState"));
  1871.             }
  1872.             return m_EverettDataItemToLCIDMappings[iDataItem];
  1873.         }
  1874.        
  1875.         unsafe static internal int IdFromEverettRegionInfoDataItem(int iDataItem)
  1876.         {
  1877.             InitEverettRegionDataItemToLCIDMappings();
  1878.            
  1879.             // Assert that it exists
  1880.             BCLDebug.Assert(iDataItem >= 0 && iDataItem < m_EverettRegionInfoDataItemToLCIDMappingsSize, String.Format(CultureInfo.CurrentCulture, "[CultureTableRecord.IdFromEverettRegionInfoDataItem]Expected Everett data item in range of data table {0}", iDataItem));
  1881.             if (iDataItem < 0 || iDataItem >= m_EverettRegionInfoDataItemToLCIDMappingsSize) {
  1882.                 // If the dataItem is not valid, throw.
  1883.                 throw new SerializationException(Environment.GetResourceString("Serialization_InvalidFieldState"));
  1884.             }
  1885.             return m_EverettRegionInfoDataItemToLCIDMappings[iDataItem];
  1886.         }
  1887.        
  1888.         // The const here should be in sync with the one defined in the native side.
  1889.         const int INT32TABLE_EVERETT_REGION_DATA_ITEM_MAPPINGS = 0;
  1890.         const int INT32TABLE_EVERETT_CULTURE_DATA_ITEM_MAPPINGS = 1;
  1891.         const int INT32TABLE_EVERETT_DATA_ITEM_TO_LCID_MAPPINGS = 2;
  1892.         const int INT32TABLE_EVERETT_REGION_DATA_ITEM_TO_LCID_MAPPINGS = 3;
  1893.        
  1894.         unsafe static int* m_EverettRegionDataItemMappings = null;
  1895.         unsafe static int m_EverettRegionDataItemMappingsSize = 0;
  1896.        
  1897.         //
  1898.         //
  1899.        
  1900.         unsafe private static void InitEverettRegionDataItemMapping()
  1901.         {
  1902.             if (m_EverettRegionDataItemMappings == null) {
  1903.                 int* temp = CultureInfo.nativeGetStaticInt32DataTable(INT32TABLE_EVERETT_REGION_DATA_ITEM_MAPPINGS, out m_EverettRegionDataItemMappingsSize);
  1904.                 m_EverettRegionDataItemMappings = temp;
  1905.                 BCLDebug.Assert(m_EverettRegionDataItemMappings != null, "CultureTableRecord.m_EverettRegionDataItemMappings can not be null");
  1906.                 BCLDebug.Assert(m_EverettRegionDataItemMappingsSize > 0, "CultureTableRecord.m_EverettRegionDataItemMappingsSize > 0");
  1907.             }
  1908.         }
  1909.        
  1910.         unsafe static int* m_EverettCultureDataItemMappings = null;
  1911.         static int m_EverettCultureDataItemMappingsSize = 0;
  1912.        
  1913.         //
  1914.         //
  1915.        
  1916.         unsafe private static void InitEverettCultureDataItemMapping()
  1917.         {
  1918.             if (m_EverettCultureDataItemMappings == null) {
  1919.                 int* temp = CultureInfo.nativeGetStaticInt32DataTable(INT32TABLE_EVERETT_CULTURE_DATA_ITEM_MAPPINGS, out m_EverettCultureDataItemMappingsSize);
  1920.                 m_EverettCultureDataItemMappings = temp;
  1921.                 BCLDebug.Assert(m_EverettCultureDataItemMappings != null, "CultureTableRecord.m_EverettCultureDataItemMappings can not be null");
  1922.                 BCLDebug.Assert(m_EverettCultureDataItemMappingsSize > 0, "CultureTableRecord.m_EverettCultureDataItemMappingsSize > 0");
  1923.             }
  1924.         }
  1925.        
  1926.        
  1927.         unsafe private static int* m_EverettDataItemToLCIDMappings = null;
  1928.         private static int m_EverettDataItemToLCIDMappingsSize = 0;
  1929.        
  1930.         //
  1931.         //
  1932.        
  1933.         unsafe private static void InitEverettDataItemToLCIDMappings()
  1934.         {
  1935.             if (m_EverettDataItemToLCIDMappings == null) {
  1936.                 int* temp = CultureInfo.nativeGetStaticInt32DataTable(INT32TABLE_EVERETT_DATA_ITEM_TO_LCID_MAPPINGS, out m_EverettDataItemToLCIDMappingsSize);
  1937.                 m_EverettDataItemToLCIDMappings = temp;
  1938.                 BCLDebug.Assert(m_EverettDataItemToLCIDMappings != null, "CultureTableRecord.m_EverettDataItemToLCIDMappings can not be null");
  1939.                 BCLDebug.Assert(m_EverettDataItemToLCIDMappingsSize > 0, "CultureTableRecord.m_EverettDataItemToLCIDMappingsSize > 0");
  1940.             }
  1941.         }
  1942.        
  1943.         unsafe private static int* m_EverettRegionInfoDataItemToLCIDMappings = null;
  1944.         private static int m_EverettRegionInfoDataItemToLCIDMappingsSize = 0;
  1945.        
  1946.         //
  1947.         //
  1948.        
  1949.         unsafe private static void InitEverettRegionDataItemToLCIDMappings()
  1950.         {
  1951.             if (m_EverettRegionInfoDataItemToLCIDMappings == null) {
  1952.                 int* temp = CultureInfo.nativeGetStaticInt32DataTable(INT32TABLE_EVERETT_REGION_DATA_ITEM_TO_LCID_MAPPINGS, out m_EverettRegionInfoDataItemToLCIDMappingsSize);
  1953.                
  1954.                 m_EverettRegionInfoDataItemToLCIDMappings = temp;
  1955.                 BCLDebug.Assert(m_EverettRegionInfoDataItemToLCIDMappings != null, "CultureTableRecord.m_EverettRegionInfoDataItemToLCIDMappings can not be null");
  1956.                 BCLDebug.Assert(m_EverettRegionInfoDataItemToLCIDMappingsSize > 0, "CultureTableRecord.m_EverettRegionInfoDataItemToLCIDMappingsSize > 0");
  1957.             }
  1958.         }
  1959.     }
  1960.    
  1961.     ////////////////////////////////////////////////////////////////////////////
  1962.     //
  1963.     // This structure contains DateTimeFormatInfo properties that can be overridden by users.
  1964.     // We define this structure so that we can fill these values in one FCALL, instead of calling GetLocaleInfo() multiple times in
  1965.     // separate FCalls.
  1966.     //
  1967.     // NOTE: When adding int fields, be sure to pad an extra int so that they are
  1968.     // aligned in DWORD. By doing so, it will make sure that String fields are
  1969.     // aligned in DWORD.
  1970.     //
  1971.     ////////////////////////////////////////////////////////////////////////////
  1972.    
  1973.     [StructLayout(LayoutKind.Sequential, Pack = 2)]
  1974.     internal struct DTFIUserOverrideValues
  1975.     {
  1976.         // DTFI values that are affected by calendar setttings.
  1977.         internal string shortDatePattern;
  1978.         internal string longDatePattern;
  1979.         internal string yearMonthPattern;
  1980.        
  1981.         // DTFI values that will not be affected by calendar settings.
  1982.         internal string amDesignator;
  1983.         internal string pmDesignator;
  1984.         internal string longTimePattern;
  1985.         internal int firstDayOfWeek;
  1986.         internal int padding1;
  1987.         // Add padding to make sure that we are aligned in DWORD. This is important for 64-bit platforms
  1988.         internal int calendarWeekRule;
  1989.         internal int padding2;
  1990.         // Add padding to make sure that we are aligned in DWORD. This is important for 64-bit platforms
  1991.     }
  1992.    
  1993.     // CultureData has a cloned strucure in the native side. we send this struct to the native side to be filled
  1994.     // by the native APIs (mostly GetLocaleInfo) to load the synthetic cultures data.
  1995.     //
  1996.     // IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT
  1997.     // any change in this structure require a change in the cloned one in the native side. (ComNlsInfo.h/.cpp)
  1998.     //
  1999.     // Also we use the default alignment which is 8-bytes in the managed and native sides so don't use the "Pack" property here
  2000.     //
  2001.     // <SyntheticSupport/>
  2002.     [StructLayout(LayoutKind.Sequential)]
  2003.     internal struct CultureData
  2004.     {
  2005.         internal string sIso639Language;
  2006.         // LOCALE_SISO639LANGNAME (TwoLetterISOLanguageName)
  2007.         internal string sIso3166CountryName;
  2008.         // LOCALE_SISO3166CTRYNAME (TwoLetterISORegionName)
  2009.         internal string sListSeparator;
  2010.         // LOCALE_SLIST (ListSeparator)
  2011.         internal string sDecimalSeparator;
  2012.         // LOCALE_SDECIMAL (NumberDecimalSeparator)
  2013.         internal string sThousandSeparator;
  2014.         // LOCALE_STHOUSAND (NumberGroupSeparator)
  2015.         internal string sCurrency;
  2016.         // LOCALE_SCURRENCY (CurrencySymbol)
  2017.         internal string sMonetaryDecimal;
  2018.         // LOCALE_SMONDECIMALSEP (CurrencyDecimalSeparator)
  2019.         internal string sMonetaryThousand;
  2020.         // LOCALE_SMONTHOUSANDSEP (CurrencyGroupSeparator)
  2021.         internal string sNegativeSign;
  2022.         // LOCALE_SNEGATIVESIGN (NegativeSign)
  2023.         internal string sAM1159;
  2024.         // LOCALE_S1159 (AMDesignator)
  2025.         internal string sPM2359;
  2026.         // LOCALE_S2359 (PMDesignator)
  2027.         internal string sAbbrevLang;
  2028.         // LOCALE_SABBREVLANGNAME (ThreeLetterWindowsLanguageName)
  2029.         internal string sEnglishLanguage;
  2030.         // LOCALE_SENGLANGUAGE (Part of EnglishName)
  2031.         internal string sEnglishCountry;
  2032.         // LOCALE_SENGCOUNTRY (Part of EnglishName)
  2033.         internal string sNativeLanguage;
  2034.         // LOCALE_SNATIVELANGNAME (Part of NativeName)
  2035.         internal string sNativeCountry;
  2036.         // LOCALE_SNATIVECTRYNAME (Part of NativeName)
  2037.         internal string sAbbrevCountry;
  2038.         // LOCALE_SABBREVCTRYNAME (ThreeLetterWindowsRegionName)
  2039.         internal string sIntlMonetarySymbol;
  2040.         // LOCALE_SINTLSYMBOL (ISOCurrencySymbol)
  2041.         internal string sEnglishCurrency;
  2042.         // LOCALE_SENGCURRNAME (CurrencyEnglishName)
  2043.         internal string sNativeCurrency;
  2044.         // LOCALE_SNATIVECURRNAME (CurrencyNativeName)
  2045.         internal string saAltSortID;
  2046.         // LOCALE_SSORTNAME (SortName)
  2047.        
  2048.         // sPositiveSign in NLS always return empty string
  2049.         internal string sPositiveSign;
  2050.         // LOCALE_SPOSITIVESIGN (PositiveSign)
  2051.         // saNativeDigits should be converted to array of string instead of array of characters later.
  2052.         internal string saNativeDigits;
  2053.         // LOCALE_SNATIVEDIGITS (NativeDigits)
  2054.         internal string waGrouping;
  2055.         // LOCALE_SGROUPING (NumberGroupSizes)
  2056.         internal string waMonetaryGrouping;
  2057.         // LOCALE_SMONGROUPING (CurrencyGroupSizes)
  2058.         internal string waFontSignature;
  2059.         // LOCALE_FONTSIGNATURE (No API for it)
  2060.         // Some fields defined only post XP
  2061.         internal string sNaN;
  2062.         // LOCALE_SNAN (NaNSymbol)
  2063.         internal string sPositiveInfinity;
  2064.         // LOCALE_SPOSINFINITY (PositiveInfinitySymbol)
  2065.         internal string sNegativeInfinity;
  2066.         // LOCALE_SNEGINFINITY (NegativeInfinitySymbol)
  2067.         internal string sISO3166CountryName2;
  2068.         // LOCALE_SISO3166CTRYNAME2 (ThreeLetterISORegionName)
  2069.         internal string sISO639Language2;
  2070.         // LOCALE_SISO639LANGNAME2 (ThreeLetterISOLanguageName)
  2071.         internal string sIetfLanguage;
  2072.         // LOCALE_SIETFLANGUAGE (IetfLanguageTag)
  2073.         internal string[] saSuperShortDayNames;
  2074.         // LOCALE_SSHORTESTDAYNAME1..LOCALE_SSHORTESTDAYNAME7 (ShortestDayNames)
  2075.         // End of the fields defined only post XP
  2076.         internal string[] saTimeFormat;
  2077.         // EnumTimeFormats (GetAllDateTimePatterns('T'))
  2078.         internal string[] saShortDate;
  2079.         // EnumDateFormatsEx (GetAllDateTimePatterns('d'))
  2080.         internal string[] saLongDate;
  2081.         // EnumDateFormatsEx (GetAllDateTimePatterns('D'))
  2082.         internal string[] saYearMonth;
  2083.         // EnumDateFormatsEx (GetAllDateTimePatterns("Y"))
  2084.         internal string[] saMonthNames;
  2085.         // LOCALE_SMONTHNAME(1~13) (MonthNames)
  2086.         // LOCALE_SDAYNAME1 means Monday in NLS (need conversion in NLS+
  2087.         internal string[] saDayNames;
  2088.         // LOCALE_SDAYNAME(1~7) (GetDayOfWeekNames)
  2089.         // LOCALE_SABBREVDAYNAME means Monday in NLS (need conversion in NLS+
  2090.         internal string[] saAbbrevDayNames;
  2091.         // LOCALE_SABBREVDAYNAME(1~7) (GetAbbreviatedDayOfWeekNames/SuperShortDayNames)
  2092.         internal string[] saAbbrevMonthNames;
  2093.         // LOCALE_SABBREVMONTHNAME(1~13)(AbbreviatedMonthNames)
  2094.         internal string[] saNativeCalendarNames;
  2095.         // GetCalendarInfo/CAL_SCALNAME (NativeCalendarName)
  2096.         internal string[] saGenitiveMonthNames;
  2097.         // GetDateFormat with "dd MMMM" (MonthGenitiveNames)
  2098.         internal string[] saAbbrevGenitiveMonthNames;
  2099.         // GetDateFormat with "d MMM" (AbbreviatedMonthGenitiveNames)
  2100.         // use also EnumCalendarInfo/CAL_ICALINTVALUE
  2101.         internal ushort[] waCalendars;
  2102.         // LOCALE_IOPTIONALCALENDAR (OptionalCalendars)
  2103.         // iFirstDayOfWeek (0 is Monday for NLS and is Sunday in NLS+)
  2104.         internal int iFirstDayOfWeek;
  2105.         // LOCALE_IFIRSTDAYOFWEEK (FirstDayOfWeek)
  2106.         internal int iDigits;
  2107.         // LOCALE_IDIGITS (NumberDecimalDigits)
  2108.         internal int iNegativeNumber;
  2109.         // LOCALE_INEGNUMBER (NumberNegativePattern)
  2110.         internal int iCurrencyDigits;
  2111.         // LOCALE_ICURRDIGITS (CurrencyDecimalDigits)
  2112.         internal int iCurrency;
  2113.         // LOCALE_ICURRENCY (CurrencyPositivePattern)
  2114.         internal int iNegativeCurrency;
  2115.         // LOCALE_INEGCURR (CurrencyNegativePattern)
  2116.         internal int iFirstWeekOfYear;
  2117.         // LOCALE_IFIRSTWEEKOFYEAR (CalendarWeekRule)
  2118.         internal int iMeasure;
  2119.         // LOCALE_IMEASURE (IsMetric)
  2120.         internal int iDigitSubstitution;
  2121.         // LOCALE_IDIGITSUBSTITUTION (DigitSubstitution)
  2122.         internal int iDefaultAnsiCodePage;
  2123.         // LOCALE_IDEFAULTANSICODEPAGE (ANSICodePage)
  2124.         internal int iDefaultOemCodePage;
  2125.         // LOCALE_IDEFAULTCODEPAGE (OEMCodePage)
  2126.         internal int iDefaultMacCodePage;
  2127.         // LOCALE_IDEFAULTMACCODEPAGE (MacCodePage)
  2128.         internal int iDefaultEbcdicCodePage;
  2129.         // LOCALE_IDEFAULTEBCDICCODEPAGE(EBCDICCodePage)
  2130.         internal int iCountry;
  2131.         // LOCALE_ICOUNTRY (No API for this field)
  2132.         internal int iPaperSize;
  2133.         // LOCALE_IPAPERSIZE (No API for this field)
  2134.         internal int iLeadingZeros;
  2135.         // LOCALE_IDAYLZERO (No API for this field)
  2136.         internal int iIntlCurrencyDigits;
  2137.         // LOCALE_IINTLCURRDIGITS (No API for this field)
  2138.         internal int iGeoId;
  2139.         // EnumSystemGeoID/GetGeoInfo (RegionInfo.GeoId)
  2140.         internal int iDefaultCalender;
  2141.         // LOCALE_ICALENDARTYPE (No API for this field)
  2142.     }
  2143. }

Developer Fusion