1
0
Fork 0

BRIDGE+GUI: new settings provider (should fix the issue of the settings resetting). Also: commit 1500, YAY!

This commit is contained in:
Mr. eXoDia 2015-07-14 10:32:32 +02:00
parent f55e4bd020
commit 2035bfdf54
8 changed files with 413 additions and 3728 deletions

326
x64_dbg_bridge/Utf8Ini.h Normal file
View File

@ -0,0 +1,326 @@
#ifndef _UTF8INI_H
#define _UTF8INI_H
#include <string>
#include <map>
#include <vector>
class Utf8Ini
{
public:
/**
\brief Serialize to the INI file format.
*/
inline std::string Serialize() const
{
std::string output;
for(const auto & section : _sections)
{
if(output.length())
appendLine(output, "");
appendLine(output, makeSectionText(section.first));
for(const auto & keyvalue : section.second)
if(keyvalue.first.length())
appendLine(output, makeKeyValueText(keyvalue.first, keyvalue.second));
}
return output;
}
/**
\brief Deserialize from the INI file format.
\param data The INI data.
\param [out] errorLine The error line (only has a meaning when this function failed).
\return true if it succeeds, false if it fails.
*/
inline bool Deserialize(const std::string & data, int & errorLine)
{
//initialize
errorLine = 0;
Clear();
//read lines
std::vector<std::string> lines;
std::string curLine;
for(auto ch : data)
{
switch(ch)
{
case '\r':
break;
case '\n':
lines.push_back(trim(curLine));
curLine.clear();
break;
default:
curLine += ch;
}
}
if(curLine.length())
lines.push_back(trim(curLine));
//parse lines
std::string section = "";
for(const auto & line : lines)
{
errorLine++;
switch(getLineType(line))
{
case LineType::Invalid:
MessageBoxA(0, line.c_str(), "line", 0);
return false;
case LineType::Comment:
case LineType::Empty:
continue;
case LineType::KeyValue:
{
std::string key;
std::string value;
if(!section.length() ||
!parseKeyValueLine(line, key, value) ||
!SetValue(section, key, value))
return false;
}
break;
case LineType::Section:
if(!parseSectionLine(line, section))
return false;
}
}
return true;
}
/**
\brief Sets a value. This will overwrite older values.
\param section The section. Must have a length greater than one.
\param key The key. Must have a length greater than one.
\param value The value. Can be empty (effectively removing the value).
\return true if the value was set successfully, false otherwise.
*/
inline bool SetValue(const std::string & section, const std::string & key, const std::string & value)
{
auto trimmedSection = trim(section);
auto trimmedKey = trim(key);
if(!trimmedSection.length() || !trimmedKey.length())
return false;
auto found = _sections.find(trimmedSection);
if(found != _sections.end())
found->second[trimmedKey] = value;
else
{
KeyValueMap keyValueMap;
keyValueMap[trimmedKey] = value;
_sections[trimmedSection] = keyValueMap;
}
return true;
}
/**
\brief Removes all key/value pairs from a section.
\param section The section to clear.
\return true if it succeeds, false otherwise.
*/
inline bool ClearSection(const std::string & section)
{
auto trimmedSection = trim(section);
if(!trimmedSection.length())
return false;
auto found = _sections.find(trimmedSection);
if(found == _sections.end())
return false;
_sections.erase(found);
return true;
}
/**
\brief Removes all sections.
*/
inline void Clear()
{
_sections.clear();
}
/**
\brief Gets a value.
\param section The section.
\param key The key.
\param [in,out] value The value.
\return The value. Empty string when the value was not found or empty.
*/
inline std::string GetValue(const std::string & section, const std::string & key) const
{
auto trimmedSection = trim(section);
auto trimmedKey = trim(key);
if(!trimmedSection.length() || !trimmedKey.length())
return "";
auto sectionFound = _sections.find(trimmedSection);
if(sectionFound == _sections.end())
return "";
const auto & keyValueMap = sectionFound->second;
auto keyFound = keyValueMap.find(trimmedKey);
if(keyFound == keyValueMap.end())
return "";
return keyFound->second;
}
private:
typedef std::map<std::string, std::string> KeyValueMap;
std::map<std::string, KeyValueMap> _sections;
enum class LineType
{
Invalid,
Empty,
Section,
KeyValue,
Comment
};
static inline LineType getLineType(const std::string & line)
{
auto len = line.length();
if(!len)
return LineType::Empty;
if(line[0] == '[' && line[len - 1] == ']')
return LineType::Section;
if(line[0] == ';')
return LineType::Comment;
if(line.find('=') != std::string::npos)
return LineType::KeyValue;
return LineType::Invalid;
}
static inline std::string trim(const std::string & str)
{
auto len = str.length();
if(!len)
return "";
size_t pre = 0;
while(str[pre] == ' ')
pre++;
size_t post = 0;
while(str[len - post - 1] == ' ' && post < len)
post++;
auto sublen = len - post - pre;
return sublen > 0 ? str.substr(pre, len - post - pre) : "";
}
static inline bool parseKeyValueLine(const std::string & line, std::string & key, std::string & value)
{
auto pos = line.find('=');
key = trim(line.substr(0, pos));
value = trim(line.substr(pos + 1));
auto len = value.length();
if(len && value[0] == '\"' && value[len - 1] == '\"')
value = unescapeValue(value);
return true;
}
static inline bool parseSectionLine(const std::string & line, std::string & section)
{
section = trim(line.substr(1, line.length() - 2));
return section.length() > 0;
}
static inline void appendLine(std::string & output, const std::string & line)
{
if(output.length())
output += "\r\n";
output += line;
}
static inline std::string makeSectionText(const std::string & section)
{
return "[" + section + "]";
}
static inline std::string makeKeyValueText(const std::string & key, const std::string & value)
{
return key + "=" + escapeValue(value);
}
static inline bool needsEscaping(const std::string & value)
{
auto len = value.length();
return len && (value[0] == ' ' || value[len - 1] == ' ' ||
value.find('\n') != std::string::npos ||
value.find('\"') != std::string::npos);
}
static inline std::string escapeValue(const std::string & value)
{
if(!needsEscaping(value))
return value;
std::string escaped = "\"";
for(auto ch : value)
{
switch(ch)
{
case '\"':
escaped += "\\\"";
break;
case '\\':
escaped += "\\\\";
break;
case '\r':
escaped += "\\r";
break;
case '\n':
escaped += "\\n";
break;
case '\t':
escaped += "\\t";
default:
escaped += ch;
}
}
return escaped + "\"";
}
static inline std::string unescapeValue(const std::string & str)
{
std::string result;
bool bEscaped = false;
for(auto ch : str)
{
if(!bEscaped)
{
switch(ch)
{
case '\"':
break;
case '\\':
bEscaped = true;
break;
default:
result += ch;
}
}
else
{
switch(ch)
{
case 'r':
result += '\r';
break;
case 'n':
result += '\n';
break;
case 't':
result += '\t';
break;
default:
result += ch;
}
bEscaped = false;
}
}
if(bEscaped)
result += '\\';
return result;
}
};
#endif //_UTF8INI_H

View File

@ -7,10 +7,10 @@
#include "_global.h"
#include "bridgemain.h"
#include <stdio.h>
#include "simpleini.h"
#include "Utf8Ini.h"
static HINSTANCE hInst;
static Utf8Ini settings;
static wchar_t szIniFile[MAX_PATH] = L"";
static CRITICAL_SECTION csIni;
@ -90,7 +90,19 @@ BRIDGE_IMPEXP const char* BridgeStart()
{
if(!_dbg_dbginit || !_gui_guiinit)
return "\"_dbg_dbginit\" || \"_gui_guiinit\" was not loaded yet, call BridgeInit!";
int errorLine = 0;
if(!BridgeSettingRead(&errorLine))
{
static char error[256] = "";
if(!errorLine)
strcpy_s(error, "Failed to read settings file from disk...");
else
sprintf_s(error, "Error in settings file on line %d...", errorLine);
return error;
}
_gui_guiinit(0, 0); //remove arguments
if(!BridgeSettingFlush())
return "Failed to save settings!";
DeleteCriticalSection(&csIni);
return 0;
}
@ -117,19 +129,11 @@ BRIDGE_IMPEXP bool BridgeSettingGet(const char* section, const char* key, char*
if(!section || !key || !value)
return false;
EnterCriticalSection(&csIni);
CSimpleIniA inifile(true, false, false);
bool success = false;
if(inifile.LoadFile(szIniFile) >= 0)
{
const char* szValue = inifile.GetValue(section, key);
if(szValue)
{
strcpy_s(value, MAX_SETTING_SIZE, szValue);
success = true;
}
}
auto foundValue = settings.GetValue(section, key);
strcpy_s(value, MAX_SETTING_SIZE, settings.GetValue(section, key).c_str());
bool result = foundValue.length() > 0;
LeaveCriticalSection(&csIni);
return success;
return result;
}
BRIDGE_IMPEXP bool BridgeSettingGetUint(const char* section, const char* key, duint* value)
@ -155,13 +159,12 @@ BRIDGE_IMPEXP bool BridgeSettingSet(const char* section, const char* key, const
if(section)
{
EnterCriticalSection(&csIni);
CSimpleIniA inifile(true, false, false);
inifile.LoadFile(szIniFile);
if(!key || !value) //delete value/key when 0
inifile.Delete(section, key, true);
if(!key)
success = settings.ClearSection(section);
else if(!value)
success = settings.SetValue(section, key, "");
else
inifile.SetValue(section, key, value);
success = inifile.SaveFile(szIniFile, false) >= 0;
success = settings.SetValue(section, key, value);
LeaveCriticalSection(&csIni);
}
return success;
@ -180,6 +183,63 @@ BRIDGE_IMPEXP bool BridgeSettingSetUint(const char* section, const char* key, du
return BridgeSettingSet(section, key, newvalue);
}
BRIDGE_IMPEXP bool BridgeSettingFlush()
{
EnterCriticalSection(&csIni);
std::string iniData = settings.Serialize();
LeaveCriticalSection(&csIni);
bool success = false;
HANDLE hFile = CreateFileW(szIniFile, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if(hFile != INVALID_HANDLE_VALUE)
{
SetEndOfFile(hFile);
DWORD written = 0;
if(WriteFile(hFile, iniData.c_str(), (DWORD)iniData.length(), &written, nullptr))
success = true;
CloseHandle(hFile);
}
return success;
}
BRIDGE_IMPEXP bool BridgeSettingRead(int* errorLine)
{
if(errorLine)
*errorLine = 0;
bool success = false;
std::string iniData;
HANDLE hFile = CreateFileW(szIniFile, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
if(hFile != INVALID_HANDLE_VALUE)
{
DWORD fileSize = GetFileSize(hFile, nullptr);
if(fileSize)
{
unsigned char utf8bom[] = { 0xEF, 0xBB, 0xBF };
char* fileData = (char*)BridgeAlloc(sizeof(utf8bom) + fileSize + 1);
DWORD read = 0;
if(ReadFile(hFile, fileData, fileSize, &read, nullptr))
{
success = true;
if(!memcmp(fileData, utf8bom, sizeof(utf8bom)))
iniData.assign(fileData + sizeof(utf8bom));
else
iniData.assign(fileData);
}
BridgeFree(fileData);
}
CloseHandle(hFile);
}
if(success) //if we failed to read the file, the current settings are better than none at all
{
EnterCriticalSection(&csIni);
int errline = 0;
success = settings.Deserialize(iniData, errline);
if(errorLine)
*errorLine = errline;
LeaveCriticalSection(&csIni);
}
return success;
}
BRIDGE_IMPEXP int BridgeGetDbgVersion()
{
return DBG_VERSION;
@ -1144,4 +1204,4 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
hInst = hinstDLL;
return TRUE;
}
}

View File

@ -48,6 +48,8 @@ BRIDGE_IMPEXP bool BridgeSettingGet(const char* section, const char* key, char*
BRIDGE_IMPEXP bool BridgeSettingGetUint(const char* section, const char* key, duint* value);
BRIDGE_IMPEXP bool BridgeSettingSet(const char* section, const char* key, const char* value);
BRIDGE_IMPEXP bool BridgeSettingSetUint(const char* section, const char* key, duint value);
BRIDGE_IMPEXP bool BridgeSettingFlush();
BRIDGE_IMPEXP bool BridgeSettingRead(int* errorLine);
BRIDGE_IMPEXP int BridgeGetDbgVersion();
//Debugger defines

File diff suppressed because it is too large Load Diff

View File

@ -24,7 +24,7 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="bridgemain.h" />
<ClInclude Include="simpleini.h" />
<ClInclude Include="Utf8Ini.h" />
<ClInclude Include="_global.h" />
</ItemGroup>
<PropertyGroup Label="Globals">

View File

@ -25,7 +25,7 @@
<ClInclude Include="bridgemain.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="simpleini.h">
<ClInclude Include="Utf8Ini.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>

View File

@ -326,6 +326,7 @@ void AppearanceDialog::on_buttonSave_clicked()
Config()->writeColors();
Config()->writeFonts();
GuiUpdateAllViews();
BridgeSettingFlush();
GuiAddStatusBarMessage("Settings saved!\n");
}

View File

@ -297,6 +297,7 @@ void SettingsDialog::SaveSettings()
if(settings.miscSymbolCache)
BridgeSettingSet("Symbols", "CachePath", ui->editSymbolCache->text().toUtf8().constData());
BridgeSettingFlush();
Config()->load();
DbgSettingsUpdated();
GuiUpdateAllViews();