1 /*
2  * PROJECT:     ReactOS Applications Manager
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Config parser
5  * COPYRIGHT:   Copyright 2009 Dmitry Chapyshev (dmitry@reactos.org)
6  *              Copyright 2015 Ismael Ferreras Morezuelas (swyterzone+ros@gmail.com)
7  *              Copyright 2017 Alexander Shaposhnikov (sanchaez@reactos.org)
8  *              Copyright 2021 Mark Jansen <mark.jansen@reactos.org>
9  */
10 #include "rapps.h"
11 #include <debug.h>
12 
13 struct CLocaleSections
14 {
15     CStringW Locale;
16     CStringW LocaleNeutral;
17     CStringW Section;
18 };
19 
20 struct CSectionNames
21 {
22     CLocaleSections ArchSpecific;
23     CLocaleSections ArchNeutral;
24 };
25 static CSectionNames g_Names;
26 
27 
28 static
29 ATL::CStringW GetINIFullPath(const ATL::CStringW& FileName)
30 {
31     ATL::CStringW szDir;
32     ATL::CStringW szBuffer;
33 
34     GetStorageDirectory(szDir);
35     szBuffer.Format(L"%ls\\rapps\\%ls", szDir.GetString(), FileName.GetString());
36 
37     return szBuffer;
38 }
39 
40 CConfigParser::CConfigParser(const ATL::CStringW& FileName)
41     : szConfigPath(GetINIFullPath(FileName))
42 {
43     CacheINI();
44 }
45 
46 void CConfigParser::ReadSection(ATL::CStringW& Buffer, const ATL::CStringW& Section, BOOL isArch)
47 {
48     DWORD len = 512;
49     DWORD result;
50 
51     do
52     {
53         len *= 2;
54 
55         result = GetPrivateProfileSectionW(Section, Buffer.GetBuffer(len), len, szConfigPath);
56         Buffer.ReleaseBuffer(result);
57     } while (result == len - 2);
58 
59     len = 0;
60     while (len < result)
61     {
62         // Explicitly use the null terminator!
63         CString tmp = Buffer.GetBuffer() + len;
64         if (tmp.GetLength() > 0)
65         {
66             len += tmp.GetLength() + 1;
67 
68             int idx = tmp.Find('=');
69             if (idx >= 0)
70             {
71                 CString key = tmp.Left(idx);
72 
73 #ifndef _M_IX86
74                 // On non-x86 architecture we need the architecture specific URL
75                 if (!isArch && key == "URLDownload")
76                 {
77                     continue;
78                 }
79 #endif
80 
81                 // Is this key already present from a more specific translation?
82                 if (m_Keys.FindKey(key) >= 0)
83                 {
84                     continue;
85                 }
86 
87                 CString value = tmp.Mid(idx+1);
88                 m_Keys.Add(key, value);
89             }
90             else
91             {
92                 DPRINT1("ERROR: invalid key/value pair: '%S'\n", tmp.GetString());
93             }
94         }
95         else
96         {
97             break;
98         }
99     }
100 }
101 
102 VOID CConfigParser::CacheINI()
103 {
104     // Cache section names
105     if (g_Names.ArchSpecific.Locale.IsEmpty())
106     {
107         CString szLocaleID;
108         const INT cchLocaleSize = 5;
109 
110         GetLocaleInfoW(GetUserDefaultLCID(), LOCALE_ILANGUAGE, szLocaleID.GetBuffer(cchLocaleSize), cchLocaleSize);
111         szLocaleID.ReleaseBuffer();
112         CString INISectionLocale = L"Section." + szLocaleID;
113 
114         g_Names.ArchSpecific.Locale = INISectionLocale + L"." CurrentArchitecture;
115         g_Names.ArchNeutral.Locale = INISectionLocale;
116 
117         // turn "Section.0c0a" into "Section.0a", keeping just the neutral lang part
118         if (szLocaleID.GetLength() >= 2)
119         {
120             g_Names.ArchSpecific.LocaleNeutral = L"Section." + szLocaleID.Right(2) + L"." CurrentArchitecture;
121             g_Names.ArchNeutral.LocaleNeutral = L"Section." + szLocaleID.Right(2);
122         }
123 
124         g_Names.ArchSpecific.Section = L"Section." CurrentArchitecture;
125         g_Names.ArchNeutral.Section = L"Section";
126     }
127 
128     // Use a shared buffer so that we don't have to re-allocate it every time
129     CStringW Buffer;
130 
131     ReadSection(Buffer, g_Names.ArchSpecific.Locale, TRUE);
132     if (!g_Names.ArchSpecific.LocaleNeutral.IsEmpty())
133     {
134         ReadSection(Buffer, g_Names.ArchSpecific.LocaleNeutral, TRUE);
135     }
136     ReadSection(Buffer, g_Names.ArchSpecific.Section, TRUE);
137 
138 
139     ReadSection(Buffer, g_Names.ArchNeutral.Locale, FALSE);
140     if (!g_Names.ArchNeutral.LocaleNeutral.IsEmpty())
141     {
142         ReadSection(Buffer, g_Names.ArchNeutral.LocaleNeutral, FALSE);
143     }
144     ReadSection(Buffer, g_Names.ArchNeutral.Section, FALSE);
145 }
146 
147 BOOL CConfigParser::GetString(const ATL::CStringW& KeyName, ATL::CStringW& ResultString)
148 {
149     int nIndex = m_Keys.FindKey(KeyName);
150     if (nIndex >= 0)
151     {
152         ResultString = m_Keys.GetValueAt(nIndex);
153         return TRUE;
154     }
155 
156     ResultString.Empty();
157     return FALSE;
158 }
159 
160 BOOL CConfigParser::GetInt(const ATL::CStringW& KeyName, INT& iResult)
161 {
162     ATL::CStringW Buffer;
163 
164     iResult = 0;
165 
166     // grab the text version of our entry
167     if (!GetString(KeyName, Buffer))
168         return FALSE;
169 
170     if (Buffer.IsEmpty())
171         return FALSE;
172 
173     // convert it to an actual integer
174     iResult = StrToIntW(Buffer.GetString());
175 
176     // we only care about values > 0
177     return (iResult > 0);
178 }
179