1 /* 2 * PROJECT: ReactOS system libraries 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Japanese era support 5 * COPYRIGHT: Copyright 2019 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) 6 */ 7 #include <k32.h> 8 9 #define NDEBUG 10 #include <debug.h> 11 #include "japanese.h" 12 13 #define JAPANESE_ERA_MAX 16 14 15 /* #define DONT_USE_REGISTRY */ 16 17 static BOOL s_bIsGannenCached = FALSE; 18 static DWORD s_JapaneseEraCount = 0; 19 static JAPANESE_ERA s_JapaneseEraTable[JAPANESE_ERA_MAX] 20 #ifdef DONT_USE_REGISTRY 21 = 22 { 23 {1868, 1, 1, {0x660E, 0x6CBB}, {0x660E, 0}, L"Meiji", L"M"}, 24 {1912, 7, 30, {0x5927, 0x6B63}, {0x5927, 0}, L"Taisho", L"T"}, 25 {1926, 12, 25, {0x662D, 0x548C}, {0x662D, 0}, L"Showa", L"S"}, 26 {1989, 1, 8, {0x5E73, 0x6210}, {0x5E73, 0}, L"Heisei", L"H"}, 27 {2019, 5, 1, {0x4EE4, 0x548C}, {0x4EE4, 0}, L"Reiwa", L"R"}, 28 } 29 #endif 30 ; 31 32 HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName); 33 34 BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex, 35 LPWSTR szValueName, ULONG valueNameSize, 36 LPWSTR szValueData, ULONG valueDataSize); 37 38 void JapaneseEra_ClearCache(void) 39 { 40 s_bIsGannenCached = FALSE; 41 s_JapaneseEraCount = 0; 42 } 43 44 static INT JapaneseEra_Compare(const void *e1, const void *e2) 45 { 46 PCJAPANESE_ERA pEra1 = (PCJAPANESE_ERA)e1; 47 PCJAPANESE_ERA pEra2 = (PCJAPANESE_ERA)e2; 48 if (pEra1->wYear < pEra2->wYear) 49 return -1; 50 if (pEra1->wYear > pEra2->wYear) 51 return 1; 52 if (pEra1->wMonth < pEra2->wMonth) 53 return -1; 54 if (pEra1->wMonth > pEra2->wMonth) 55 return 1; 56 if (pEra1->wDay < pEra2->wDay) 57 return -1; 58 if (pEra1->wDay > pEra2->wDay) 59 return 1; 60 return 0; 61 } 62 63 BOOL JapaneseEra_IsFirstYearGannen(void) 64 { 65 #ifdef DONT_USE_REGISTRY 66 return TRUE; 67 #else 68 HANDLE KeyHandle; 69 DWORD dwIndex; 70 WCHAR szName[32], szValue[32]; 71 static BOOL s_bFirstIsGannen = TRUE; 72 73 if (s_bIsGannenCached) 74 return s_bFirstIsGannen; 75 76 KeyHandle = NLS_RegOpenKey(NULL, L"\\Registry\\Machine\\System\\" 77 L"CurrentControlSet\\Control\\Nls\\Calendars\\Japanese"); 78 if (!KeyHandle) 79 return TRUE; 80 81 s_bFirstIsGannen = TRUE; 82 for (dwIndex = 0; dwIndex < 16; ++dwIndex) 83 { 84 if (!NLS_RegEnumValue(KeyHandle, dwIndex, szName, sizeof(szName), 85 szValue, sizeof(szValue))) 86 { 87 break; 88 } 89 90 if (lstrcmpiW(szName, L"InitialEraYear") == 0) 91 { 92 s_bFirstIsGannen = (szValue[0] == 0x5143); 93 break; 94 } 95 } 96 97 NtClose(KeyHandle); 98 99 s_bIsGannenCached = TRUE; 100 101 return s_bFirstIsGannen; 102 #endif 103 } 104 105 /* 106 * SEE ALSO: 107 * https://en.wikipedia.org/wiki/Japanese_era_name 108 * https://docs.microsoft.com/en-us/windows/desktop/Intl/era-handling-for-the-japanese-calendar 109 */ 110 static PCJAPANESE_ERA JapaneseEra_Load(DWORD *pdwCount) 111 { 112 #ifndef DONT_USE_REGISTRY 113 HANDLE KeyHandle; 114 DWORD dwIndex; 115 WCHAR szName[128], szValue[128]; 116 JAPANESE_ERA *pEntry; 117 LPWSTR pch1, pch2, pch3, pch4; 118 #endif 119 120 ASSERT(pdwCount != NULL); 121 122 /* return cache if any */ 123 if (s_JapaneseEraCount != 0) 124 { 125 *pdwCount = s_JapaneseEraCount; 126 return s_JapaneseEraTable; 127 } 128 129 #ifdef DONT_USE_REGISTRY 130 s_JapaneseEraCount = ARRAYSIZE(s_JapaneseEraTable); 131 #else 132 /* init */ 133 *pdwCount = 0; 134 RtlZeroMemory(&s_JapaneseEraTable, sizeof(s_JapaneseEraTable)); 135 136 /* open registry key */ 137 KeyHandle = NLS_RegOpenKey(NULL, L"\\Registry\\Machine\\System\\" 138 L"CurrentControlSet\\Control\\Nls\\Calendars\\Japanese\\Eras"); 139 if (!KeyHandle) 140 return NULL; 141 142 /* for all values */ 143 for (dwIndex = 0; dwIndex < JAPANESE_ERA_MAX; ++dwIndex) 144 { 145 pEntry = &s_JapaneseEraTable[dwIndex]; 146 147 /* get name and data */ 148 if (!NLS_RegEnumValue(KeyHandle, dwIndex, szName, sizeof(szName), 149 szValue, sizeof(szValue))) 150 { 151 break; 152 } 153 154 /* split fields */ 155 pch1 = szName; 156 pch2 = wcschr(pch1, L' '); 157 if (pch2 == NULL) 158 { 159 break; 160 } 161 *pch2++ = UNICODE_NULL; 162 163 pch3 = wcschr(pch2, L' '); 164 if (pch3 == NULL) 165 { 166 break; 167 } 168 *pch3++ = UNICODE_NULL; 169 170 pEntry->wYear = _wtoi(pch1); 171 pEntry->wMonth = _wtoi(pch2); 172 pEntry->wDay = _wtoi(pch3); 173 if (pEntry->wYear == 0 || pEntry->wMonth == 0 || pEntry->wDay == 0) 174 { 175 break; 176 } 177 178 /* split fields */ 179 pch1 = szValue; 180 pch2 = wcschr(pch1, L'_'); 181 if (pch2 == NULL) 182 { 183 break; 184 } 185 *pch2++ = UNICODE_NULL; 186 187 pch3 = wcschr(pch2, L'_'); 188 if (pch3 == NULL) 189 { 190 break; 191 } 192 *pch3++ = UNICODE_NULL; 193 194 pch4 = wcschr(pch3, L'_'); 195 if (pch4 == NULL) 196 { 197 break; 198 } 199 *pch4++ = UNICODE_NULL; 200 201 /* store */ 202 RtlStringCbCopyW(pEntry->szEraName, sizeof(pEntry->szEraName), pch1); 203 RtlStringCbCopyW(pEntry->szEraAbbrev, sizeof(pEntry->szEraAbbrev), pch2); 204 RtlStringCbCopyW(pEntry->szEnglishEraName, sizeof(pEntry->szEnglishEraName), pch3); 205 RtlStringCbCopyW(pEntry->szEnglishEraAbbrev, sizeof(pEntry->szEnglishEraAbbrev), pch4); 206 } 207 208 /* close key */ 209 NtClose(KeyHandle); 210 211 /* sort */ 212 qsort(s_JapaneseEraTable, s_JapaneseEraCount, sizeof(JAPANESE_ERA), 213 JapaneseEra_Compare); 214 215 /* make cache */ 216 s_JapaneseEraCount = dwIndex; 217 #endif 218 219 *pdwCount = s_JapaneseEraCount; 220 221 return s_JapaneseEraTable; 222 } 223 224 static BOOL JapaneseEra_ToSystemTime(PCJAPANESE_ERA pEra, LPSYSTEMTIME pst) 225 { 226 ASSERT(pEra != NULL); 227 ASSERT(pst != NULL); 228 229 ZeroMemory(pst, sizeof(*pst)); 230 pst->wYear = pEra->wYear; 231 pst->wMonth = pEra->wMonth; 232 pst->wDay = pEra->wDay; 233 return TRUE; 234 } 235 236 PCJAPANESE_ERA JapaneseEra_Find(const SYSTEMTIME *pst OPTIONAL) 237 { 238 DWORD dwIndex, dwCount = 0; 239 PCJAPANESE_ERA pTable, pEntry, pPrevEntry = NULL; 240 SYSTEMTIME st1, st2; 241 FILETIME ft1, ft2; 242 LONG nCompare; 243 244 /* pst --> ft1 */ 245 if (pst == NULL) 246 { 247 GetLocalTime(&st1); 248 pst = &st1; 249 } 250 SystemTimeToFileTime(pst, &ft1); 251 252 /* load era table */ 253 pTable = JapaneseEra_Load(&dwCount); 254 if (pTable == NULL || dwCount == 0 || dwCount > JAPANESE_ERA_MAX) 255 { 256 return NULL; 257 } 258 259 /* for all eras */ 260 for (dwIndex = 0; dwIndex < dwCount; dwIndex++) 261 { 262 pEntry = &pTable[dwIndex]; 263 264 /* pEntry --> st2 --> ft2 */ 265 JapaneseEra_ToSystemTime(pEntry, &st2); 266 SystemTimeToFileTime(&st2, &ft2); 267 268 /* ft1 <=> ft2 */ 269 nCompare = CompareFileTime(&ft1, &ft2); 270 if (nCompare == 0) 271 return pEntry; 272 if (nCompare < 0) 273 return pPrevEntry; 274 pPrevEntry = pEntry; 275 } 276 277 return pPrevEntry; 278 } 279