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