mirror of
https://github.com/chibicitiberiu/rainmeter-studio.git
synced 2024-02-24 04:33:31 +00:00
![jsmorley](/git/assets/img/avatar_default.png)
This reverts commit 73069b692f9018f7b8f10a1b8e5a3125f2c252d0. Reverted due to some risk of issues when used in Lua when the .lua file is not encoded and the user's local code page does not support the ANSI "no-break space".
1561 lines
37 KiB
C++
1561 lines
37 KiB
C++
/*
|
|
Copyright (C) 2004 Kimmo Pekkola
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "StdAfx.h"
|
|
#include "../Common/MathParser.h"
|
|
#include "../Common/PathUtil.h"
|
|
#include "ConfigParser.h"
|
|
#include "Litestep.h"
|
|
#include "Rainmeter.h"
|
|
#include "System.h"
|
|
#include "Measure.h"
|
|
#include "Meter.h"
|
|
#include "resource.h"
|
|
|
|
using namespace Gdiplus;
|
|
|
|
std::unordered_map<std::wstring, std::wstring> ConfigParser::c_MonitorVariables;
|
|
|
|
/*
|
|
** The constructor
|
|
**
|
|
*/
|
|
ConfigParser::ConfigParser() :
|
|
m_LastReplaced(false),
|
|
m_LastDefaultUsed(false),
|
|
m_LastValueDefined(false),
|
|
m_CurrentSection(),
|
|
m_MeterWindow()
|
|
{
|
|
}
|
|
|
|
/*
|
|
** The destructor
|
|
**
|
|
*/
|
|
ConfigParser::~ConfigParser()
|
|
{
|
|
}
|
|
|
|
void ConfigParser::Initialize(const std::wstring& filename, MeterWindow* meterWindow, LPCTSTR skinSection, const std::wstring* resourcePath)
|
|
{
|
|
m_MeterWindow = meterWindow;
|
|
|
|
m_Measures.clear();
|
|
m_Sections.clear();
|
|
m_Values.clear();
|
|
m_BuiltInVariables.clear();
|
|
m_Variables.clear();
|
|
|
|
m_StyleTemplate.clear();
|
|
m_LastReplaced = false;
|
|
m_LastDefaultUsed = false;
|
|
m_LastValueDefined = false;
|
|
|
|
m_CurrentSection = nullptr;
|
|
m_SectionInsertPos = m_Sections.end();
|
|
|
|
// Set the built-in variables. Do this before the ini file is read so that the paths can be used with @include
|
|
SetBuiltInVariables(filename, resourcePath, meterWindow);
|
|
ResetMonitorVariables(meterWindow);
|
|
|
|
System::UpdateIniFileMappingList();
|
|
|
|
ReadIniFile(filename, skinSection);
|
|
ReadVariables();
|
|
|
|
// Clear and minimize
|
|
m_FoundSections.clear();
|
|
m_ListVariables.clear();
|
|
m_SectionInsertPos = m_Sections.end();
|
|
}
|
|
|
|
void ConfigParser::SetBuiltInVariables(const std::wstring& filename, const std::wstring* resourcePath, MeterWindow* meterWindow)
|
|
{
|
|
auto insertVariable = [&](const WCHAR* name, std::wstring value)
|
|
{
|
|
return m_BuiltInVariables.insert(std::make_pair(name, value));
|
|
};
|
|
|
|
insertVariable(L"PROGRAMPATH", GetRainmeter().GetPath());
|
|
insertVariable(L"PROGRAMDRIVE", GetRainmeter().GetDrive());
|
|
insertVariable(L"SETTINGSPATH", GetRainmeter().GetSettingsPath());
|
|
insertVariable(L"SKINSPATH", GetRainmeter().GetSkinPath());
|
|
insertVariable(L"PLUGINSPATH", GetRainmeter().GetPluginPath());
|
|
insertVariable(L"CURRENTPATH", PathUtil::GetFolderFromFilePath(filename));
|
|
insertVariable(L"ADDONSPATH", GetRainmeter().GetAddonPath());
|
|
|
|
if (meterWindow)
|
|
{
|
|
insertVariable(L"CURRENTFILE", meterWindow->GetFileName());
|
|
insertVariable(L"CURRENTCONFIG", meterWindow->GetFolderPath());
|
|
insertVariable(L"ROOTCONFIG", meterWindow->GetRootName());
|
|
insertVariable(L"ROOTCONFIGPATH", meterWindow->GetRootPath());
|
|
}
|
|
|
|
insertVariable(L"CRLF", L"\n");
|
|
|
|
m_CurrentSection = &(insertVariable(L"CURRENTSECTION", L"").first->second); // shortcut
|
|
|
|
if (resourcePath)
|
|
{
|
|
SetVariable(L"@", *resourcePath);
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Sets all user-defined variables.
|
|
**
|
|
*/
|
|
void ConfigParser::ReadVariables()
|
|
{
|
|
std::list<std::wstring>::const_iterator iter = m_ListVariables.begin();
|
|
for ( ; iter != m_ListVariables.end(); ++iter)
|
|
{
|
|
SetVariable((*iter), ReadString(L"Variables", (*iter).c_str(), L"", false));
|
|
}
|
|
}
|
|
|
|
void ConfigParser::SetVariable(std::wstring strVariable, const std::wstring& strValue)
|
|
{
|
|
StrToUpperC(strVariable);
|
|
m_Variables[strVariable] = strValue;
|
|
}
|
|
|
|
void ConfigParser::SetBuiltInVariable(const std::wstring& strVariable, const std::wstring& strValue)
|
|
{
|
|
m_BuiltInVariables[strVariable] = strValue;
|
|
}
|
|
|
|
/*
|
|
** Gets a value for the variable. Returns nullptr if not found.
|
|
**
|
|
*/
|
|
const std::wstring* ConfigParser::GetVariable(const std::wstring& strVariable)
|
|
{
|
|
const std::wstring strTmp = StrToUpper(strVariable);
|
|
|
|
// #1: Built-in variables
|
|
std::unordered_map<std::wstring, std::wstring>::const_iterator iter = m_BuiltInVariables.find(strTmp);
|
|
if (iter != m_BuiltInVariables.end())
|
|
{
|
|
return &(*iter).second;
|
|
}
|
|
|
|
// #2: Monitor variables
|
|
iter = c_MonitorVariables.find(strTmp);
|
|
if (iter != c_MonitorVariables.end())
|
|
{
|
|
return &(*iter).second;
|
|
}
|
|
|
|
// #3: User-defined variables
|
|
iter = m_Variables.find(strTmp);
|
|
if (iter != m_Variables.end())
|
|
{
|
|
return &(*iter).second;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/*
|
|
** Gets the value of a section variable. Returns true if strValue is set.
|
|
** The selector is stripped from strVariable.
|
|
**
|
|
*/
|
|
bool ConfigParser::GetSectionVariable(std::wstring& strVariable, std::wstring& strValue)
|
|
{
|
|
size_t colonPos = strVariable.find_last_of(L':');
|
|
if (colonPos == std::wstring::npos)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const std::wstring selector = strVariable.substr(colonPos + 1);
|
|
const WCHAR* selectorSz = selector.c_str();
|
|
strVariable.resize(colonPos);
|
|
|
|
bool isKeySelector = (!selector.empty() && iswalpha(selectorSz[0]));
|
|
|
|
if (isKeySelector)
|
|
{
|
|
// [Meter:X], [Meter:Y], [Meter:W], [Meter:H]
|
|
Meter* meter = m_MeterWindow->GetMeter(strVariable);
|
|
if (meter)
|
|
{
|
|
WCHAR buffer[32];
|
|
if (_wcsicmp(selectorSz, L"X") == 0)
|
|
{
|
|
_itow_s(meter->GetX(), buffer, 10);
|
|
}
|
|
else if (_wcsicmp(selectorSz, L"Y") == 0)
|
|
{
|
|
_itow_s(meter->GetY(), buffer, 10);
|
|
}
|
|
else if (_wcsicmp(selectorSz, L"W") == 0)
|
|
{
|
|
_itow_s(meter->GetW(), buffer, 10);
|
|
}
|
|
else if (_wcsicmp(selectorSz, L"H") == 0)
|
|
{
|
|
_itow_s(meter->GetH(), buffer, 10);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
|
|
strValue = buffer;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Number: [Measure:], [Measure:dec]
|
|
// Percentual: [Measure:%], [Measure:%, dec]
|
|
// Scale: [Measure:/scale], [Measure:/scale, dec]
|
|
// Max/Min: [Measure:MaxValue], [Measure:MaxValue:/scale, dec] ('%' cannot be used)
|
|
enum VALUETYPE
|
|
{
|
|
RAW = 0,
|
|
PERCENTUAL = 1,
|
|
MAX = 2,
|
|
MIN = 3
|
|
} valueType = RAW;
|
|
|
|
if (isKeySelector)
|
|
{
|
|
if (_wcsicmp(selectorSz, L"MaxValue") == 0)
|
|
{
|
|
valueType = MAX;
|
|
}
|
|
else if (_wcsicmp(selectorSz, L"MinValue") == 0)
|
|
{
|
|
valueType = MIN;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
|
|
selectorSz = L"";
|
|
}
|
|
else
|
|
{
|
|
colonPos = strVariable.find_last_of(L':');
|
|
if (colonPos != std::wstring::npos)
|
|
{
|
|
do
|
|
{
|
|
const WCHAR* keySelectorSz = strVariable.c_str() + colonPos + 1;
|
|
|
|
if (_wcsicmp(keySelectorSz, L"MaxValue") == 0)
|
|
{
|
|
valueType = MAX;
|
|
}
|
|
else if (_wcsicmp(keySelectorSz, L"MinValue") == 0)
|
|
{
|
|
valueType = MIN;
|
|
}
|
|
else
|
|
{
|
|
// Section name contains ':' ?
|
|
break;
|
|
}
|
|
|
|
strVariable.resize(colonPos);
|
|
}
|
|
while (0);
|
|
}
|
|
}
|
|
|
|
Measure* measure = m_MeterWindow->GetMeasure(strVariable);
|
|
if (measure)
|
|
{
|
|
int scale = 1;
|
|
|
|
const WCHAR* decimalsSz = wcschr(selectorSz, L',');
|
|
if (decimalsSz)
|
|
{
|
|
++decimalsSz;
|
|
}
|
|
|
|
if (*selectorSz == L'%') // Percentual
|
|
{
|
|
if (valueType == MAX || valueType == MIN) // '%' cannot be used with MAX/MIN value
|
|
{
|
|
return false;
|
|
}
|
|
|
|
valueType = PERCENTUAL;
|
|
}
|
|
else if (*selectorSz == L'/') // Scale
|
|
{
|
|
errno = 0;
|
|
scale = _wtoi(selectorSz + 1);
|
|
if (errno == EINVAL || scale == 0) // Invalid scale value
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (decimalsSz)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
decimalsSz = selectorSz;
|
|
}
|
|
|
|
double value = (valueType == PERCENTUAL) ? measure->GetRelativeValue() * 100.0
|
|
: (valueType == MAX) ? measure->GetMaxValue() / scale
|
|
: (valueType == MIN) ? measure->GetMinValue() / scale
|
|
: measure->GetValue() / scale;
|
|
int decimals = 10;
|
|
|
|
if (decimalsSz)
|
|
{
|
|
while (iswspace(*decimalsSz)) ++decimalsSz;
|
|
|
|
if (*decimalsSz)
|
|
{
|
|
decimals = _wtoi(decimalsSz);
|
|
decimals = max(0, decimals);
|
|
decimals = min(32, decimals);
|
|
}
|
|
else
|
|
{
|
|
decimalsSz = nullptr;
|
|
}
|
|
}
|
|
|
|
WCHAR format[32];
|
|
WCHAR buffer[128];
|
|
_snwprintf_s(format, _TRUNCATE, L"%%.%if", decimals);
|
|
int bufferLen = _snwprintf_s(buffer, _TRUNCATE, format, value);
|
|
|
|
if (!decimalsSz)
|
|
{
|
|
// Remove trailing zeros if decimal count was not specified.
|
|
measure->RemoveTrailingZero(buffer, bufferLen);
|
|
bufferLen = (int)wcslen(buffer);
|
|
}
|
|
|
|
strValue.assign(buffer, bufferLen);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void ConfigParser::ResetMonitorVariables(MeterWindow* meterWindow)
|
|
{
|
|
// Set the SCREENAREA/WORKAREA variables
|
|
if (c_MonitorVariables.empty())
|
|
{
|
|
SetMultiMonitorVariables(true);
|
|
}
|
|
|
|
// Set the SCREENAREA/WORKAREA variables for present monitor
|
|
SetAutoSelectedMonitorVariables(meterWindow);
|
|
}
|
|
|
|
/*
|
|
** Sets new values for the SCREENAREA/WORKAREA variables.
|
|
**
|
|
*/
|
|
void ConfigParser::SetMultiMonitorVariables(bool reset)
|
|
{
|
|
auto setMonitorVariable = [&](const WCHAR* variable, const WCHAR* value)
|
|
{
|
|
c_MonitorVariables[variable] = value;
|
|
};
|
|
|
|
if (!reset && c_MonitorVariables.empty())
|
|
{
|
|
reset = true; // Set all variables
|
|
}
|
|
|
|
const size_t numOfMonitors = System::GetMonitorCount(); // intentional
|
|
const MultiMonitorInfo& monitorsInfo = System::GetMultiMonitorInfo();
|
|
const std::vector<MonitorInfo>& monitors = monitorsInfo.monitors;
|
|
|
|
WCHAR buffer[32];
|
|
const RECT workArea = monitors[monitorsInfo.primary - 1].work;
|
|
const RECT scrArea = monitors[monitorsInfo.primary - 1].screen;
|
|
|
|
_itow_s(workArea.left, buffer, 10);
|
|
setMonitorVariable(L"WORKAREAX", buffer);
|
|
setMonitorVariable(L"PWORKAREAX", buffer);
|
|
_itow_s(workArea.top, buffer, 10);
|
|
setMonitorVariable(L"WORKAREAY", buffer);
|
|
setMonitorVariable(L"PWORKAREAY", buffer);
|
|
_itow_s(workArea.right - workArea.left, buffer, 10);
|
|
setMonitorVariable(L"WORKAREAWIDTH", buffer);
|
|
setMonitorVariable(L"PWORKAREAWIDTH", buffer);
|
|
_itow_s(workArea.bottom - workArea.top, buffer, 10);
|
|
setMonitorVariable(L"WORKAREAHEIGHT", buffer);
|
|
setMonitorVariable(L"PWORKAREAHEIGHT", buffer);
|
|
|
|
if (reset)
|
|
{
|
|
_itow_s(scrArea.left, buffer, 10);
|
|
setMonitorVariable(L"SCREENAREAX", buffer);
|
|
setMonitorVariable(L"PSCREENAREAX", buffer);
|
|
_itow_s(scrArea.top, buffer, 10);
|
|
setMonitorVariable(L"SCREENAREAY", buffer);
|
|
setMonitorVariable(L"PSCREENAREAY", buffer);
|
|
_itow_s(scrArea.right - scrArea.left, buffer, 10);
|
|
setMonitorVariable(L"SCREENAREAWIDTH", buffer);
|
|
setMonitorVariable(L"PSCREENAREAWIDTH", buffer);
|
|
_itow_s(scrArea.bottom - scrArea.top, buffer, 10);
|
|
setMonitorVariable(L"SCREENAREAHEIGHT", buffer);
|
|
setMonitorVariable(L"PSCREENAREAHEIGHT", buffer);
|
|
|
|
_itow_s(monitorsInfo.vsL, buffer, 10);
|
|
setMonitorVariable(L"VSCREENAREAX", buffer);
|
|
_itow_s(monitorsInfo.vsT, buffer, 10);
|
|
setMonitorVariable(L"VSCREENAREAY", buffer);
|
|
_itow_s(monitorsInfo.vsW, buffer, 10);
|
|
setMonitorVariable(L"VSCREENAREAWIDTH", buffer);
|
|
_itow_s(monitorsInfo.vsH, buffer, 10);
|
|
setMonitorVariable(L"VSCREENAREAHEIGHT", buffer);
|
|
}
|
|
|
|
int i = 1;
|
|
for (auto iter = monitors.cbegin(); iter != monitors.cend(); ++iter, ++i)
|
|
{
|
|
WCHAR buffer2[64];
|
|
|
|
const RECT work = ((*iter).active) ? (*iter).work : workArea;
|
|
|
|
_itow_s(work.left, buffer, 10);
|
|
_snwprintf_s(buffer2, _TRUNCATE, L"WORKAREAX@%i", i);
|
|
setMonitorVariable(buffer2, buffer);
|
|
_itow_s(work.top, buffer, 10);
|
|
_snwprintf_s(buffer2, _TRUNCATE, L"WORKAREAY@%i", i);
|
|
setMonitorVariable(buffer2, buffer);
|
|
_itow_s(work.right - work.left, buffer, 10);
|
|
_snwprintf_s(buffer2, _TRUNCATE, L"WORKAREAWIDTH@%i", i);
|
|
setMonitorVariable(buffer2, buffer);
|
|
_itow_s(work.bottom - work.top, buffer, 10);
|
|
_snwprintf_s(buffer2, _TRUNCATE, L"WORKAREAHEIGHT@%i", i);
|
|
setMonitorVariable(buffer2, buffer);
|
|
|
|
if (reset)
|
|
{
|
|
const RECT screen = ((*iter).active) ? (*iter).screen : scrArea;
|
|
|
|
_itow_s(screen.left, buffer, 10);
|
|
_snwprintf_s(buffer2, _TRUNCATE, L"SCREENAREAX@%i", i);
|
|
setMonitorVariable(buffer2, buffer);
|
|
_itow_s(screen.top, buffer, 10);
|
|
_snwprintf_s(buffer2, _TRUNCATE, L"SCREENAREAY@%i", i);
|
|
setMonitorVariable(buffer2, buffer);
|
|
_itow_s(screen.right - screen.left, buffer, 10);
|
|
_snwprintf_s(buffer2, _TRUNCATE, L"SCREENAREAWIDTH@%i", i);
|
|
setMonitorVariable(buffer2, buffer);
|
|
_itow_s(screen.bottom - screen.top, buffer, 10);
|
|
_snwprintf_s(buffer2, _TRUNCATE, L"SCREENAREAHEIGHT@%i", i);
|
|
setMonitorVariable(buffer2, buffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Sets new SCREENAREA/WORKAREA variables for present monitor.
|
|
**
|
|
*/
|
|
void ConfigParser::SetAutoSelectedMonitorVariables(MeterWindow* meterWindow)
|
|
{
|
|
if (meterWindow)
|
|
{
|
|
const int numOfMonitors = (int)System::GetMonitorCount();
|
|
const MultiMonitorInfo& monitorsInfo = System::GetMultiMonitorInfo();
|
|
const std::vector<MonitorInfo>& monitors = monitorsInfo.monitors;
|
|
|
|
WCHAR buffer[32];
|
|
int w1, w2, s1, s2;
|
|
int screenIndex;
|
|
|
|
// Set X / WIDTH
|
|
screenIndex = monitorsInfo.primary;
|
|
if (meterWindow->GetXScreenDefined())
|
|
{
|
|
int i = meterWindow->GetXScreen();
|
|
if (i >= 0 && (i == 0 || i <= numOfMonitors && monitors[i - 1].active))
|
|
{
|
|
screenIndex = i;
|
|
}
|
|
}
|
|
|
|
if (screenIndex == 0)
|
|
{
|
|
s1 = w1 = monitorsInfo.vsL;
|
|
s2 = w2 = monitorsInfo.vsW;
|
|
}
|
|
else
|
|
{
|
|
w1 = monitors[screenIndex - 1].work.left;
|
|
w2 = monitors[screenIndex - 1].work.right - monitors[screenIndex - 1].work.left;
|
|
s1 = monitors[screenIndex - 1].screen.left;
|
|
s2 = monitors[screenIndex - 1].screen.right - monitors[screenIndex - 1].screen.left;
|
|
}
|
|
|
|
_itow_s(w1, buffer, 10);
|
|
SetBuiltInVariable(L"WORKAREAX", buffer);
|
|
_itow_s(w2, buffer, 10);
|
|
SetBuiltInVariable(L"WORKAREAWIDTH", buffer);
|
|
_itow_s(s1, buffer, 10);
|
|
SetBuiltInVariable(L"SCREENAREAX", buffer);
|
|
_itow_s(s2, buffer, 10);
|
|
SetBuiltInVariable(L"SCREENAREAWIDTH", buffer);
|
|
|
|
// Set Y / HEIGHT
|
|
screenIndex = monitorsInfo.primary;
|
|
if (meterWindow->GetYScreenDefined())
|
|
{
|
|
int i = meterWindow->GetYScreen();
|
|
if (i >= 0 && (i == 0 || i <= numOfMonitors && monitors[i - 1].active))
|
|
{
|
|
screenIndex = i;
|
|
}
|
|
}
|
|
|
|
if (screenIndex == 0)
|
|
{
|
|
s1 = w1 = monitorsInfo.vsL;
|
|
s2 = w2 = monitorsInfo.vsW;
|
|
}
|
|
else
|
|
{
|
|
w1 = monitors[screenIndex - 1].work.top;
|
|
w2 = monitors[screenIndex - 1].work.bottom - monitors[screenIndex - 1].work.top;
|
|
s1 = monitors[screenIndex - 1].screen.top;
|
|
s2 = monitors[screenIndex - 1].screen.bottom - monitors[screenIndex - 1].screen.top;
|
|
}
|
|
|
|
_itow_s(w1, buffer, 10);
|
|
SetBuiltInVariable(L"WORKAREAY", buffer);
|
|
_itow_s(w2, buffer, 10);
|
|
SetBuiltInVariable(L"WORKAREAHEIGHT", buffer);
|
|
_itow_s(s1, buffer, 10);
|
|
SetBuiltInVariable(L"SCREENAREAY", buffer);
|
|
_itow_s(s2, buffer, 10);
|
|
SetBuiltInVariable(L"SCREENAREAHEIGHT", buffer);
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Replaces environment and internal variables in the given string.
|
|
**
|
|
*/
|
|
bool ConfigParser::ReplaceVariables(std::wstring& result)
|
|
{
|
|
bool replaced = false;
|
|
|
|
PathUtil::ExpandEnvironmentVariables(result);
|
|
|
|
if (c_MonitorVariables.empty())
|
|
{
|
|
SetMultiMonitorVariables(true);
|
|
}
|
|
|
|
// Check for variables (#VAR#)
|
|
size_t start = 0, end;
|
|
bool loop = true;
|
|
|
|
do
|
|
{
|
|
start = result.find(L'#', start);
|
|
if (start != std::wstring::npos)
|
|
{
|
|
size_t si = start + 1;
|
|
end = result.find(L'#', si);
|
|
if (end != std::wstring::npos)
|
|
{
|
|
size_t ei = end - 1;
|
|
if (si != ei && result[si] == L'*' && result[ei] == L'*')
|
|
{
|
|
result.erase(ei, 1);
|
|
result.erase(si, 1);
|
|
start = ei;
|
|
}
|
|
else
|
|
{
|
|
std::wstring strVariable = result.substr(si, end - si);
|
|
const std::wstring* value = GetVariable(strVariable);
|
|
if (value)
|
|
{
|
|
// Variable found, replace it with the value
|
|
result.replace(start, end - start + 1, *value);
|
|
start += (*value).length();
|
|
replaced = true;
|
|
}
|
|
else
|
|
{
|
|
start = end;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
loop = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
loop = false;
|
|
}
|
|
}
|
|
while (loop);
|
|
|
|
return replaced;
|
|
}
|
|
|
|
/*
|
|
** Replaces measures in the given string.
|
|
**
|
|
*/
|
|
bool ConfigParser::ReplaceMeasures(std::wstring& result)
|
|
{
|
|
bool replaced = false;
|
|
|
|
size_t start = 0;
|
|
while ((start = result.find(L'[', start)) != std::wstring::npos)
|
|
{
|
|
size_t si = start + 1;
|
|
size_t end = result.find(L']', si);
|
|
if (end == std::wstring::npos)
|
|
{
|
|
break;
|
|
}
|
|
|
|
size_t next = result.find(L'[', si);
|
|
if (next == std::wstring::npos || end < next)
|
|
{
|
|
size_t ei = end - 1;
|
|
if (si != ei && result[si] == L'*' && result[ei] == L'*')
|
|
{
|
|
result.erase(ei, 1);
|
|
result.erase(si, 1);
|
|
start = ei;
|
|
}
|
|
else
|
|
{
|
|
std::wstring var = result.substr(si, end - si);
|
|
|
|
Measure* measure = GetMeasure(var);
|
|
if (measure)
|
|
{
|
|
const WCHAR* value = measure->GetStringOrFormattedValue(AUTOSCALE_OFF, 1, -1, false);
|
|
size_t valueLen = wcslen(value);
|
|
|
|
// Measure found, replace it with the value
|
|
result.replace(start, end - start + 1, value, valueLen);
|
|
start += valueLen;
|
|
replaced = true;
|
|
}
|
|
else
|
|
{
|
|
std::wstring value;
|
|
if (GetSectionVariable(var, value))
|
|
{
|
|
// Replace section variable with the value.
|
|
result.replace(start, end - start + 1, value);
|
|
start += value.length();
|
|
replaced = true;
|
|
}
|
|
else
|
|
{
|
|
start = end;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
start = next;
|
|
}
|
|
}
|
|
|
|
return replaced;
|
|
}
|
|
|
|
const std::wstring& ConfigParser::ReadString(LPCTSTR section, LPCTSTR key, LPCTSTR defValue, bool bReplaceMeasures)
|
|
{
|
|
static std::wstring result;
|
|
|
|
// Clear last status
|
|
m_LastReplaced = false;
|
|
m_LastDefaultUsed = false;
|
|
m_LastValueDefined = false;
|
|
|
|
const std::wstring strSection = section;
|
|
const std::wstring strKey = key;
|
|
const std::wstring strDefault = defValue;
|
|
|
|
const std::wstring& strValue = GetValue(strSection, strKey, strDefault);
|
|
if (&strValue == &strDefault)
|
|
{
|
|
bool foundStyleValue = false;
|
|
|
|
// If the template is defined read the value from there.
|
|
std::vector<std::wstring>::const_reverse_iterator iter = m_StyleTemplate.rbegin();
|
|
for ( ; iter != m_StyleTemplate.rend(); ++iter)
|
|
{
|
|
const std::wstring& strStyleValue = GetValue((*iter), strKey, strDefault);
|
|
|
|
//LogDebugF(L"StyleTemplate: [%s] %s (from [%s]) : strDefault=%s (0x%p), strStyleValue=%s (0x%p)",
|
|
// section, key, (*iter).c_str(), strDefault.c_str(), &strDefault, strStyleValue.c_str(), &strStyleValue);
|
|
|
|
if (&strStyleValue != &strDefault)
|
|
{
|
|
result = strStyleValue;
|
|
foundStyleValue = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!foundStyleValue)
|
|
{
|
|
result = strDefault;
|
|
m_LastDefaultUsed = true;
|
|
return result;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = strValue;
|
|
}
|
|
|
|
if (!result.empty())
|
|
{
|
|
m_LastValueDefined = true;
|
|
|
|
if (result.size() >= 3)
|
|
{
|
|
if (result.find(L'#') != std::wstring::npos)
|
|
{
|
|
m_CurrentSection->assign(strSection); // Set temporarily
|
|
|
|
if (ReplaceVariables(result))
|
|
{
|
|
m_LastReplaced = true;
|
|
}
|
|
|
|
m_CurrentSection->clear(); // Reset
|
|
}
|
|
else
|
|
{
|
|
PathUtil::ExpandEnvironmentVariables(result);
|
|
}
|
|
|
|
if (bReplaceMeasures && ReplaceMeasures(result))
|
|
{
|
|
m_LastReplaced = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool ConfigParser::IsKeyDefined(LPCTSTR section, LPCTSTR key)
|
|
{
|
|
ReadString(section, key, L"", false);
|
|
return !m_LastDefaultUsed;
|
|
}
|
|
|
|
bool ConfigParser::IsValueDefined(LPCTSTR section, LPCTSTR key)
|
|
{
|
|
ReadString(section, key, L"", false);
|
|
return m_LastValueDefined;
|
|
}
|
|
|
|
void ConfigParser::AddMeasure(Measure* pMeasure)
|
|
{
|
|
if (pMeasure)
|
|
{
|
|
m_Measures[StrToUpper(pMeasure->GetOriginalName())] = pMeasure;
|
|
}
|
|
}
|
|
|
|
Measure* ConfigParser::GetMeasure(const std::wstring& name)
|
|
{
|
|
std::unordered_map<std::wstring, Measure*>::const_iterator iter = m_Measures.find(StrToUpper(name));
|
|
if (iter != m_Measures.end())
|
|
{
|
|
return (*iter).second;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
std::vector<Gdiplus::REAL> ConfigParser::ReadFloats(LPCTSTR section, LPCTSTR key)
|
|
{
|
|
std::vector<Gdiplus::REAL> result;
|
|
const std::wstring& str = ReadString(section, key, L"");
|
|
if (!str.empty())
|
|
{
|
|
// Tokenize and parse the floats
|
|
const WCHAR delimiter = L';';
|
|
size_t lastPos, pos = 0;
|
|
do
|
|
{
|
|
lastPos = str.find_first_not_of(delimiter, pos);
|
|
if (lastPos == std::wstring::npos) break;
|
|
|
|
pos = str.find_first_of(delimiter, lastPos + 1);
|
|
|
|
result.push_back((Gdiplus::REAL)ParseDouble(str.substr(lastPos, pos - lastPos).c_str(), 0.0)); // (pos != std::wstring::npos) ? pos - lastPos : pos
|
|
if (pos == std::wstring::npos) break;
|
|
|
|
++pos;
|
|
}
|
|
while (true);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int ConfigParser::ReadInt(LPCTSTR section, LPCTSTR key, int defValue)
|
|
{
|
|
const std::wstring& result = ReadString(section, key, L"");
|
|
|
|
if (!m_LastDefaultUsed)
|
|
{
|
|
const WCHAR* string = result.c_str();
|
|
if (*string == L'(')
|
|
{
|
|
double dblValue;
|
|
const WCHAR* errMsg = MathParser::CheckedParse(string, &dblValue);
|
|
if (!errMsg)
|
|
{
|
|
return (int)dblValue;
|
|
}
|
|
|
|
LogErrorF(m_MeterWindow, L"Formula: %s in key \"%s\" in [%s]", errMsg, key, section);
|
|
}
|
|
else if (*string)
|
|
{
|
|
errno = 0;
|
|
int intValue = wcstol(string, nullptr, 10);
|
|
if (errno != ERANGE)
|
|
{
|
|
return intValue;
|
|
}
|
|
}
|
|
}
|
|
|
|
return defValue;
|
|
}
|
|
|
|
uint32_t ConfigParser::ReadUInt(LPCTSTR section, LPCTSTR key, uint32_t defValue)
|
|
{
|
|
const std::wstring& result = ReadString(section, key, L"");
|
|
|
|
if (!m_LastDefaultUsed)
|
|
{
|
|
const WCHAR* string = result.c_str();
|
|
if (*string == L'(')
|
|
{
|
|
double dblValue;
|
|
const WCHAR* errMsg = MathParser::CheckedParse(string, &dblValue);
|
|
if (!errMsg)
|
|
{
|
|
return (uint32_t)dblValue;
|
|
}
|
|
|
|
LogErrorF(m_MeterWindow, L"Formula: %s in key \"%s\" in [%s]", errMsg, key, section);
|
|
}
|
|
else if (*string)
|
|
{
|
|
errno = 0;
|
|
uint32_t uintValue = wcstoul(string, nullptr, 10);
|
|
if (errno != ERANGE)
|
|
{
|
|
return uintValue;
|
|
}
|
|
}
|
|
}
|
|
|
|
return defValue;
|
|
}
|
|
|
|
uint64_t ConfigParser::ReadUInt64(LPCTSTR section, LPCTSTR key, uint64_t defValue)
|
|
{
|
|
const std::wstring& result = ReadString(section, key, L"");
|
|
|
|
if (!m_LastDefaultUsed)
|
|
{
|
|
const WCHAR* string = result.c_str();
|
|
if (*string == L'(')
|
|
{
|
|
double dblValue;
|
|
const WCHAR* errMsg = MathParser::CheckedParse(string, &dblValue);
|
|
if (!errMsg)
|
|
{
|
|
return (uint64_t)dblValue;
|
|
}
|
|
|
|
LogErrorF(m_MeterWindow, L"Formula: %s in key \"%s\" in [%s]", errMsg, key, section);
|
|
}
|
|
else if (*string)
|
|
{
|
|
errno = 0;
|
|
uint64_t uint64Value = _wcstoui64(string, nullptr, 10);
|
|
if (errno != ERANGE)
|
|
{
|
|
return uint64Value;
|
|
}
|
|
}
|
|
}
|
|
|
|
return defValue;
|
|
}
|
|
|
|
double ConfigParser::ReadFloat(LPCTSTR section, LPCTSTR key, double defValue)
|
|
{
|
|
const std::wstring& result = ReadString(section, key, L"");
|
|
|
|
if (!m_LastDefaultUsed)
|
|
{
|
|
double value;
|
|
const WCHAR* string = result.c_str();
|
|
if (*string == L'(')
|
|
{
|
|
const WCHAR* errMsg = MathParser::CheckedParse(string, &value);
|
|
if (!errMsg)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
LogErrorF(m_MeterWindow, L"Formula: %s in key \"%s\" in [%s]", errMsg, key, section);
|
|
}
|
|
else if (*string)
|
|
{
|
|
errno = 0;
|
|
value = wcstod(string, nullptr);
|
|
if (errno != ERANGE)
|
|
{
|
|
return value;
|
|
}
|
|
}
|
|
}
|
|
|
|
return defValue;
|
|
}
|
|
|
|
// Returns true if the formula was read successfully, false for failure.
|
|
bool ConfigParser::ParseFormula(const std::wstring& formula, double* resultValue)
|
|
{
|
|
// Formulas must be surrounded by parenthesis
|
|
if (!formula.empty() && formula[0] == L'(' && formula[formula.size() - 1] == L')')
|
|
{
|
|
const WCHAR* string = formula.c_str();
|
|
const WCHAR* errMsg = MathParser::CheckedParse(string, resultValue);
|
|
if (errMsg != nullptr)
|
|
{
|
|
LogErrorF(m_MeterWindow, L"Formula: %s: %s", errMsg, string);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
ARGB ConfigParser::ReadColor(LPCTSTR section, LPCTSTR key, ARGB defValue)
|
|
{
|
|
const std::wstring& result = ReadString(section, key, L"");
|
|
|
|
return (m_LastDefaultUsed) ? defValue : ParseColor(result.c_str());
|
|
}
|
|
|
|
Rect ConfigParser::ReadRect(LPCTSTR section, LPCTSTR key, const Rect& defValue)
|
|
{
|
|
const std::wstring& result = ReadString(section, key, L"");
|
|
|
|
return (m_LastDefaultUsed) ? defValue : ParseRect(result.c_str());
|
|
}
|
|
|
|
RECT ConfigParser::ReadRECT(LPCTSTR section, LPCTSTR key, const RECT& defValue)
|
|
{
|
|
const std::wstring& result = ReadString(section, key, L"");
|
|
|
|
RECT r;
|
|
if (m_LastDefaultUsed)
|
|
{
|
|
r = defValue;
|
|
}
|
|
else
|
|
{
|
|
r = ParseRECT(result.c_str());
|
|
}
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
** Splits the string from the delimiters.
|
|
** Now trims empty element in vector and white-space in each string.
|
|
**
|
|
** Modified from http://www.digitalpeer.com/id/simple
|
|
*/
|
|
std::vector<std::wstring> ConfigParser::Tokenize(const std::wstring& str, const std::wstring& delimiters)
|
|
{
|
|
std::vector<std::wstring> tokens;
|
|
|
|
size_t lastPos, pos = 0;
|
|
do
|
|
{
|
|
lastPos = str.find_first_not_of(delimiters, pos);
|
|
if (lastPos == std::wstring::npos) break;
|
|
|
|
pos = str.find_first_of(delimiters, lastPos + 1);
|
|
std::wstring token = str.substr(lastPos, pos - lastPos); // len = (pos != std::wstring::npos) ? pos - lastPos : pos
|
|
|
|
size_t pos2 = token.find_first_not_of(L" \t\r\n");
|
|
if (pos2 != std::wstring::npos)
|
|
{
|
|
size_t lastPos2 = token.find_last_not_of(L" \t\r\n");
|
|
if (pos2 != 0 || lastPos2 != (token.size() - 1))
|
|
{
|
|
// Trim white-space
|
|
token.assign(token, pos2, lastPos2 - pos2 + 1);
|
|
}
|
|
tokens.push_back(token);
|
|
}
|
|
|
|
if (pos == std::wstring::npos) break;
|
|
++pos;
|
|
}
|
|
while (true);
|
|
|
|
return tokens;
|
|
}
|
|
|
|
/*
|
|
** Helper method that parses the floating-point value from the given string.
|
|
** If the given string is invalid format or causes overflow/underflow, returns given default value.
|
|
**
|
|
*/
|
|
double ConfigParser::ParseDouble(LPCTSTR string, double defValue)
|
|
{
|
|
assert(string);
|
|
|
|
double value;
|
|
if (*string == L'(')
|
|
{
|
|
const WCHAR* errMsg = MathParser::CheckedParse(string, &value);
|
|
if (!errMsg)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
LogErrorF(L"Formula: %s: %s", errMsg, string);
|
|
}
|
|
else if (*string)
|
|
{
|
|
errno = 0;
|
|
double value = wcstod(string, nullptr);
|
|
if (errno != ERANGE)
|
|
{
|
|
return value;
|
|
}
|
|
}
|
|
|
|
return defValue;
|
|
}
|
|
|
|
/*
|
|
** Helper method that parses the integer value from the given string.
|
|
** If the given string is invalid format or causes overflow/underflow, returns given default value.
|
|
**
|
|
*/
|
|
int ConfigParser::ParseInt(LPCTSTR string, int defValue)
|
|
{
|
|
assert(string);
|
|
|
|
if (*string == L'(')
|
|
{
|
|
double dblValue;
|
|
const WCHAR* errMsg = MathParser::CheckedParse(string, &dblValue);
|
|
if (!errMsg)
|
|
{
|
|
return (int)dblValue;
|
|
}
|
|
|
|
LogErrorF(L"Formula: %s: %s", errMsg, string);
|
|
}
|
|
else if (*string)
|
|
{
|
|
errno = 0;
|
|
int intValue = wcstol(string, nullptr, 10);
|
|
if (errno != ERANGE)
|
|
{
|
|
return intValue;
|
|
}
|
|
}
|
|
|
|
return defValue;
|
|
}
|
|
|
|
/*
|
|
** Helper method that parses the unsigned integer value from the given string.
|
|
** If the given string is invalid format or causes overflow/underflow, returns given default value.
|
|
**
|
|
*/
|
|
uint32_t ConfigParser::ParseUInt(LPCTSTR string, uint32_t defValue)
|
|
{
|
|
assert(string);
|
|
|
|
if (*string == L'(')
|
|
{
|
|
double dblValue;
|
|
const WCHAR* errMsg = MathParser::CheckedParse(string, &dblValue);
|
|
if (!errMsg)
|
|
{
|
|
return (uint32_t)dblValue;
|
|
}
|
|
|
|
LogErrorF(L"Formula: %s: %s", errMsg, string);
|
|
}
|
|
else if (*string)
|
|
{
|
|
errno = 0;
|
|
uint32_t uintValue = wcstoul(string, nullptr, 10);
|
|
if (errno != ERANGE)
|
|
{
|
|
return uintValue;
|
|
}
|
|
}
|
|
|
|
return defValue;
|
|
}
|
|
|
|
/*
|
|
** Helper method that parses the 64bit unsigned integer value from the given string.
|
|
** If the given string is invalid format or causes overflow/underflow, returns given default value.
|
|
**
|
|
*/
|
|
uint64_t ConfigParser::ParseUInt64(LPCTSTR string, uint64_t defValue)
|
|
{
|
|
assert(string);
|
|
|
|
if (*string == L'(')
|
|
{
|
|
double dblValue;
|
|
const WCHAR* errMsg = MathParser::CheckedParse(string, &dblValue);
|
|
if (!errMsg)
|
|
{
|
|
return (uint64_t)dblValue;
|
|
}
|
|
|
|
LogErrorF(L"Formula: %s: %s", errMsg, string);
|
|
}
|
|
else if (*string)
|
|
{
|
|
errno = 0;
|
|
uint64_t uint64Value = _wcstoui64(string, nullptr, 10);
|
|
if (errno != ERANGE)
|
|
{
|
|
return uint64Value;
|
|
}
|
|
}
|
|
|
|
return defValue;
|
|
}
|
|
|
|
/*
|
|
** Helper template that parses four comma separated values from the given string.
|
|
**
|
|
*/
|
|
template <typename T>
|
|
bool ParseInt4(LPCTSTR string, T& v1, T& v2, T& v3, T& v4)
|
|
{
|
|
if (wcschr(string, L','))
|
|
{
|
|
WCHAR* parseSz = _wcsdup(string);
|
|
WCHAR* token;
|
|
|
|
token = wcstok(parseSz, L",");
|
|
if (token)
|
|
{
|
|
v1 = ConfigParser::ParseInt(token, 0);
|
|
|
|
token = wcstok(nullptr, L",");
|
|
if (token)
|
|
{
|
|
v2 = ConfigParser::ParseInt(token, 0);
|
|
|
|
token = wcstok(nullptr, L",");
|
|
if (token)
|
|
{
|
|
v3 = ConfigParser::ParseInt(token, 0);
|
|
|
|
token = wcstok(nullptr, L",");
|
|
if (token)
|
|
{
|
|
v4 = ConfigParser::ParseInt(token, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
free(parseSz);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
** Helper method that parses the color values from the given string.
|
|
** The color can be supplied as three/four comma separated values or as one
|
|
** hex-value.
|
|
**
|
|
*/
|
|
ARGB ConfigParser::ParseColor(LPCTSTR string)
|
|
{
|
|
int R = 255, G = 255, B = 255, A = 255;
|
|
|
|
if (!ParseInt4(string, R, G, B, A))
|
|
{
|
|
if (wcsncmp(string, L"0x", 2) == 0)
|
|
{
|
|
string += 2; // skip prefix
|
|
}
|
|
|
|
size_t len = wcslen(string);
|
|
if (len >= 8 && !iswspace(string[6]))
|
|
{
|
|
swscanf(string, L"%02x%02x%02x%02x", &R, &G, &B, &A);
|
|
}
|
|
else if (len >= 6)
|
|
{
|
|
swscanf(string, L"%02x%02x%02x", &R, &G, &B);
|
|
}
|
|
}
|
|
|
|
return Color::MakeARGB(A, R, G, B);
|
|
}
|
|
|
|
/*
|
|
** Helper method that parses the Gdiplus::Rect values from the given string.
|
|
** The rect can be supplied as four comma separated values (X/Y/Width/Height).
|
|
**
|
|
*/
|
|
Rect ConfigParser::ParseRect(LPCTSTR string)
|
|
{
|
|
Rect r;
|
|
ParseInt4(string, r.X, r.Y, r.Width, r.Height);
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
** Helper method that parses the RECT values from the given string.
|
|
** The rect can be supplied as four comma separated values (left/top/right/bottom).
|
|
**
|
|
*/
|
|
RECT ConfigParser::ParseRECT(LPCTSTR string)
|
|
{
|
|
RECT r = {0};
|
|
ParseInt4(string, r.left, r.top, r.right, r.bottom);
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
** Reads the given ini file and fills the m_Values and m_Keys maps.
|
|
**
|
|
*/
|
|
void ConfigParser::ReadIniFile(const std::wstring& iniFile, LPCTSTR skinSection, int depth)
|
|
{
|
|
if (depth > 100) // Is 100 enough to assume the include loop never ends?
|
|
{
|
|
GetRainmeter().ShowMessage(nullptr, GetString(ID_STR_INCLUDEINFINITELOOP), MB_OK | MB_ICONERROR);
|
|
return;
|
|
}
|
|
|
|
// Verify whether the file exists
|
|
if (_waccess(iniFile.c_str(), 0) == -1)
|
|
{
|
|
LogErrorF(m_MeterWindow, L"Unable to read file: %s", iniFile.c_str());
|
|
return;
|
|
}
|
|
|
|
// Avoid "IniFileMapping"
|
|
std::wstring iniRead = System::GetTemporaryFile(iniFile);
|
|
bool temporary = (!iniRead.empty() && (iniRead.size() != 1 || iniRead[0] != L'?'));
|
|
|
|
if (temporary)
|
|
{
|
|
if (GetRainmeter().GetDebug()) LogDebugF(m_MeterWindow, L"Reading file: %s (Temp: %s)", iniFile.c_str(), iniRead.c_str());
|
|
}
|
|
else
|
|
{
|
|
if (GetRainmeter().GetDebug()) LogDebugF(m_MeterWindow, L"Reading file: %s", iniFile.c_str());
|
|
iniRead = iniFile;
|
|
}
|
|
|
|
// Get all the sections (i.e. different meters)
|
|
std::list<std::wstring> sections;
|
|
std::unordered_set<std::wstring> unique;
|
|
std::wstring key, value; // buffer
|
|
|
|
DWORD itemsSize = MAX_LINE_LENGTH;
|
|
WCHAR* items = new WCHAR[itemsSize];
|
|
WCHAR* pos = nullptr;
|
|
WCHAR* epos = nullptr;
|
|
|
|
if (skinSection == nullptr)
|
|
{
|
|
// Get all the sections
|
|
do
|
|
{
|
|
items[0] = 0;
|
|
DWORD res = GetPrivateProfileSectionNames(items, itemsSize, iniRead.c_str());
|
|
if (res == 0) // File not found
|
|
{
|
|
delete [] items;
|
|
if (temporary) System::RemoveFile(iniRead);
|
|
return;
|
|
}
|
|
if (res < itemsSize - 2) // Fits in the buffer
|
|
{
|
|
epos = items + res;
|
|
break;
|
|
}
|
|
|
|
delete [] items;
|
|
itemsSize *= 2;
|
|
items = new WCHAR[itemsSize];
|
|
}
|
|
while (true);
|
|
|
|
// Read the sections
|
|
pos = items;
|
|
while (pos < epos)
|
|
{
|
|
if (*pos)
|
|
{
|
|
value = pos; // section name
|
|
StrToUpperC(key.assign(value));
|
|
if (unique.insert(key).second)
|
|
{
|
|
if (m_FoundSections.insert(key).second)
|
|
{
|
|
m_Sections.insert(m_SectionInsertPos, value);
|
|
}
|
|
sections.push_back(value);
|
|
}
|
|
pos += value.size() + 1;
|
|
}
|
|
else // Empty string
|
|
{
|
|
++pos;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Special case: Read only "Rainmeter" and specified section from "Rainmeter.ini"
|
|
const std::wstring strRainmeter = L"Rainmeter";
|
|
const std::wstring strFolder = skinSection;
|
|
|
|
sections.push_back(strRainmeter);
|
|
sections.push_back(strFolder);
|
|
|
|
if (depth == 0) // Add once
|
|
{
|
|
m_Sections.push_back(strRainmeter);
|
|
m_Sections.push_back(strFolder);
|
|
}
|
|
}
|
|
|
|
// Read the keys and values
|
|
for (auto it = sections.cbegin(); it != sections.cend(); ++it)
|
|
{
|
|
unique.clear();
|
|
|
|
const WCHAR* sectionName = (*it).c_str();
|
|
bool isVariables = (_wcsicmp(sectionName, L"Variables") == 0);
|
|
bool isMetadata = (skinSection == nullptr && !isVariables && _wcsicmp(sectionName, L"Metadata") == 0);
|
|
bool resetInsertPos = true;
|
|
|
|
// Read all "key=value" from the section
|
|
do
|
|
{
|
|
items[0] = 0;
|
|
DWORD res = GetPrivateProfileSection(sectionName, items, itemsSize, iniRead.c_str());
|
|
if (res < itemsSize - 2) // Fits in the buffer
|
|
{
|
|
epos = items + res;
|
|
break;
|
|
}
|
|
|
|
delete [] items;
|
|
itemsSize *= 2;
|
|
items = new WCHAR[itemsSize];
|
|
}
|
|
while (true);
|
|
|
|
pos = items;
|
|
while (pos < epos)
|
|
{
|
|
if (*pos)
|
|
{
|
|
size_t len = wcslen(pos);
|
|
WCHAR* sep = wmemchr(pos, L'=', len);
|
|
if (sep != nullptr && sep != pos)
|
|
{
|
|
size_t clen = sep - pos; // key's length
|
|
|
|
StrToUpperC(key.assign(pos, clen));
|
|
if (unique.insert(key).second)
|
|
{
|
|
++sep;
|
|
clen = len - (clen + 1); // value's length
|
|
|
|
// Trim surrounded quotes from value
|
|
if (clen >= 2 && (sep[0] == L'"' || sep[0] == L'\'') && sep[clen - 1] == sep[0])
|
|
{
|
|
clen -= 2;
|
|
++sep;
|
|
}
|
|
|
|
if (wcsncmp(key.c_str(), L"@INCLUDE", 8) == 0)
|
|
{
|
|
if (clen > 0)
|
|
{
|
|
value.assign(sep, clen);
|
|
ReadVariables();
|
|
ReplaceVariables(value);
|
|
if (!PathUtil::IsAbsolute(value))
|
|
{
|
|
// Relative to the ini folder
|
|
value.insert(0, PathUtil::GetFolderFromFilePath(iniFile));
|
|
}
|
|
|
|
if (resetInsertPos)
|
|
{
|
|
auto jt = it;
|
|
if (++jt == sections.end()) // Special case: @include was used in the last section of the current file
|
|
{
|
|
// Set the insertion place to the last
|
|
m_SectionInsertPos = m_Sections.end();
|
|
resetInsertPos = false;
|
|
}
|
|
else
|
|
{
|
|
// Find the appropriate insertion place
|
|
for (jt = m_Sections.cbegin(); jt != m_Sections.cend(); ++jt)
|
|
{
|
|
if (_wcsicmp((*jt).c_str(), sectionName) == 0)
|
|
{
|
|
m_SectionInsertPos = ++jt;
|
|
resetInsertPos = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ReadIniFile(value, skinSection, depth + 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!isMetadata) // Uncache Metadata's key-value pair in the skin
|
|
{
|
|
value.assign(sep, clen);
|
|
SetValue((*it), key, value);
|
|
|
|
if (isVariables)
|
|
{
|
|
m_ListVariables.push_back(key);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
pos += len + 1;
|
|
}
|
|
else // Empty string
|
|
{
|
|
++pos;
|
|
}
|
|
}
|
|
}
|
|
|
|
delete [] items;
|
|
if (temporary) System::RemoveFile(iniRead);
|
|
}
|
|
|
|
/*
|
|
** Sets the value for the key under the given section.
|
|
**
|
|
*/
|
|
void ConfigParser::SetValue(const std::wstring& strSection, const std::wstring& strKey, const std::wstring& strValue)
|
|
{
|
|
// LogDebugF(L"[%s] %s=%s (size: %i)", strSection.c_str(), strKey.c_str(), strValue.c_str(), (int)m_Values.size());
|
|
|
|
std::wstring strTmp;
|
|
strTmp.reserve(strSection.size() + 1 + strKey.size());
|
|
strTmp = strSection;
|
|
strTmp += L'~';
|
|
strTmp += strKey;
|
|
|
|
m_Values[StrToUpperC(strTmp)] = strValue;
|
|
}
|
|
|
|
/*
|
|
** Deletes the value for the key under the given section.
|
|
**
|
|
*/
|
|
void ConfigParser::DeleteValue(const std::wstring& strSection, const std::wstring& strKey)
|
|
{
|
|
std::wstring strTmp;
|
|
strTmp.reserve(strSection.size() + 1 + strKey.size());
|
|
strTmp = strSection;
|
|
strTmp += L'~';
|
|
strTmp += strKey;
|
|
|
|
std::unordered_map<std::wstring, std::wstring>::const_iterator iter = m_Values.find(StrToUpperC(strTmp));
|
|
if (iter != m_Values.end())
|
|
{
|
|
m_Values.erase(iter);
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Returns the value for the key under the given section.
|
|
**
|
|
*/
|
|
const std::wstring& ConfigParser::GetValue(const std::wstring& strSection, const std::wstring& strKey, const std::wstring& strDefault)
|
|
{
|
|
std::wstring strTmp;
|
|
strTmp.reserve(strSection.size() + 1 + strKey.size());
|
|
strTmp = strSection;
|
|
strTmp += L'~';
|
|
strTmp += strKey;
|
|
|
|
std::unordered_map<std::wstring, std::wstring>::const_iterator iter = m_Values.find(StrToUpperC(strTmp));
|
|
return (iter != m_Values.end()) ? (*iter).second : strDefault;
|
|
}
|