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 
JapaneseEra_ClearCache(void)38 void JapaneseEra_ClearCache(void)
39 {
40     s_bIsGannenCached = FALSE;
41     s_JapaneseEraCount = 0;
42 }
43 
JapaneseEra_Compare(const void * e1,const void * e2)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 
JapaneseEra_IsFirstYearGannen(void)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  */
JapaneseEra_Load(DWORD * pdwCount)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 
JapaneseEra_ToSystemTime(PCJAPANESE_ERA pEra,LPSYSTEMTIME pst)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 
JapaneseEra_Find(const SYSTEMTIME * pst OPTIONAL)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