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 static CStringW 28 GetINIFullPath(const CStringW &FileName) 29 { 30 CStringW szDir; 31 CStringW szBuffer; 32 33 GetStorageDirectory(szDir); 34 szBuffer.Format(L"%ls\\rapps\\%ls", szDir.GetString(), FileName.GetString()); 35 36 return szBuffer; 37 } 38 39 CConfigParser::CConfigParser(const CStringW &FileName) : szConfigPath(GetINIFullPath(FileName)) 40 { 41 CacheINI(); 42 } 43 44 void 45 CConfigParser::ReadSection(CStringW &Buffer, const CStringW &Section, BOOL isArch) 46 { 47 DWORD len = 512; 48 DWORD result; 49 50 do 51 { 52 len *= 2; 53 54 result = GetPrivateProfileSectionW(Section, Buffer.GetBuffer(len), len, szConfigPath); 55 Buffer.ReleaseBuffer(result); 56 } while (result == len - 2); 57 58 len = 0; 59 while (len < result) 60 { 61 // Explicitly use the null terminator! 62 CString tmp = Buffer.GetBuffer() + len; 63 if (tmp.GetLength() > 0) 64 { 65 len += tmp.GetLength() + 1; 66 67 int idx = tmp.Find('='); 68 if (idx >= 0) 69 { 70 CString key = tmp.Left(idx); 71 72 #ifndef _M_IX86 73 // On non-x86 architecture we need the architecture specific URL 74 if (!isArch && key == "URLDownload") 75 { 76 continue; 77 } 78 #endif 79 80 // Is this key already present from a more specific translation? 81 if (m_Keys.FindKey(key) >= 0) 82 { 83 continue; 84 } 85 86 CString value = tmp.Mid(idx + 1); 87 m_Keys.Add(key, value); 88 } 89 else 90 { 91 DPRINT1("ERROR: invalid key/value pair: '%S'\n", tmp.GetString()); 92 } 93 } 94 else 95 { 96 break; 97 } 98 } 99 } 100 101 VOID 102 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 ReadSection(Buffer, g_Names.ArchNeutral.Locale, FALSE); 139 if (!g_Names.ArchNeutral.LocaleNeutral.IsEmpty()) 140 { 141 ReadSection(Buffer, g_Names.ArchNeutral.LocaleNeutral, FALSE); 142 } 143 ReadSection(Buffer, g_Names.ArchNeutral.Section, FALSE); 144 } 145 146 BOOL 147 CConfigParser::GetString(const CStringW &KeyName, 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 161 CConfigParser::GetInt(const CStringW &KeyName, INT &iResult) 162 { 163 CStringW Buffer; 164 165 iResult = 0; 166 167 // grab the text version of our entry 168 if (!GetString(KeyName, Buffer)) 169 return FALSE; 170 171 if (Buffer.IsEmpty()) 172 return FALSE; 173 174 // convert it to an actual integer 175 iResult = StrToIntW(Buffer.GetString()); 176 177 // we only care about values > 0 178 return (iResult > 0); 179 } 180