[RAPPS]
[reactos.git] / reactos / base / applications / rapps / available.cpp
index 66277d2..f889117 100644 (file)
  * PROJECT:         ReactOS Applications Manager
  * LICENSE:         GPL - See COPYING in the top level directory
  * FILE:            base/applications/rapps/available.cpp
- * PURPOSE:         Functions for working with available applications
+ * PURPOSE:         Classes for working with available applications
  * PROGRAMMERS:     Dmitry Chapyshev           (dmitry@reactos.org)
  *                  Ismael Ferreras Morezuelas (swyterzone+ros@gmail.com)
+ *                  Alexander Shaposhnikov     (chaez.san@gmail.com)
  */
 
 #include "rapps.h"
 
-inline void _AddText(UINT a, LPCWSTR b, DWORD c, DWORD d)
+ // CAvailableApplicationInfo
+CAvailableApplicationInfo::CAvailableApplicationInfo(const ATL::CStringW& sFileNameParam)
+    : m_Parser(sFileNameParam)
 {
-    if (b[0] != '\0')
-    {
-        WCHAR szText[MAX_STR_LEN];
-        LoadStringW(hInst, a, szText, _countof(szText));
-        InsertRichEditText(szText, c);
-        InsertRichEditText(b, d);
-    }
-}
-
-inline void _AddTextNewl(UINT a, DWORD b)
-{
-    WCHAR szText[MAX_STR_LEN];
-    LoadStringW(hInst, a, szText, _countof(szText));
-    InsertRichEditText(L"\n", 0);
-    InsertRichEditText(szText, b);
-    InsertRichEditText(L"\n", 0);
-}
+    LicenseType = LICENSE_TYPE::None;
+    sFileName = sFileNameParam;
 
-template<typename T, size_t N, size_t N2>
-inline BOOL _GetString(LPCWSTR a, T(&b)[N], T(&cFileName)[N2])
-{
-    return ParserGetString(a, b, N, cFileName);
+    RetrieveGeneralInfo();
 }
 
-template<typename T, size_t N, size_t N2>
-inline void _GetStringNullFailure(LPCWSTR a, T(&b)[N], T(&cFileName)[N2])
+VOID CAvailableApplicationInfo::RefreshAppInfo()
 {
-    if (!_GetString(a, b, cFileName))
+    if (szUrlDownload.IsEmpty())
     {
-        b[0] = '\0';
+        RetrieveGeneralInfo();
     }
 }
 
-//App is "installed" if the RegName or Name is in the registry
-inline BOOL _AppInstallCheckWithKey(PAPPLICATION_INFO Info, REGSAM key)
+VOID CAvailableApplicationInfo::RetrieveGeneralInfo()
 {
-    return (*Info->szRegName
-        && (IsInstalledApplication(Info->szRegName, TRUE, key)
-        || IsInstalledApplication(Info->szRegName, FALSE, key)))
-        || (*Info->szName && (IsInstalledApplication(Info->szName, TRUE, key)
-            || IsInstalledApplication(Info->szName, FALSE, key)));
-}
+    Category = m_Parser.GetInt(L"Category");
 
+    if (!GetString(L"Name", szName)
+        || !GetString(L"URLDownload", szUrlDownload))
+    {
+        return;
+    }
 
-//Check both registry keys in 64bit system
-//TODO: check system type beforehand to avoid double checks?
-inline BOOL _AppInstallCheck(PAPPLICATION_INFO Info)
-{
-    return  _AppInstallCheckWithKey(Info, KEY_WOW64_32KEY)
-        || _AppInstallCheckWithKey(Info, KEY_WOW64_64KEY);
+    GetString(L"RegName", szRegName);
+    GetString(L"Version", szVersion);
+    GetString(L"License", szLicense);
+    GetString(L"Description", szDesc);
+    GetString(L"Size", szSize);
+    GetString(L"URLSite", szUrlSite);
+    GetString(L"CDPath", szCDPath);
+    GetString(L"Language", szRegName);
+    GetString(L"SHA1", szSHA1);
+
+    RetrieveLicenseType();
+    RetrieveLanguages();
+    RetrieveInstalledStatus();
+    if (m_IsInstalled)
+    {
+        RetrieveInstalledVersion();
+    }
 }
 
-//App is "installed" if the RegName or Name is in the registry
-inline BOOL _GetInstalledVersionWithKey(PAPPLICATION_INFO Info, LPWSTR szVersion, UINT iVersionSize, REGSAM key)
+VOID CAvailableApplicationInfo::RetrieveInstalledStatus()
 {
-    return (*Info->szRegName
-        && (InstalledVersion(szVersion, iVersionSize, Info->szRegName, TRUE, key)
-        || InstalledVersion(szVersion, iVersionSize, Info->szRegName, FALSE, key)))
-        || (*Info->szName && (InstalledVersion(szVersion, iVersionSize, Info->szName, TRUE, key)
-            || InstalledVersion(szVersion, iVersionSize, Info->szName, FALSE, key)));
+    m_IsInstalled = ::GetInstalledVersion(NULL, szRegName)
+        || ::GetInstalledVersion(NULL, szName);
 }
 
-//App is "installed" if the RegName or Name is in the registry
-inline BOOL _GetInstalledVersion(PAPPLICATION_INFO Info, LPWSTR szVersion, UINT iVersionSize)
+VOID CAvailableApplicationInfo::RetrieveInstalledVersion()
 {
-    return  _GetInstalledVersionWithKey(Info, szVersion, iVersionSize, KEY_WOW64_32KEY)
-        || _GetInstalledVersionWithKey(Info, szVersion, iVersionSize, KEY_WOW64_64KEY);
+    m_HasInstalledVersion = ::GetInstalledVersion(&szInstalledVersion, szRegName)
+        || ::GetInstalledVersion(&szInstalledVersion, szName);
 }
 
-LIST_ENTRY CachedEntriesHead = {&CachedEntriesHead, &CachedEntriesHead};
-PLIST_ENTRY pCachedEntry = &CachedEntriesHead;
-
-BOOL
-ShowAvailableAppInfo(INT Index)
+VOID CAvailableApplicationInfo::RetrieveLanguages()
 {
-    PAPPLICATION_INFO Info = (PAPPLICATION_INFO) ListViewGetlParam(Index);
-    WCHAR szVersion[MAX_PATH] = L"\0";
-    WCHAR szLicense[MAX_PATH] = L"\0";
-    BOOL bIsInstalled = _AppInstallCheck(Info), 
-        bHasVersion = _GetInstalledVersion(Info, szVersion, _countof(szVersion));
+    const WCHAR cDelimiter = L'|';
+    ATL::CStringW szBuffer;
 
-    if (!Info) return FALSE;
+    // TODO: Get multiline parameter
+    if (!m_Parser.GetString(L"Languages", szBuffer))
+    {
+        m_HasLanguageInfo = FALSE;
+        return;
+    }
 
-    NewRichEditText(Info->szName, CFE_BOLD);
-    //installed status
-    if (bIsInstalled)
+    // Parse parameter string
+    ATL::CStringW m_szLocale;
+    int iLCID;
+    for (INT i = 0; szBuffer[i] != UNICODE_NULL; ++i)
     {
-        if (bHasVersion)
+        if (szBuffer[i] != cDelimiter && szBuffer[i] != L'\n')
         {
-            if (CompareVersionsStrings(Info->szVersion, szVersion))
-                _AddTextNewl(IDS_STATUS_UPDATE_AVAILABLE, CFE_ITALIC);
-            else
-                _AddTextNewl(IDS_STATUS_INSTALLED, CFE_ITALIC);
-
-            _AddText(IDS_AINFO_VERSION, szVersion, CFE_BOLD, 0);
-        } 
+            m_szLocale += szBuffer[i];
+        }
         else
-            _AddTextNewl(IDS_STATUS_INSTALLED, CFE_ITALIC);
+        {
+            if (StrToIntExW(m_szLocale.GetString(), STIF_DEFAULT, &iLCID))
+            {
+                Languages.Add(static_cast<LCID>(iLCID));
+                m_szLocale.Empty();
+            }
+        }
+    }
 
+    // For the text after delimiter
+    if (!m_szLocale.IsEmpty())
+    {
+        if (StrToIntExW(m_szLocale.GetString(), STIF_DEFAULT, &iLCID))
+        {
+            Languages.Add(static_cast<LCID>(iLCID));
+        }
     }
-    else
-        _AddTextNewl(IDS_STATUS_NOTINSTALLED, CFE_ITALIC);
 
+    m_HasLanguageInfo = TRUE;
+}
+
+VOID CAvailableApplicationInfo::RetrieveLicenseType()
+{
+    INT IntBuffer = m_Parser.GetInt(L"LicenseType");
 
-    _AddText(IDS_AINFO_AVAILABLEVERSION, Info->szVersion, CFE_BOLD, 0);
-    //license
-    switch (Info->LicenseType)
+    if (IntBuffer < 0 || IntBuffer > LICENSE_TYPE::Max)
     {
-        case LICENSE_TYPE::OpenSource:
-            LoadStringW(hInst, IDS_LICENSE_OPENSOURCE, szLicense, _countof(szLicense));
-            break;
-        case LICENSE_TYPE::Freeware:
-            LoadStringW(hInst, IDS_LICENSE_FREEWARE, szLicense, _countof(szLicense));
-            break;
-        case LICENSE_TYPE::Trial:
-            LoadStringW(hInst, IDS_LICENSE_TRIAL, szLicense, _countof(szLicense));
-            break;
-        default:
-            break;
+        LicenseType = LICENSE_TYPE::None;
     }
-    if (szLicense[0] != '\0')
+    else
     {
-        StringCbPrintfW(szLicense, _countof(szLicense), L"%ls (%ls)", szLicense, Info->szLicense);
-        _AddText(IDS_AINFO_LICENSE, szLicense, CFE_BOLD, 0);
+        LicenseType = (LICENSE_TYPE) IntBuffer;
     }
-    else
+}
+
+BOOL CAvailableApplicationInfo::FindInLanguages(LCID what) const
+{
+    if (!m_HasLanguageInfo)
     {
-        _AddText(IDS_AINFO_LICENSE, Info->szLicense, CFE_BOLD, 0);
+        return FALSE;
     }
-    _AddText(IDS_AINFO_SIZE, Info->szSize, CFE_BOLD, 0);
-    _AddText(IDS_AINFO_URLSITE, Info->szUrlSite, CFE_BOLD, CFE_LINK);
-    _AddText(IDS_AINFO_DESCRIPTION, Info->szDesc, CFE_BOLD, 0);
-    _AddText(IDS_AINFO_URLDOWNLOAD, Info->szUrlDownload, CFE_BOLD, CFE_LINK);
 
-    return TRUE;
+    //Find locale code in the list
+    const INT nLanguagesSize = Languages.GetSize();
+    for (INT i = 0; i < nLanguagesSize; ++i)
+    {
+        if (Languages[i] == what)
+        {
+            return TRUE;
+        }
+    }
+
+    return FALSE;
 }
 
-static BOOL
-DeleteCurrentAppsDB(VOID)
+BOOL CAvailableApplicationInfo::HasLanguageInfo() const
 {
-    HANDLE hFind = INVALID_HANDLE_VALUE;
-    WIN32_FIND_DATAW FindFileData;
-    WCHAR szCabPath[MAX_PATH];
-    WCHAR szSearchPath[MAX_PATH];
-    WCHAR szPath[MAX_PATH];
-    WCHAR szTmp[MAX_PATH];
-    HRESULT hr;
-    BOOL result = TRUE;
+    return m_HasLanguageInfo;
+}
 
-    if (!GetStorageDirectory(szPath, _countof(szPath)))
-        return FALSE;
+BOOL CAvailableApplicationInfo::HasNativeLanguage() const
+{
+    return FindInLanguages(GetUserDefaultLCID());
+}
 
-    hr = StringCbPrintfW(szCabPath, sizeof(szCabPath),
-        L"%ls\\rappmgr.cab",
-        szPath);
-    if (FAILED(hr))
-        return FALSE;
+BOOL CAvailableApplicationInfo::HasEnglishLanguage() const
+{
+    return FindInLanguages(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), SORT_DEFAULT));
+}
 
-    result = result && DeleteFileW(szCabPath);
+BOOL CAvailableApplicationInfo::IsInstalled() const
+{
+    return m_IsInstalled;
+}
 
-    hr = StringCbCatW(szPath, sizeof(szPath), L"\\rapps\\");
+BOOL CAvailableApplicationInfo::HasInstalledVersion() const
+{
+    return m_HasInstalledVersion;
+}
+
+BOOL CAvailableApplicationInfo::HasUpdate() const
+{
+    return (szInstalledVersion.Compare(szVersion) < 0) ? TRUE : FALSE;
+}
 
-    if (FAILED(hr))
+VOID CAvailableApplicationInfo::SetLastWriteTime(FILETIME* ftTime)
+{
+    RtlCopyMemory(&ftCacheStamp, ftTime, sizeof(FILETIME));
+}
+
+inline BOOL CAvailableApplicationInfo::GetString(LPCWSTR lpKeyName, ATL::CStringW& ReturnedString)
+{
+    if (!m_Parser.GetString(lpKeyName, ReturnedString))
+    {
+        ReturnedString.Empty();
         return FALSE;
+    }
+    return TRUE;
+}
+// CAvailableApplicationInfo 
+
+// CAvailableApps
+CAvailableApps::CAvailableApps()
+{
+    //set all paths
+    if (GetStorageDirectory(m_szPath))
+    {
+        m_szAppsPath = m_szPath + L"\\rapps\\";
+        m_szCabPath = m_szPath + L"\\rappmgr.cab";
+        m_szSearchPath = m_szAppsPath + L"*.txt";
+    }
+}
+
+VOID CAvailableApps::FreeCachedEntries()
+{
+    POSITION InfoListPosition = m_InfoList.GetHeadPosition();
+
+    /* loop and deallocate all the cached app infos in the list */
+    while (InfoListPosition)
+    {
+        CAvailableApplicationInfo* Info = m_InfoList.GetAt(InfoListPosition);
+        m_InfoList.RemoveHead();
+        delete Info;
+
+        InfoListPosition = m_InfoList.GetHeadPosition();
+    }
+}
 
-    hr = StringCbPrintfW(szSearchPath, sizeof(szSearchPath),
-        L"%ls*.txt",
-        szPath);
-    if (FAILED(hr))
+BOOL CAvailableApps::DeleteCurrentAppsDB()
+{
+    HANDLE hFind = INVALID_HANDLE_VALUE;
+    WIN32_FIND_DATAW FindFileData;
+    BOOL result = TRUE;
+
+    if (m_szPath.IsEmpty())
         return FALSE;
 
-    hFind = FindFirstFileW(szSearchPath, &FindFileData);
+    result = result && DeleteFileW(m_szCabPath.GetString());
+    hFind = FindFirstFileW(m_szSearchPath.GetString(), &FindFileData);
 
     if (hFind == INVALID_HANDLE_VALUE)
         return result;
 
+    ATL::CStringW szTmp;
     do
     {
-        hr = StringCbPrintfW(szTmp, sizeof(szTmp),
-            L"%ls%ls",
-            szPath, FindFileData.cFileName);
-        if (FAILED(hr))
-            continue;
-        result = result && DeleteFileW(szTmp);
-
-    }
-    while (FindNextFileW(hFind, &FindFileData) != 0);
+        szTmp = m_szPath + FindFileData.cFileName;
+        result = result && DeleteFileW(szTmp.GetString());
+    } while (FindNextFileW(hFind, &FindFileData) != 0);
 
     FindClose(hFind);
 
     return result;
 }
 
-
-BOOL
-UpdateAppsDB(VOID)
+BOOL CAvailableApps::UpdateAppsDB()
 {
-    WCHAR szPath[MAX_PATH];
-    WCHAR szAppsPath[MAX_PATH];
-    WCHAR szCabPath[MAX_PATH];
-
     if (!DeleteCurrentAppsDB())
         return FALSE;
 
-    DownloadApplicationsDB(APPLICATION_DATABASE_URL);
+    DownloadManager::DownloadApplicationsDB(APPLICATION_DATABASE_URL);
 
-    if (!GetStorageDirectory(szPath, _countof(szPath)))
-        return FALSE;
-
-    if (FAILED(StringCbPrintfW(szCabPath, sizeof(szCabPath),
-        L"%ls\\rappmgr.cab",
-        szPath)))
-    {
+    if (m_szPath.IsEmpty())
         return FALSE;
-    }
 
-    if (FAILED(StringCbPrintfW(szAppsPath, sizeof(szAppsPath),
-        L"%ls\\rapps\\",
-        szPath)))
+    if (!ExtractFilesFromCab(m_szCabPath, m_szAppsPath))
     {
         return FALSE;
     }
 
-    ExtractFilesFromCab(szCabPath, szAppsPath);
-
     return TRUE;
 }
 
-
-BOOL
-EnumAvailableApplications(INT EnumType, AVAILENUMPROC lpEnumProc)
+BOOL CAvailableApps::EnumAvailableApplications(INT EnumType, AVAILENUMPROC lpEnumProc)
 {
     HANDLE hFind = INVALID_HANDLE_VALUE;
     WIN32_FIND_DATAW FindFileData;
-    WCHAR szPath[MAX_PATH];
-    WCHAR szAppsPath[MAX_PATH];
-    WCHAR szCabPath[MAX_PATH];
-    PAPPLICATION_INFO Info;
-    HRESULT hr;
 
-    if (!GetStorageDirectory(szPath, _countof(szPath)))
-        return FALSE;
-
-    hr = StringCbPrintfW(szCabPath, sizeof(szCabPath),
-        L"%ls\\rappmgr.cab",
-        szPath);
-    if (FAILED(hr))
-        return FALSE;
-
-    hr = StringCbCatW(szPath, sizeof(szPath), L"\\rapps\\");
-
-    if (FAILED(hr))
-        return FALSE;
-
-    hr = StringCbCopyW(szAppsPath, sizeof(szAppsPath), szPath);
-
-    if (FAILED(hr))
-        return FALSE;
-
-    if (!CreateDirectory(szPath, NULL) &&
+    if (!CreateDirectoryW(m_szPath.GetString(), NULL) &&
         GetLastError() != ERROR_ALREADY_EXISTS)
     {
         return FALSE;
     }
 
-    hr = StringCbCatW(szPath, sizeof(szPath), L"*.txt");
-
-    if (FAILED(hr))
-        return FALSE;
-
-    hFind = FindFirstFileW(szPath, &FindFileData);
+    hFind = FindFirstFileW(m_szSearchPath.GetString(), &FindFileData);
 
     if (hFind == INVALID_HANDLE_VALUE)
     {
-        if (GetFileAttributesW(szCabPath) == INVALID_FILE_ATTRIBUTES)
-            DownloadApplicationsDB(APPLICATION_DATABASE_URL);
+        if (GetFileAttributesW(m_szCabPath) == INVALID_FILE_ATTRIBUTES)
+            DownloadManager::DownloadApplicationsDB(APPLICATION_DATABASE_URL);
 
-        ExtractFilesFromCab(szCabPath, szAppsPath);
-        hFind = FindFirstFileW(szPath, &FindFileData);
+        ExtractFilesFromCab(m_szCabPath, m_szAppsPath);
+        hFind = FindFirstFileW(m_szSearchPath.GetString(), &FindFileData);
 
         if (hFind == INVALID_HANDLE_VALUE)
             return FALSE;
@@ -297,106 +291,194 @@ EnumAvailableApplications(INT EnumType, AVAILENUMPROC lpEnumProc)
 
     do
     {
-        /* loop for all the cached entries */
-        for (pCachedEntry = CachedEntriesHead.Flink; pCachedEntry != &CachedEntriesHead; pCachedEntry = pCachedEntry->Flink)
+        // loop for all the cached entries
+        POSITION CurrentListPosition = m_InfoList.GetHeadPosition();
+        CAvailableApplicationInfo* Info = NULL;
+
+        while (CurrentListPosition != NULL)
         {
-            Info = CONTAINING_RECORD(pCachedEntry, APPLICATION_INFO, List);
+            POSITION LastListPosition = CurrentListPosition;
+            Info = m_InfoList.GetNext(CurrentListPosition);
 
-            /* do we already have this entry in cache? */
-            if (_wcsicmp(FindFileData.cFileName, Info->cFileName) == 0)
+            // do we already have this entry in cache?
+            if (Info->sFileName == FindFileData.cFileName)
             {
-                /* is it current enough, or the file has been modified since our last time here? */
+                // is it current enough, or the file has been modified since our last time here?
                 if (CompareFileTime(&FindFileData.ftLastWriteTime, &Info->ftCacheStamp) == 1)
                 {
-                    /* recreate our cache, this is the slow path */
-                    RemoveEntryList(&Info->List);
-                    HeapFree(GetProcessHeap(), 0, Info);
+                    // recreate our cache, this is the slow path
+                    m_InfoList.RemoveAt(LastListPosition);
+
+                    delete Info;
+                    Info = NULL;
+                    break;
                 }
                 else
                 {
-                    /* speedy path, compare directly, we already have the data */
+                    // speedy path, compare directly, we already have the data
                     goto skip_if_cached;
                 }
-
-                break;
             }
         }
 
-        /* create a new entry */
-        Info = (PAPPLICATION_INFO) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(APPLICATION_INFO));
-
-        if (!Info)
-            break;
-
-        Info->Category = ParserGetInt(L"Category", FindFileData.cFileName);
-        INT IntBuffer = ParserGetInt(L"LicenseType", FindFileData.cFileName);
-        if (IntBuffer < LICENSE_TYPE::Max)
-        {
-            Info->LicenseType = (LICENSE_TYPE) IntBuffer;
-        }
-
-        /* copy the cache-related fields for the next time */
-        RtlCopyMemory(&Info->cFileName, &FindFileData.cFileName, MAX_PATH);
-        RtlCopyMemory(&Info->ftCacheStamp, &FindFileData.ftLastWriteTime, sizeof(FILETIME));
-
-        /* add our cached entry to the cached list */
-        InsertTailList(&CachedEntriesHead, &Info->List);
+        // create a new entry
+        Info = new CAvailableApplicationInfo(FindFileData.cFileName);
 
-    skip_if_cached:
+        // set a timestamp for the next time
+        Info->SetLastWriteTime(&FindFileData.ftLastWriteTime);
+        m_InfoList.AddTail(Info);
 
+skip_if_cached:
         if (Info->Category == FALSE)
             continue;
 
         if (EnumType != Info->Category && EnumType != ENUM_ALL_AVAILABLE)
             continue;
 
-        /* if our cache hit was only partial, we need to parse
-           and lazily fill the rest of fields only when needed */
+        Info->RefreshAppInfo();
 
-        if (Info->szUrlDownload[0] == 0)
-        {
-            if (!_GetString(L"Name", Info->szName, FindFileData.cFileName)
-                || !_GetString(L"URLDownload", Info->szUrlDownload, FindFileData.cFileName))
-            {
-                continue;
-            }
-
-            _GetStringNullFailure(L"RegName", Info->szRegName, FindFileData.cFileName);
-            _GetStringNullFailure(L"Version", Info->szVersion, FindFileData.cFileName);
-            _GetStringNullFailure(L"License", Info->szLicense, FindFileData.cFileName);
-            _GetStringNullFailure(L"Description", Info->szDesc, FindFileData.cFileName);
-            _GetStringNullFailure(L"Size", Info->szSize, FindFileData.cFileName);
-            _GetStringNullFailure(L"URLSite", Info->szUrlSite, FindFileData.cFileName);
-            _GetStringNullFailure(L"CDPath", Info->szCDPath, FindFileData.cFileName);
-            _GetStringNullFailure(L"Language", Info->szRegName, FindFileData.cFileName);
-            _GetStringNullFailure(L"SHA1", Info->szSHA1, FindFileData.cFileName);
-        }
-
-        if (!lpEnumProc(Info))
+        if (!lpEnumProc(static_cast<PAPPLICATION_INFO>(Info), m_szAppsPath.GetString()))
             break;
 
-    }
-    while (FindNextFileW(hFind, &FindFileData) != 0);
+    } while (FindNextFileW(hFind, &FindFileData) != 0);
 
     FindClose(hFind);
-
     return TRUE;
 }
 
-VOID FreeCachedAvailableEntries(VOID)
+const ATL::CStringW & CAvailableApps::GetFolderPath()
 {
-    PAPPLICATION_INFO Info;
+    return m_szPath;
+}
 
-    /* loop and deallocate all the cached app infos in the list */
-    for (pCachedEntry = CachedEntriesHead.Flink; pCachedEntry != &CachedEntriesHead;)
+const ATL::CStringW & CAvailableApps::GetAppPath()
+{
+    return m_szAppsPath;
+}
+
+const ATL::CStringW & CAvailableApps::GetCabPath()
+{
+    return m_szCabPath;
+}
+
+const LPCWSTR CAvailableApps::GetFolderPathString()
+{
+    return m_szPath.GetString();
+}
+
+const LPCWSTR CAvailableApps::GetAppPathString()
+{
+    return m_szPath.GetString();
+}
+
+const LPCWSTR CAvailableApps::GetCabPathString()
+{
+    return m_szPath.GetString();
+}
+// CAvailableApps
+
+// CConfigParser
+ATL::CStringW CConfigParser::m_szLocaleID;
+ATL::CStringW CConfigParser::m_szCachedINISectionLocale;
+ATL::CStringW CConfigParser::m_szCachedINISectionLocaleNeutral;
+
+CConfigParser::CConfigParser(const ATL::CStringW& FileName) : szConfigPath(GetINIFullPath(FileName))
+{
+    // we don't have cached section strings for the current system language, create them, lazy
+    CacheINILocaleLazy();
+}
+
+ATL::CStringW CConfigParser::GetINIFullPath(const ATL::CStringW& FileName)
+{
+    ATL::CStringW szDir;
+    ATL::CStringW szBuffer;
+
+    GetStorageDirectory(szDir);
+    szBuffer.Format(L"%ls\\rapps\\%ls", szDir, FileName);
+
+    return szBuffer;
+}
+
+VOID CConfigParser::CacheINILocaleLazy()
+{
+    if (m_szLocaleID.IsEmpty())
     {
-        Info = CONTAINING_RECORD(pCachedEntry, APPLICATION_INFO, List);
+        // TODO: Set default locale if call fails
+        // find out what is the current system lang code (e.g. "0a") and append it to SectionLocale
+        GetLocaleInfoW(GetUserDefaultLCID(), LOCALE_ILANGUAGE,
+                       m_szLocaleID.GetBuffer(m_cchLocaleSize), m_cchLocaleSize);
 
-        /* grab a reference to the next linked entry before getting rid of the current one */
-        pCachedEntry = pCachedEntry->Flink;
+        m_szLocaleID.ReleaseBuffer();
+        m_szCachedINISectionLocale = L"Section." + m_szLocaleID;
 
-        /* flush them down the toilet :D */
-        RemoveEntryList(&Info->List);
-        HeapFree(GetProcessHeap(), 0, Info);
+        // turn "Section.0c0a" into "Section.0a", keeping just the neutral lang part
+        m_szCachedINISectionLocaleNeutral = m_szCachedINISectionLocale + m_szLocaleID.Right(2);
     }
-}
\ No newline at end of file
+}
+
+const ATL::CStringW& CConfigParser::GetLocale()
+{
+    CacheINILocaleLazy();
+    return m_szLocaleID;
+}
+
+INT CConfigParser::GetLocaleSize()
+{
+    return m_cchLocaleSize;
+}
+
+UINT CConfigParser::GetString(const ATL::CStringW& KeyName, ATL::CStringW& ResultString)
+{
+    DWORD dwResult;
+
+    LPWSTR ResultStringBuffer = ResultString.GetBuffer(MAX_PATH);
+    // 1st - find localized strings (e.g. "Section.0c0a")
+    dwResult = GetPrivateProfileStringW(m_szCachedINISectionLocale.GetString(),
+                                        KeyName.GetString(),
+                                        NULL,
+                                        ResultStringBuffer,
+                                        MAX_PATH,
+                                        szConfigPath.GetString());
+
+    if (!dwResult)
+    {
+        // 2nd - if they weren't present check for neutral sub-langs/ generic translations (e.g. "Section.0a")
+        dwResult = GetPrivateProfileStringW(m_szCachedINISectionLocaleNeutral.GetString(),
+                                            KeyName.GetString(),
+                                            NULL,
+                                            ResultStringBuffer,
+                                            MAX_PATH,
+                                            szConfigPath.GetString());
+        if (!dwResult)
+        {
+            // 3rd - if they weren't present fallback to standard english strings (just "Section")
+            dwResult = GetPrivateProfileStringW(L"Section",
+                                                KeyName.GetString(),
+                                                NULL,
+                                                ResultStringBuffer,
+                                                MAX_PATH,
+                                                szConfigPath.GetString());
+        }
+    }
+
+    ResultString.ReleaseBuffer();
+    return (dwResult != 0 ? TRUE : FALSE);
+}
+
+UINT CConfigParser::GetInt(const ATL::CStringW& KeyName)
+{
+    ATL::CStringW Buffer;
+
+    // grab the text version of our entry
+    if (!GetString(KeyName, Buffer))
+        return FALSE;
+
+    if (Buffer.IsEmpty())
+        return FALSE;
+
+    // convert it to an actual integer
+    int result = StrToIntW(Buffer.GetString());
+
+    return (UINT) (result <= 0) ? 0 : result;
+}
+// CConfigParser
\ No newline at end of file