Merge branch 'global-user-directory'

Please read https://wiki.dolphin-emu.org/index.php?title=Controlling_the_Global_User_Directory
for documentation about this feature.

Thanks to neobrain and RachelB for their work on this change, and thanks to
everyone who helped test it.
This commit is contained in:
Pierre Bourdon
2013-09-14 19:24:27 +02:00
1640 changed files with 899 additions and 872 deletions

View File

@ -40,18 +40,13 @@
#define SYSDATA_DIR "Sys"
#elif defined __APPLE__
#define SYSDATA_DIR "Contents/Resources/Sys"
#define SHARED_USER_DIR File::GetBundleDirectory() + \
DIR_SEP USERDATA_DIR DIR_SEP
#elif defined ANDROID
#define SYSDATA_DIR "/sdcard/dolphin-emu"
#define SHARED_USER_DIR SYSDATA_DIR
#else
#ifdef DATA_DIR
#define SYSDATA_DIR DATA_DIR "sys"
#define SHARED_USER_DIR DATA_DIR USERDATA_DIR DIR_SEP
#else
#define SYSDATA_DIR "sys"
#define SHARED_USER_DIR ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP
#endif
#endif
@ -64,7 +59,7 @@
#define GC_USER_DIR "GC"
#define WII_USER_DIR "Wii"
#define CONFIG_DIR "Config"
#define GAMECONFIG_DIR "GameConfig"
#define GAMESETTINGS_DIR "GameSettings"
#define MAPS_DIR "Maps"
#define CACHE_DIR "Cache"
#define SHADERCACHE_DIR "ShaderCache"

View File

@ -551,11 +551,24 @@ bool DeleteDirRecursively(const std::string &directory)
// Create directory and copy contents (does not overwrite existing files)
void CopyDir(const std::string &source_path, const std::string &dest_path)
{
#ifndef _WIN32
if (source_path == dest_path) return;
if (!File::Exists(source_path)) return;
if (!File::Exists(dest_path)) File::CreateFullPath(dest_path);
#ifdef _WIN32
WIN32_FIND_DATA ffd;
HANDLE hFind = FindFirstFile(UTF8ToTStr(source_path + "\\*").c_str(), &ffd);
if (hFind == INVALID_HANDLE_VALUE)
{
FindClose(hFind);
return;
}
do
{
const std::string virtualName(TStrToUTF8(ffd.cFileName));
#else
struct dirent dirent, *result = NULL;
DIR *dirp = opendir(source_path.c_str());
if (!dirp) return;
@ -563,10 +576,9 @@ void CopyDir(const std::string &source_path, const std::string &dest_path)
while (!readdir_r(dirp, &dirent, &result) && result)
{
const std::string virtualName(result->d_name);
#endif
// check for "." and ".."
if (((virtualName[0] == '.') && (virtualName[1] == '\0')) ||
((virtualName[0] == '.') && (virtualName[1] == '.') &&
(virtualName[2] == '\0')))
if (virtualName == "." || virtualName == "..")
continue;
std::string source, dest;
@ -580,6 +592,10 @@ void CopyDir(const std::string &source_path, const std::string &dest_path)
CopyDir(source, dest);
}
else if (!File::Exists(dest)) File::Copy(source, dest);
#ifdef _WIN32
} while (FindNextFile(hFind, &ffd) != 0);
FindClose(hFind);
#else
}
closedir(dirp);
#endif
@ -643,9 +659,9 @@ std::string GetSysDirectory()
std::string sysDir;
#if defined (__APPLE__)
sysDir = GetBundleDirectory();
sysDir += DIR_SEP;
sysDir += SYSDATA_DIR;
sysDir = GetBundleDirectory() + DIR_SEP + SYSDATA_DIR;
#elif defined (_WIN32)
sysDir = GetExeDirectory() + DIR_SEP + SYSDATA_DIR;
#else
sysDir = SYSDATA_DIR;
#endif
@ -665,7 +681,52 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new
if (paths[D_USER_IDX].empty())
{
#ifdef _WIN32
paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
// Detect where the User directory is. There are four different cases (on top of the
// command line flag, which overrides all this):
// 1. HKCU\Software\Dolphin Emulator\LocalUserConfig exists and is true
// -> Use GetExeDirectory()\User
// 2. HKCU\Software\Dolphin Emulator\UserConfigPath exists
// -> Use this as the user directory path
// 3. My Documents exists
// -> Use My Documents\Dolphin Emulator as the User directory path
// 4. Default
// -> Use GetExeDirectory()\User
// Check our registry keys
HKEY hkey;
DWORD local = 0;
TCHAR configPath[MAX_PATH] = {0};
if (RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Software\\Dolphin Emulator"), NULL, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS)
{
DWORD size = 4;
if (RegQueryValueEx(hkey, TEXT("LocalUserConfig"), NULL, NULL, reinterpret_cast<LPBYTE>(&local), &size) != ERROR_SUCCESS)
local = 0;
size = MAX_PATH;
if (RegQueryValueEx(hkey, TEXT("UserConfigPath"), NULL, NULL, (LPBYTE)configPath, &size) != ERROR_SUCCESS)
configPath[0] = 0;
RegCloseKey(hkey);
}
// Get Program Files path in case we need it.
TCHAR my_documents[MAX_PATH];
bool my_documents_found = SUCCEEDED(SHGetFolderPath(NULL, CSIDL_MYDOCUMENTS, NULL, SHGFP_TYPE_CURRENT, my_documents));
if (local) // Case 1
paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
else if (configPath[0]) // Case 2
paths[D_USER_IDX] = TStrToUTF8(configPath);
else if (my_documents_found) // Case 3
paths[D_USER_IDX] = TStrToUTF8(my_documents) + DIR_SEP "Dolphin Emulator" DIR_SEP;
else // Case 4
paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
// Prettify the path: it will be displayed in some places, we don't want a mix of \ and /.
paths[D_USER_IDX] = ReplaceAll(paths[D_USER_IDX], "\\", DIR_SEP);
// Make sure it ends in DIR_SEP.
if (*paths[D_USER_IDX].rbegin() != DIR_SEP_CHR)
paths[D_USER_IDX] += DIR_SEP;
#else
if (File::Exists(ROOT_DIR DIR_SEP USERDATA_DIR))
paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
@ -679,7 +740,7 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new
paths[D_WIIROOT_IDX] = paths[D_USER_IDX] + WII_USER_DIR;
paths[D_WIIUSER_IDX] = paths[D_WIIROOT_IDX] + DIR_SEP;
paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
paths[D_GAMECONFIG_IDX] = paths[D_USER_IDX] + GAMECONFIG_DIR DIR_SEP;
paths[D_GAMESETTINGS_IDX] = paths[D_USER_IDX] + GAMESETTINGS_DIR DIR_SEP;
paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
@ -730,37 +791,37 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new
break;
case D_USER_IDX:
paths[D_GCUSER_IDX] = paths[D_USER_IDX] + GC_USER_DIR DIR_SEP;
paths[D_WIIROOT_IDX] = paths[D_USER_IDX] + WII_USER_DIR;
paths[D_WIIUSER_IDX] = paths[D_WIIROOT_IDX] + DIR_SEP;
paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
paths[D_GAMECONFIG_IDX] = paths[D_USER_IDX] + GAMECONFIG_DIR DIR_SEP;
paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP;
paths[D_SCREENSHOTS_IDX] = paths[D_USER_IDX] + SCREENSHOTS_DIR DIR_SEP;
paths[D_OPENCL_IDX] = paths[D_USER_IDX] + OPENCL_DIR DIR_SEP;
paths[D_HIRESTEXTURES_IDX] = paths[D_USER_IDX] + HIRES_TEXTURES_DIR DIR_SEP;
paths[D_DUMP_IDX] = paths[D_USER_IDX] + DUMP_DIR DIR_SEP;
paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP;
paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP;
paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP;
paths[D_DUMPDSP_IDX] = paths[D_DUMP_IDX] + DUMP_DSP_DIR DIR_SEP;
paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOGS_DIR DIR_SEP;
paths[D_MAILLOGS_IDX] = paths[D_LOGS_IDX] + MAIL_LOGS_DIR DIR_SEP;
paths[D_WIISYSCONF_IDX] = paths[D_WIIUSER_IDX] + WII_SYSCONF_DIR DIR_SEP;
paths[D_THEMES_IDX] = paths[D_USER_IDX] + THEMES_DIR DIR_SEP;
paths[F_DOLPHINCONFIG_IDX] = paths[D_CONFIG_IDX] + DOLPHIN_CONFIG;
paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG;
paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG;
paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG;
paths[F_WIISYSCONF_IDX] = paths[D_WIISYSCONF_IDX] + WII_SYSCONF;
paths[F_RAMDUMP_IDX] = paths[D_DUMP_IDX] + RAM_DUMP;
paths[F_ARAMDUMP_IDX] = paths[D_DUMP_IDX] + ARAM_DUMP;
paths[F_FAKEVMEMDUMP_IDX] = paths[D_DUMP_IDX] + FAKEVMEM_DUMP;
paths[F_GCSRAM_IDX] = paths[D_GCUSER_IDX] + GC_SRAM;
paths[D_GCUSER_IDX] = paths[D_USER_IDX] + GC_USER_DIR DIR_SEP;
paths[D_WIIROOT_IDX] = paths[D_USER_IDX] + WII_USER_DIR;
paths[D_WIIUSER_IDX] = paths[D_WIIROOT_IDX] + DIR_SEP;
paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
paths[D_GAMESETTINGS_IDX] = paths[D_USER_IDX] + GAMESETTINGS_DIR DIR_SEP;
paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP;
paths[D_SCREENSHOTS_IDX] = paths[D_USER_IDX] + SCREENSHOTS_DIR DIR_SEP;
paths[D_OPENCL_IDX] = paths[D_USER_IDX] + OPENCL_DIR DIR_SEP;
paths[D_HIRESTEXTURES_IDX] = paths[D_USER_IDX] + HIRES_TEXTURES_DIR DIR_SEP;
paths[D_DUMP_IDX] = paths[D_USER_IDX] + DUMP_DIR DIR_SEP;
paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP;
paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP;
paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP;
paths[D_DUMPDSP_IDX] = paths[D_DUMP_IDX] + DUMP_DSP_DIR DIR_SEP;
paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOGS_DIR DIR_SEP;
paths[D_MAILLOGS_IDX] = paths[D_LOGS_IDX] + MAIL_LOGS_DIR DIR_SEP;
paths[D_WIISYSCONF_IDX] = paths[D_WIIUSER_IDX] + WII_SYSCONF_DIR DIR_SEP;
paths[D_THEMES_IDX] = paths[D_USER_IDX] + THEMES_DIR DIR_SEP;
paths[F_DOLPHINCONFIG_IDX] = paths[D_CONFIG_IDX] + DOLPHIN_CONFIG;
paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG;
paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG;
paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG;
paths[F_WIISYSCONF_IDX] = paths[D_WIISYSCONF_IDX] + WII_SYSCONF;
paths[F_RAMDUMP_IDX] = paths[D_DUMP_IDX] + RAM_DUMP;
paths[F_ARAMDUMP_IDX] = paths[D_DUMP_IDX] + ARAM_DUMP;
paths[F_FAKEVMEMDUMP_IDX] = paths[D_DUMP_IDX] + FAKEVMEM_DUMP;
paths[F_GCSRAM_IDX] = paths[D_GCUSER_IDX] + GC_SRAM;
break;
case D_CONFIG_IDX:
@ -800,12 +861,10 @@ std::string GetThemeDir(const std::string& theme_name)
{
std::string dir = File::GetUserPath(D_THEMES_IDX) + theme_name + "/";
#if !defined(_WIN32)
// If theme does not exist in user's dir load from shared directory
if (!File::Exists(dir))
dir = SHARED_USER_DIR THEMES_DIR "/" + theme_name + "/";
#endif
dir = GetSysDirectory() + THEMES_DIR "/" + theme_name + "/";
return dir;
}

View File

@ -21,8 +21,8 @@ enum {
D_GCUSER_IDX,
D_WIIROOT_IDX,
D_WIIUSER_IDX,
D_CONFIG_IDX,
D_GAMECONFIG_IDX,
D_CONFIG_IDX, // global settings
D_GAMESETTINGS_IDX, // user-specified settings which override both the global and the default settings (per game)
D_MAPS_IDX,
D_CACHE_IDX,
D_SHADERCACHE_IDX,

View File

@ -20,7 +20,7 @@
namespace {
static void ParseLine(const std::string& line, std::string* keyOut, std::string* valueOut)
void ParseLine(const std::string& line, std::string* keyOut, std::string* valueOut)
{
if (line[0] == '#')
return;
@ -37,32 +37,15 @@ static void ParseLine(const std::string& line, std::string* keyOut, std::string*
}
std::string* IniFile::Section::GetLine(const char* key, std::string* valueOut)
{
for (std::vector<std::string>::iterator iter = lines.begin(); iter != lines.end(); ++iter)
{
std::string& line = *iter;
std::string lineKey;
ParseLine(line, &lineKey, valueOut);
if (!strcasecmp(lineKey.c_str(), key))
return &line;
}
return 0;
}
void IniFile::Section::Set(const char* key, const char* newValue)
{
std::string value;
std::string* line = GetLine(key, &value);
if (line)
{
// Change the value - keep the key and comment
*line = StripSpaces(key) + " = " + newValue;
}
auto it = values.find(key);
if (it != values.end())
it->second = newValue;
else
{
// The key did not already exist in this section - let's add it.
lines.push_back(std::string(key) + " = " + newValue);
values[key] = newValue;
keys_order.push_back(key);
}
}
@ -74,20 +57,6 @@ void IniFile::Section::Set(const char* key, const std::string& newValue, const s
Delete(key);
}
bool IniFile::Section::Get(const char* key, std::string* value, const char* defaultValue)
{
std::string* line = GetLine(key, value);
if (!line)
{
if (defaultValue)
{
*value = defaultValue;
}
return false;
}
return true;
}
void IniFile::Section::Set(const char* key, const float newValue, const float defaultValue)
{
if (newValue != defaultValue)
@ -126,7 +95,24 @@ void IniFile::Section::Set(const char* key, const std::vector<std::string>& newV
Set(key, temp.c_str());
}
bool IniFile::Section::Get(const char* key, std::vector<std::string>& values)
bool IniFile::Section::Get(const char* key, std::string* value, const char* defaultValue)
{
auto it = values.find(key);
if (it != values.end())
{
*value = it->second;
return true;
}
else if (defaultValue)
{
*value = defaultValue;
return true;
}
else
return false;
}
bool IniFile::Section::Get(const char* key, std::vector<std::string>& out)
{
std::string temp;
bool retval = Get(key, &temp, 0);
@ -145,7 +131,7 @@ bool IniFile::Section::Get(const char* key, std::vector<std::string>& values)
subEnd = temp.find_first_of(",", subStart);
if (subStart != subEnd)
// take from first char until next ,
values.push_back(StripSpaces(temp.substr(subStart, subEnd - subStart)));
out.push_back(StripSpaces(temp.substr(subStart, subEnd - subStart)));
// Find the next non , char
subStart = temp.find_first_not_of(",", subEnd);
@ -206,28 +192,18 @@ bool IniFile::Section::Get(const char* key, double* value, double defaultValue)
bool IniFile::Section::Exists(const char *key) const
{
for (std::vector<std::string>::const_iterator iter = lines.begin(); iter != lines.end(); ++iter)
{
std::string lineKey;
ParseLine(*iter, &lineKey, NULL);
if (!strcasecmp(lineKey.c_str(), key))
return true;
}
return false;
return values.find(key) != values.end();
}
bool IniFile::Section::Delete(const char *key)
{
std::string* line = GetLine(key, 0);
for (std::vector<std::string>::iterator liter = lines.begin(); liter != lines.end(); ++liter)
{
if (line == &*liter)
{
lines.erase(liter);
return true;
}
}
return false;
auto it = values.find(key);
if (it == values.end())
return false;
values.erase(it);
keys_order.erase(std::find(keys_order.begin(), keys_order.end(), key));
return true;
}
// IniFile
@ -286,11 +262,7 @@ bool IniFile::Exists(const char* sectionName, const char* key) const
void IniFile::SetLines(const char* sectionName, const std::vector<std::string> &lines)
{
Section* section = GetOrCreateSection(sectionName);
section->lines.clear();
for (std::vector<std::string>::const_iterator iter = lines.begin(); iter != lines.end(); ++iter)
{
section->lines.push_back(*iter);
}
section->lines = lines;
}
bool IniFile::DeleteKey(const char* sectionName, const char* key)
@ -298,16 +270,7 @@ bool IniFile::DeleteKey(const char* sectionName, const char* key)
Section* section = GetSection(sectionName);
if (!section)
return false;
std::string* line = section->GetLine(key, 0);
for (std::vector<std::string>::iterator liter = section->lines.begin(); liter != section->lines.end(); ++liter)
{
if (line == &(*liter))
{
section->lines.erase(liter);
return true;
}
}
return false; //shouldn't happen
return section->Delete(key);
}
// Return a list of all keys in a section
@ -316,13 +279,7 @@ bool IniFile::GetKeys(const char* sectionName, std::vector<std::string>& keys) c
const Section* section = GetSection(sectionName);
if (!section)
return false;
keys.clear();
for (std::vector<std::string>::const_iterator liter = section->lines.begin(); liter != section->lines.end(); ++liter)
{
std::string key;
ParseLine(*liter, &key, 0);
keys.push_back(key);
}
keys = section->keys_order;
return true;
}
@ -364,13 +321,13 @@ void IniFile::SortSections()
std::sort(sections.begin(), sections.end());
}
bool IniFile::Load(const char* filename)
bool IniFile::Load(const char* filename, bool keep_current_data)
{
// Maximum number of letters in a line
static const int MAX_BYTES = 1024*32;
sections.clear();
sections.push_back(Section(""));
if (!keep_current_data)
sections.clear();
// first section consists of the comments before the first real section
// Open file
@ -379,12 +336,13 @@ bool IniFile::Load(const char* filename)
if (in.fail()) return false;
Section* current_section = NULL;
while (!in.eof())
{
char templine[MAX_BYTES];
in.getline(templine, MAX_BYTES);
std::string line = templine;
#ifndef _WIN32
// Check for CRLF eol and convert it to LF
if (!line.empty() && line.at(line.size()-1) == '\r')
@ -405,12 +363,25 @@ bool IniFile::Load(const char* filename)
{
// New section!
std::string sub = line.substr(1, endpos - 1);
sections.push_back(Section(sub));
current_section = GetOrCreateSection(sub.c_str());
}
}
else
{
sections[sections.size() - 1].lines.push_back(line);
if (current_section)
{
std::string key, value;
ParseLine(line, &key, &value);
// Lines starting with '$' or '+' are kept verbatim. Kind
// of a hack, but the support for raw lines inside an INI
// is a hack anyway.
if ((key == "" && value == "")
|| (line.size() >= 1 && (line[0] == '$' || line[0] == '+')))
current_section->lines.push_back(line.c_str());
else
current_section->Set(key, value.c_str());
}
}
}
}
@ -429,27 +400,33 @@ bool IniFile::Save(const char* filename)
return false;
}
// Currently testing if dolphin community can handle the requirements of C++11 compilation
// If you get a compiler error on this line, your compiler is probably old.
// Update to g++ 4.4 or a recent version of clang (XCode 4.2 on OS X).
// If you don't want to update, complain in a google code issue, the dolphin forums or #dolphin-emu.
for (auto iter = sections.begin(); iter != sections.end(); ++iter)
{
const Section& section = *iter;
if (section.name != "")
{
if (section.keys_order.size() != 0 || section.lines.size() != 0)
out << "[" << section.name << "]" << std::endl;
}
for (std::vector<std::string>::const_iterator liter = section.lines.begin(); liter != section.lines.end(); ++liter)
if (section.keys_order.size() == 0)
{
std::string s = *liter;
out << s << std::endl;
for (auto liter = section.lines.begin(); liter != section.lines.end(); ++liter)
{
std::string s = *liter;
out << s << std::endl;
}
}
else
{
for (auto kvit = section.keys_order.begin(); kvit != section.keys_order.end(); ++kvit)
{
auto pair = section.values.find(*kvit);
out << pair->first << " = " << pair->second << std::endl;
}
}
}
out.close();
return true;
}

View File

@ -6,11 +6,21 @@
#ifndef _INIFILE_H_
#define _INIFILE_H_
#include <map>
#include <string>
#include <set>
#include <vector>
#include "StringUtil.h"
struct CaseInsensitiveStringCompare
{
bool operator() (const std::string& a, const std::string& b) const
{
return strcasecmp(a.c_str(), b.c_str()) < 0;
}
};
class IniFile
{
public:
@ -25,7 +35,6 @@ public:
bool Exists(const char *key) const;
bool Delete(const char *key);
std::string* GetLine(const char* key, std::string* valueOut);
void Set(const char* key, const char* newValue);
void Set(const char* key, const std::string& newValue, const std::string& defaultValue);
@ -68,12 +77,24 @@ public:
}
protected:
std::vector<std::string> lines;
std::string name;
std::vector<std::string> keys_order;
std::map<std::string, std::string, CaseInsensitiveStringCompare> values;
std::vector<std::string> lines;
};
bool Load(const char* filename);
bool Load(const std::string &filename) { return Load(filename.c_str()); }
/**
* Loads sections and keys.
* @param filename filename of the ini file which should be loaded
* @param keep_current_data If true, "extends" the currently loaded list of sections and keys with the loaded data (and replaces existing entries). If false, existing data will be erased.
* @warning Using any other operations than "Get*" and "Exists" is untested and will behave unexpectedly
* @todo This really is just a hack to support having two levels of gameinis (defaults and user-specified) and should eventually be replaced with a less stupid system.
*/
bool Load(const char* filename, bool keep_current_data = false);
bool Load(const std::string &filename, bool keep_current_data = false) { return Load(filename.c_str(), keep_current_data); }
bool Save(const char* filename);
bool Save(const std::string &filename) { return Save(filename.c_str()); }