Because ConfigFile exposed the entire class to plugins, anything that changes the class breaks ABI, and the last ConfigFile commit did just that. So this adds an additional hack to allow additional internal variables without breaking ABI. It changes the lpFileData in to a variable that creates another internal variable structure as a rather terrible hack for adding new internal variables. So, what's the lesson? Never expose anything you don't need to expose unless you need maximum performance and its structure size will never change.
1199 lines
33 KiB
C++
1199 lines
33 KiB
C++
/********************************************************************************
|
|
Copyright (C) 2001-2012 Hugh Bailey <obs.jim@gmail.com>
|
|
|
|
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
********************************************************************************/
|
|
|
|
|
|
#include "XT.h"
|
|
|
|
|
|
|
|
|
|
|
|
//WARNING: LEAVE THIS FILE NOW OR YOU WILL BE FOREVER SCARRED BY THE HORRENDOUS CODE I WROTE 4000 YEARS AGO
|
|
//
|
|
// ...yes, I really need to rewrite this file, I know.
|
|
|
|
struct InternalUselessStuff
|
|
{
|
|
TSTR lpActualFileData;
|
|
HANDLE hHorribleThreadSafetyMutex;
|
|
|
|
inline InternalUselessStuff() {hHorribleThreadSafetyMutex = OSCreateMutex();}
|
|
inline ~InternalUselessStuff() {OSCloseMutex(hHorribleThreadSafetyMutex);}
|
|
};
|
|
|
|
#define InternalStuff reinterpret_cast<InternalUselessStuff*>(lpFileData)
|
|
|
|
|
|
/*=========================================================
|
|
Config
|
|
===========================================================*/
|
|
|
|
BOOL ConfigFile::Create(CTSTR lpConfigFile)
|
|
{
|
|
strFileName = lpConfigFile;
|
|
|
|
if (!lpFileData)
|
|
lpFileData = (TSTR)new InternalUselessStuff;
|
|
|
|
if(LoadFile(XFILE_CREATEALWAYS))
|
|
LoadData();
|
|
else
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
BOOL ConfigFile::Open(CTSTR lpConfigFile, BOOL bOpenAlways)
|
|
{
|
|
strFileName = lpConfigFile;
|
|
|
|
if (!lpFileData)
|
|
lpFileData = (TSTR)new InternalUselessStuff;
|
|
|
|
if (LoadFile(bOpenAlways ? XFILE_OPENALWAYS : XFILE_OPENEXISTING))
|
|
LoadData();
|
|
else
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
#define hHorribleThreadSafetyMutex reinterpret_cast<InternalUselessStuff*>(lpFileData)->hHorribleThreadSafetyMutex
|
|
#define lpActualFileData reinterpret_cast<InternalUselessStuff*>(lpFileData)->lpActualFileData
|
|
|
|
BOOL ConfigFile::LoadFile(DWORD dwOpenMode)
|
|
{
|
|
XFile file;
|
|
if(!file.Open(strFileName, XFILE_READ, dwOpenMode))
|
|
{
|
|
//Log(TEXT("Couldn't load config file: \"%s\""), (TSTR)strFileName);
|
|
return 0;
|
|
}
|
|
|
|
OSEnterMutex(hHorribleThreadSafetyMutex);
|
|
|
|
if(bOpen)
|
|
Close();
|
|
|
|
dwLength = (DWORD)file.GetFileSize();
|
|
|
|
if (dwLength >= 3) // remove BOM if present
|
|
{
|
|
char buff[3];
|
|
file.Read(&buff, 3);
|
|
if (memcmp(buff, "\xEF\xBB\xBF", 3))
|
|
file.SetPos(0, XFILE_BEGIN);
|
|
else
|
|
dwLength -= 3;
|
|
}
|
|
|
|
LPSTR lpTempFileData = (LPSTR)Allocate(dwLength+5);
|
|
file.Read(&lpTempFileData[2], dwLength);
|
|
lpTempFileData[0] = lpTempFileData[dwLength+2] = 13;
|
|
lpTempFileData[1] = lpTempFileData[dwLength+3] = 10;
|
|
lpTempFileData[dwLength+4] = 0;
|
|
file.Close();
|
|
|
|
lpActualFileData = utf8_createTstr(lpTempFileData);
|
|
dwLength = slen(lpActualFileData);
|
|
Free(lpTempFileData);
|
|
|
|
bOpen = 1;
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
|
|
return 1;
|
|
}
|
|
|
|
void ConfigFile::LoadData()
|
|
{
|
|
OSEnterMutex(hHorribleThreadSafetyMutex);
|
|
TSTR lpCurLine = lpActualFileData, lpNextLine;
|
|
ConfigSection *lpCurSection=NULL;
|
|
DWORD i;
|
|
|
|
lpNextLine = schr(lpCurLine, '\r');
|
|
|
|
while(*(lpCurLine = (lpNextLine+2)))
|
|
{
|
|
lpNextLine = schr(lpCurLine, '\r');
|
|
if (!lpNextLine)
|
|
CrashError(TEXT("Your %s file is corrupt, please delete it and re-launch OBS."), strFileName.Array());
|
|
*lpNextLine = 0;
|
|
|
|
if((*lpCurLine == '[') && (*(lpNextLine-1) == ']'))
|
|
{
|
|
lpCurSection = Sections.CreateNew();
|
|
|
|
lpCurSection->name = sfix(sdup(lpCurLine+1));
|
|
lpCurSection->name[lpNextLine-lpCurLine-2] = 0;
|
|
}
|
|
else if(lpCurSection && *lpCurLine && (*(LPWORD)lpCurLine != '//'))
|
|
{
|
|
TSTR lpValuePtr = schr(lpCurLine, '=');
|
|
if (!lpValuePtr)
|
|
CrashError(TEXT("Your %s file is corrupt, please delete it and re-launch OBS."), strFileName.Array());
|
|
|
|
if(lpValuePtr[1] != 0)
|
|
{
|
|
ConfigKey *key=NULL;
|
|
|
|
*lpValuePtr = 0;
|
|
|
|
for(i=0; i<lpCurSection->Keys.Num(); i++)
|
|
{
|
|
if(scmpi(lpCurLine, lpCurSection->Keys[i].name) == 0)
|
|
{
|
|
key = &lpCurSection->Keys[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!key)
|
|
{
|
|
key = lpCurSection->Keys.CreateNew();
|
|
key->name = sfix(sdup(lpCurLine));
|
|
}
|
|
|
|
*lpValuePtr = '=';
|
|
|
|
lpCurLine = lpValuePtr+1;
|
|
|
|
TSTR value = sfix(sdup(lpCurLine));
|
|
key->ValueList << value;
|
|
}
|
|
}
|
|
|
|
*lpNextLine = '\r';
|
|
}
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
}
|
|
|
|
void ConfigFile::Close()
|
|
{
|
|
if (!bOpen)
|
|
return;
|
|
|
|
OSEnterMutex(hHorribleThreadSafetyMutex);
|
|
DWORD i,j,k;
|
|
|
|
for(i=0; i<Sections.Num(); i++)
|
|
{
|
|
ConfigSection §ion = Sections[i];
|
|
Free(section.name);
|
|
|
|
for(j=0; j<section.Keys.Num(); j++)
|
|
{
|
|
ConfigKey &key = section.Keys[j];
|
|
Free(key.name);
|
|
|
|
for(k=0; k<key.ValueList.Num(); k++)
|
|
Free(key.ValueList[k]);
|
|
|
|
key.ValueList.Clear();
|
|
}
|
|
section.Keys.Clear();
|
|
}
|
|
Sections.Clear();
|
|
|
|
if(lpActualFileData)
|
|
{
|
|
Free(lpActualFileData);
|
|
lpActualFileData = NULL;
|
|
}
|
|
|
|
bOpen = 0;
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
|
|
if (lpFileData)
|
|
{
|
|
delete reinterpret_cast<InternalUselessStuff*>(lpFileData);
|
|
lpFileData = NULL;
|
|
}
|
|
}
|
|
|
|
BOOL ConfigFile::SaveAs(CTSTR lpPath)
|
|
{
|
|
OSEnterMutex(hHorribleThreadSafetyMutex);
|
|
XFile newFile;
|
|
|
|
String tmpPath = lpPath;
|
|
tmpPath.AppendString(TEXT(".tmp"));
|
|
|
|
if (newFile.Open(tmpPath, XFILE_WRITE, XFILE_CREATEALWAYS))
|
|
{
|
|
if (newFile.Write("\xEF\xBB\xBF", 3) != 3)
|
|
{
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!newFile.WriteAsUTF8(lpActualFileData))
|
|
{
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return FALSE;
|
|
}
|
|
|
|
newFile.Close();
|
|
if (!OSRenameFile(tmpPath, lpPath))
|
|
Log(TEXT("ConfigFile::SaveAs: Unable to move new config file %s to %s"), tmpPath.Array(), lpPath);
|
|
|
|
strFileName = lpPath;
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return TRUE;
|
|
}
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void ConfigFile::SetFilePath(CTSTR lpPath)
|
|
{
|
|
strFileName = lpPath;
|
|
}
|
|
|
|
String ConfigFile::GetString(CTSTR lpSection, CTSTR lpKey, CTSTR def)
|
|
{
|
|
OSEnterMutex(hHorribleThreadSafetyMutex);
|
|
assert(lpSection);
|
|
assert(lpKey);
|
|
|
|
DWORD i,j;
|
|
|
|
for(i=0; i<Sections.Num(); i++)
|
|
{
|
|
ConfigSection §ion = Sections[i];
|
|
if(scmpi(lpSection, section.name) == 0)
|
|
{
|
|
for(j=0; j<section.Keys.Num(); j++)
|
|
{
|
|
ConfigKey &key = section.Keys[j];
|
|
if(scmpi(lpKey, key.name) == 0)
|
|
{
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return String(key.ValueList[0]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
|
|
if(def)
|
|
return String(def);
|
|
else
|
|
return String();
|
|
}
|
|
|
|
CTSTR ConfigFile::GetStringPtr(CTSTR lpSection, CTSTR lpKey, CTSTR def)
|
|
{
|
|
OSEnterMutex(hHorribleThreadSafetyMutex);
|
|
assert(lpSection);
|
|
assert(lpKey);
|
|
|
|
DWORD i,j;
|
|
|
|
for(i=0; i<Sections.Num(); i++)
|
|
{
|
|
ConfigSection §ion = Sections[i];
|
|
if(scmpi(lpSection, section.name) == 0)
|
|
{
|
|
for(j=0; j<section.Keys.Num(); j++)
|
|
{
|
|
ConfigKey &key = section.Keys[j];
|
|
if(scmpi(lpKey, key.name) == 0)
|
|
{
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return key.ValueList[0];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
|
|
if(def)
|
|
return def;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
int ConfigFile::GetInt(CTSTR lpSection, CTSTR lpKey, int def)
|
|
{
|
|
OSEnterMutex(hHorribleThreadSafetyMutex);
|
|
assert(lpSection);
|
|
assert(lpKey);
|
|
|
|
DWORD i,j;
|
|
|
|
for(i=0; i<Sections.Num(); i++)
|
|
{
|
|
ConfigSection §ion = Sections[i];
|
|
if(scmpi(lpSection, section.name) == 0)
|
|
{
|
|
for(j=0; j<section.Keys.Num(); j++)
|
|
{
|
|
ConfigKey &key = section.Keys[j];
|
|
if(scmpi(lpKey, key.name) == 0)
|
|
{
|
|
if(scmpi(key.ValueList[0], TEXT("true")) == 0)
|
|
{
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return 1;
|
|
}
|
|
else if(scmpi(key.ValueList[0], TEXT("false")) == 0)
|
|
{
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
if(ValidIntString(key.ValueList[0]))
|
|
{
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return tstring_base_to_int(key.ValueList[0], NULL, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return def;
|
|
}
|
|
|
|
DWORD ConfigFile::GetHex(CTSTR lpSection, CTSTR lpKey, DWORD def)
|
|
{
|
|
OSEnterMutex(hHorribleThreadSafetyMutex);
|
|
assert(lpSection);
|
|
assert(lpKey);
|
|
|
|
DWORD i,j;
|
|
|
|
for(i=0; i<Sections.Num(); i++)
|
|
{
|
|
ConfigSection §ion = Sections[i];
|
|
if(scmpi(lpSection, section.name) == 0)
|
|
{
|
|
for(j=0; j<section.Keys.Num(); j++)
|
|
{
|
|
ConfigKey &key = section.Keys[j];
|
|
if(scmpi(lpKey, key.name) == 0)
|
|
{
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return tstring_base_to_int(key.ValueList[0], NULL, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return def;
|
|
}
|
|
|
|
float ConfigFile::GetFloat(CTSTR lpSection, CTSTR lpKey, float def)
|
|
{
|
|
OSEnterMutex(hHorribleThreadSafetyMutex);
|
|
assert(lpSection);
|
|
assert(lpKey);
|
|
|
|
DWORD i,j;
|
|
|
|
for(i=0; i<Sections.Num(); i++)
|
|
{
|
|
ConfigSection §ion = Sections[i];
|
|
if(scmpi(lpSection, section.name) == 0)
|
|
{
|
|
for(j=0; j<section.Keys.Num(); j++)
|
|
{
|
|
ConfigKey &key = section.Keys[j];
|
|
if(scmpi(lpKey, key.name) == 0)
|
|
{
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return (float)tstof(key.ValueList[0]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return def;
|
|
}
|
|
|
|
Color4 ConfigFile::GetColor(CTSTR lpSection, CTSTR lpKey)
|
|
{
|
|
OSEnterMutex(hHorribleThreadSafetyMutex);
|
|
assert(lpSection);
|
|
assert(lpKey);
|
|
|
|
DWORD i,j;
|
|
|
|
for(i=0; i<Sections.Num(); i++)
|
|
{
|
|
ConfigSection §ion = Sections[i];
|
|
if(scmpi(lpSection, section.name) == 0)
|
|
{
|
|
for(j=0; j<section.Keys.Num(); j++)
|
|
{
|
|
ConfigKey &key = section.Keys[j];
|
|
if(scmpi(lpKey, key.name) == 0)
|
|
{
|
|
TSTR strValue = key.ValueList[0];
|
|
if(*strValue == '{')
|
|
{
|
|
Color4 ret;
|
|
|
|
ret.x = float(tstof(++strValue));
|
|
|
|
if(!(strValue = schr(strValue, ',')))
|
|
break;
|
|
ret.y = float(tstof(++strValue));
|
|
|
|
if(!(strValue = schr(strValue, ',')))
|
|
break;
|
|
ret.z = float(tstof(++strValue));
|
|
|
|
if(!(strValue = schr(strValue, ',')))
|
|
{
|
|
ret.w = 1.0f;
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return ret;
|
|
}
|
|
ret.w = float(tstof(++strValue));
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return ret;
|
|
}
|
|
else if(*strValue == '[')
|
|
{
|
|
Color4 ret;
|
|
|
|
ret.x = (float(tstoi(++strValue))/255.0f)+0.001f;
|
|
|
|
if(!(strValue = schr(strValue, ',')))
|
|
break;
|
|
ret.y = (float(tstoi(++strValue))/255.0f)+0.001f;
|
|
|
|
if(!(strValue = schr(strValue, ',')))
|
|
break;
|
|
ret.z = (float(tstoi(++strValue))/255.0f)+0.001f;
|
|
|
|
if(!(strValue = schr(strValue, ',')))
|
|
{
|
|
ret.w = 1.0f;
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return ret;
|
|
}
|
|
ret.w = (float(tstoi(++strValue))/255.0f)+0.001f;
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return ret;
|
|
}
|
|
else if( (*LPWORD(strValue) == 'x0') ||
|
|
(*LPWORD(strValue) == 'X0') )
|
|
{
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return RGBA_to_Vect4(tstring_base_to_int(strValue+2, NULL, 16));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return Color4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
}
|
|
|
|
BOOL ConfigFile::GetStringList(CTSTR lpSection, CTSTR lpKey, StringList &StrList)
|
|
{
|
|
OSEnterMutex(hHorribleThreadSafetyMutex);
|
|
assert(lpSection);
|
|
assert(lpKey);
|
|
|
|
BOOL bFoundKey = 0;
|
|
|
|
DWORD i,j,k;
|
|
|
|
for(i=0; i<Sections.Num(); i++)
|
|
{
|
|
ConfigSection §ion = Sections[i];
|
|
if(scmpi(lpSection, section.name) == 0)
|
|
{
|
|
for(j=0; j<section.Keys.Num(); j++)
|
|
{
|
|
ConfigKey &key = section.Keys[j];
|
|
if(scmpi(lpKey, key.name) == 0)
|
|
{
|
|
for(k=0; k<key.ValueList.Num(); k++)
|
|
StrList << key.ValueList[k];
|
|
|
|
bFoundKey = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return bFoundKey;
|
|
}
|
|
|
|
BOOL ConfigFile::GetIntList(CTSTR lpSection, CTSTR lpKey, List<int> &IntList)
|
|
{
|
|
OSEnterMutex(hHorribleThreadSafetyMutex);
|
|
assert(lpSection);
|
|
assert(lpKey);
|
|
|
|
DWORD i,j,k;
|
|
BOOL bFoundKey = 0;
|
|
|
|
for(i=0; i<Sections.Num(); i++)
|
|
{
|
|
ConfigSection §ion = Sections[i];
|
|
if(scmpi(lpSection, section.name) == 0)
|
|
{
|
|
for(j=0; j<section.Keys.Num(); j++)
|
|
{
|
|
ConfigKey &key = section.Keys[j];
|
|
if(scmpi(lpKey, key.name) == 0)
|
|
{
|
|
for(k=0; k<key.ValueList.Num(); k++)
|
|
{
|
|
if(scmpi(key.ValueList[k], TEXT("true")) == 0)
|
|
IntList << 1;
|
|
else if(scmpi(key.ValueList[k], TEXT("false")) == 0)
|
|
IntList << 0;
|
|
else
|
|
{
|
|
if(ValidIntString(key.ValueList[k]))
|
|
IntList << tstring_base_to_int(key.ValueList[k], NULL, 0);
|
|
}
|
|
}
|
|
|
|
bFoundKey = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return bFoundKey;
|
|
}
|
|
|
|
BOOL ConfigFile::GetFloatList(CTSTR lpSection, CTSTR lpKey, List<float> &FloatList)
|
|
{
|
|
OSEnterMutex(hHorribleThreadSafetyMutex);
|
|
assert(lpSection);
|
|
assert(lpKey);
|
|
|
|
DWORD i,j,k;
|
|
BOOL bFoundKey = 0;
|
|
|
|
for(i=0; i<Sections.Num(); i++)
|
|
{
|
|
ConfigSection §ion = Sections[i];
|
|
if(scmpi(lpSection, section.name) == 0)
|
|
{
|
|
for(j=0; j<section.Keys.Num(); j++)
|
|
{
|
|
ConfigKey &key = section.Keys[j];
|
|
if(scmpi(lpKey, key.name) == 0)
|
|
{
|
|
for(k=0; k<key.ValueList.Num(); k++)
|
|
FloatList << (float)tstof(key.ValueList[k]);
|
|
|
|
bFoundKey = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return bFoundKey;
|
|
}
|
|
|
|
BOOL ConfigFile::GetColorList(CTSTR lpSection, CTSTR lpKey, List<Color4> &ColorList)
|
|
{
|
|
OSEnterMutex(hHorribleThreadSafetyMutex);
|
|
assert(lpSection);
|
|
assert(lpKey);
|
|
|
|
DWORD i,j,k;
|
|
BOOL bFoundKey = 0;
|
|
|
|
for(i=0; i<Sections.Num(); i++)
|
|
{
|
|
ConfigSection §ion = Sections[i];
|
|
if(scmpi(lpSection, section.name) == 0)
|
|
{
|
|
for(j=0; j<section.Keys.Num(); j++)
|
|
{
|
|
ConfigKey &key = section.Keys[j];
|
|
if(scmpi(lpKey, key.name) == 0)
|
|
{
|
|
for(k=0; k<key.ValueList.Num(); k++)
|
|
{
|
|
TSTR strValue = key.ValueList[k];
|
|
if(*strValue == '{')
|
|
{
|
|
Color4 ret;
|
|
|
|
ret.x = float(tstof(++strValue));
|
|
|
|
if(!(strValue = schr(strValue, ',')))
|
|
break;
|
|
ret.y = float(tstof(++strValue));
|
|
|
|
if(!(strValue = schr(strValue, ',')))
|
|
break;
|
|
ret.z = float(tstof(++strValue));
|
|
|
|
if(!(strValue = schr(strValue, ',')))
|
|
ret.w = 0.0f;
|
|
else
|
|
ret.w = float(tstof(++strValue));
|
|
ColorList << ret;
|
|
}
|
|
else if(*strValue == '[')
|
|
{
|
|
Color4 ret;
|
|
|
|
ret.x = float(tstoi(++strValue))/255.0f;
|
|
|
|
if(!(strValue = schr(strValue, ',')))
|
|
break;
|
|
ret.y = float(tstoi(++strValue))/255.0f;
|
|
|
|
if(!(strValue = schr(strValue, ',')))
|
|
break;
|
|
ret.z = float(tstoi(++strValue))/255.0f;
|
|
|
|
if(!(strValue = schr(strValue, ',')))
|
|
ret.w = 0.0f;
|
|
else
|
|
ret.w = float(tstoi(++strValue))/255.0f;
|
|
|
|
ColorList << ret;
|
|
}
|
|
else if( (*LPWORD(strValue) == 'x0') ||
|
|
(*LPWORD(strValue) == 'X0') )
|
|
{
|
|
ColorList << RGBA_to_Vect4(tstring_base_to_int(strValue+2, NULL, 16));
|
|
}
|
|
}
|
|
|
|
bFoundKey = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return bFoundKey;
|
|
}
|
|
|
|
void ConfigFile::SetString(CTSTR lpSection, CTSTR lpKey, CTSTR lpString)
|
|
{
|
|
if(!bOpen)
|
|
return;
|
|
|
|
if(!lpString)
|
|
lpString = TEXT("");
|
|
|
|
SetKey(lpSection, lpKey, lpString);
|
|
}
|
|
|
|
void ConfigFile::SetInt(CTSTR lpSection, CTSTR lpKey, int number)
|
|
{
|
|
if(!bOpen)
|
|
return;
|
|
|
|
TCHAR strNum[20];
|
|
|
|
itots_s(number, strNum, 19, 10);
|
|
SetKey(lpSection, lpKey, strNum);
|
|
}
|
|
|
|
void ConfigFile::SetHex(CTSTR lpSection, CTSTR lpKey, DWORD number)
|
|
{
|
|
if(!bOpen)
|
|
return;
|
|
|
|
TCHAR strNum[22] = TEXT("0x");
|
|
|
|
itots_s(number, strNum+2, 19, 16);
|
|
SetKey(lpSection, lpKey, strNum);
|
|
}
|
|
|
|
void ConfigFile::SetFloat(CTSTR lpSection, CTSTR lpKey, float number)
|
|
{
|
|
if(!bOpen)
|
|
return;
|
|
|
|
TCHAR strNum[20];
|
|
|
|
tsprintf_s(strNum, 19, TEXT("%f"), number);
|
|
SetKey(lpSection, lpKey, strNum);
|
|
}
|
|
|
|
void ConfigFile::SetColor(CTSTR lpSection, CTSTR lpKey, const Color4 &color)
|
|
{
|
|
if(!bOpen)
|
|
return;
|
|
|
|
TCHAR strColor[50];
|
|
|
|
tsprintf_s(strColor, 49, TEXT("{%.3f, %.3f, %.3f, %.3f}"), color.x, color.y, color.z, color.w);
|
|
SetKey(lpSection, lpKey, strColor);
|
|
}
|
|
|
|
void ConfigFile::AddString(CTSTR lpSection, CTSTR lpKey, CTSTR lpString)
|
|
{
|
|
if(!bOpen)
|
|
return;
|
|
|
|
if(!lpString)
|
|
return;
|
|
|
|
AddKey(lpSection, lpKey, lpString);
|
|
}
|
|
|
|
void ConfigFile::AddInt(CTSTR lpSection, CTSTR lpKey, int number)
|
|
{
|
|
if(!bOpen)
|
|
return;
|
|
|
|
TCHAR strNum[20];
|
|
|
|
itots_s(number, strNum, 19, 10);
|
|
AddKey(lpSection, lpKey, strNum);
|
|
}
|
|
|
|
void ConfigFile::AddFloat(CTSTR lpSection, CTSTR lpKey, float number)
|
|
{
|
|
if(!bOpen)
|
|
return;
|
|
|
|
TCHAR strNum[20];
|
|
|
|
tsprintf_s(strNum, 19, TEXT("%f"), number);
|
|
AddKey(lpSection, lpKey, strNum);
|
|
}
|
|
|
|
void ConfigFile::AddColor(CTSTR lpSection, CTSTR lpKey, const Color4 &color)
|
|
{
|
|
if(!bOpen)
|
|
return;
|
|
|
|
TCHAR strColor[50];
|
|
|
|
tsprintf_s(strColor, 49, TEXT("{%.3f, %.3f, %.3f, %.3f}"), color.x, color.y, color.z, color.w);
|
|
AddKey(lpSection, lpKey, strColor);
|
|
}
|
|
|
|
|
|
void ConfigFile::SetStringList(CTSTR lpSection, CTSTR lpKey, StringList &StrList)
|
|
{
|
|
while(HasKey(lpSection, lpKey))
|
|
Remove(lpSection, lpKey);
|
|
|
|
for(unsigned int i=0; i<StrList.Num(); i++)
|
|
AddString(lpSection, lpKey, StrList[i]);
|
|
}
|
|
|
|
void ConfigFile::SetIntList(CTSTR lpSection, CTSTR lpKey, List<int> &IntList)
|
|
{
|
|
while(HasKey(lpSection, lpKey))
|
|
Remove(lpSection, lpKey);
|
|
|
|
for(unsigned int i=0; i<IntList.Num(); i++)
|
|
AddInt(lpSection, lpKey, IntList[i]);
|
|
}
|
|
|
|
void ConfigFile::SetFloatList(CTSTR lpSection, CTSTR lpKey, List<float> &FloatList)
|
|
{
|
|
while(HasKey(lpSection, lpKey))
|
|
Remove(lpSection, lpKey);
|
|
|
|
for(unsigned int i=0; i<FloatList.Num(); i++)
|
|
AddFloat(lpSection, lpKey, FloatList[i]);
|
|
}
|
|
|
|
void ConfigFile::SetColorList(CTSTR lpSection, CTSTR lpKey, List<Color4> &ColorList)
|
|
{
|
|
while(HasKey(lpSection, lpKey))
|
|
Remove(lpSection, lpKey);
|
|
|
|
for(unsigned int i=0; i<ColorList.Num(); i++)
|
|
AddColor(lpSection, lpKey, ColorList[i]);
|
|
}
|
|
|
|
|
|
BOOL ConfigFile::HasKey(CTSTR lpSection, CTSTR lpKey)
|
|
{
|
|
OSEnterMutex(hHorribleThreadSafetyMutex);
|
|
for(unsigned int i=0; i<Sections.Num(); i++)
|
|
{
|
|
ConfigSection §ion = Sections[i];
|
|
if(scmpi(section.name, lpSection) == 0)
|
|
{
|
|
for(unsigned int j=0; j<section.Keys.Num(); j++)
|
|
{
|
|
if(scmpi(section.Keys[j].name, lpKey) == 0)
|
|
{
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
void ConfigFile::SetKey(CTSTR lpSection, CTSTR lpKey, CTSTR newvalue)
|
|
{
|
|
OSEnterMutex(hHorribleThreadSafetyMutex);
|
|
assert(lpSection);
|
|
assert(lpKey);
|
|
TSTR lpTemp = lpActualFileData, lpEnd = &lpActualFileData[dwLength], lpSectionStart;
|
|
DWORD dwSectionNameSize = slen(lpSection), dwKeyNameSize = slen(lpKey);
|
|
BOOL bInSection = 0;
|
|
|
|
do
|
|
{
|
|
lpTemp = sstr(lpTemp, TEXT("\n["));
|
|
if(!lpTemp)
|
|
break;
|
|
|
|
lpTemp += 2;
|
|
if((scmpi_n(lpTemp, lpSection, dwSectionNameSize) == 0) && (lpTemp[dwSectionNameSize] == ']'))
|
|
{
|
|
bInSection = 1;
|
|
lpSectionStart = lpTemp = schr(lpTemp, '\n')+1;
|
|
break;
|
|
}
|
|
}while(lpTemp < lpEnd);
|
|
|
|
if(!bInSection)
|
|
{
|
|
lpTemp -= 2;
|
|
|
|
XFile file(strFileName, XFILE_WRITE, XFILE_CREATEALWAYS);
|
|
file.Write("\xEF\xBB\xBF", 3);
|
|
file.WriteAsUTF8(&lpActualFileData[2], dwLength-4);
|
|
file.Write("\r\n[", 3);
|
|
file.WriteAsUTF8(lpSection, dwSectionNameSize);
|
|
file.Write("]\r\n", 3);
|
|
file.WriteAsUTF8(lpKey, dwKeyNameSize);
|
|
file.Write("=", 1);
|
|
file.WriteAsUTF8(newvalue, slen(newvalue));
|
|
file.Write("\r\n", 2);
|
|
file.Close();
|
|
|
|
if(LoadFile(XFILE_OPENEXISTING))
|
|
LoadData();
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return;
|
|
}
|
|
|
|
do
|
|
{
|
|
if(*lpTemp == '[')
|
|
{
|
|
XFile file(strFileName, XFILE_WRITE, XFILE_CREATEALWAYS);
|
|
file.Write("\xEF\xBB\xBF", 3);
|
|
file.WriteAsUTF8(&lpActualFileData[2], DWORD(lpSectionStart-lpActualFileData-2));
|
|
file.WriteAsUTF8(lpKey, dwKeyNameSize);
|
|
file.Write("=", 1);
|
|
file.WriteAsUTF8(newvalue, slen(newvalue));
|
|
file.Write("\r\n", 2);
|
|
file.WriteAsUTF8(lpSectionStart, slen(lpSectionStart)-2);
|
|
file.Close();
|
|
|
|
if(LoadFile(XFILE_OPENEXISTING))
|
|
LoadData();
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return;
|
|
}
|
|
else if(*(LPWORD)lpTemp == '//')
|
|
{
|
|
lpTemp = schr(lpTemp, '\n')+1;
|
|
continue;
|
|
}
|
|
else if(bInSection)
|
|
{
|
|
if((scmpi_n(lpTemp, lpKey, dwKeyNameSize) == 0) && (lpTemp[dwKeyNameSize] == '='))
|
|
{
|
|
lpTemp = &lpTemp[dwKeyNameSize+1];
|
|
|
|
TSTR lpNextLine = schr(lpTemp, '\r');
|
|
int newlen = slen(newvalue);
|
|
|
|
if ((*lpTemp == '\r' && *newvalue == '\0') || (lpNextLine - lpTemp == newlen && !scmp_n(lpTemp, newvalue, newlen)))
|
|
{
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return;
|
|
}
|
|
|
|
String tmpFileName = strFileName;
|
|
tmpFileName += TEXT(".tmp");
|
|
|
|
XFile file;
|
|
if (file.Open(tmpFileName, XFILE_WRITE, XFILE_CREATEALWAYS))
|
|
{
|
|
if (file.Write("\xEF\xBB\xBF", 3) != 3)
|
|
{
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return;
|
|
}
|
|
if (!file.WriteAsUTF8(&lpActualFileData[2], DWORD(lpTemp - lpActualFileData - 2)))
|
|
{
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return;
|
|
}
|
|
if (!file.WriteAsUTF8(newvalue, slen(newvalue)))
|
|
{
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return;
|
|
}
|
|
if (!file.WriteAsUTF8(lpNextLine, slen(lpNextLine) - 2))
|
|
{
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return;
|
|
}
|
|
|
|
file.Close();
|
|
if (!OSRenameFile(tmpFileName, strFileName))
|
|
Log(TEXT("ConfigFile::SetKey: Unable to move new config file %s to %s"), tmpFileName.Array(), strFileName.Array());
|
|
}
|
|
|
|
if(LoadFile(XFILE_OPENEXISTING))
|
|
LoadData();
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return;
|
|
}
|
|
}
|
|
|
|
lpTemp = schr(lpTemp, '\n')+1;
|
|
}while(lpTemp < lpEnd);
|
|
|
|
XFile file(strFileName, XFILE_WRITE, XFILE_CREATEALWAYS);
|
|
file.Write("\xEF\xBB\xBF", 3);
|
|
file.WriteAsUTF8(&lpActualFileData[2], DWORD(lpSectionStart-lpActualFileData-2));
|
|
file.WriteAsUTF8(lpKey, dwKeyNameSize);
|
|
file.Write("=", 1);
|
|
file.WriteAsUTF8(newvalue, slen(newvalue));
|
|
file.Write("\r\n", 2);
|
|
file.WriteAsUTF8(lpSectionStart, slen(lpSectionStart)-2);
|
|
file.Close();
|
|
|
|
if(LoadFile(XFILE_OPENEXISTING))
|
|
LoadData();
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
}
|
|
|
|
void ConfigFile::Remove(CTSTR lpSection, CTSTR lpKey)
|
|
{
|
|
OSEnterMutex(hHorribleThreadSafetyMutex);
|
|
assert(lpSection);
|
|
assert(lpKey);
|
|
TSTR lpTemp = lpActualFileData, lpEnd = &lpActualFileData[dwLength], lpSectionStart;
|
|
DWORD dwSectionNameSize = slen(lpSection), dwKeyNameSize = slen(lpKey);
|
|
BOOL bInSection = 0;
|
|
|
|
do
|
|
{
|
|
lpTemp = sstr(lpTemp, TEXT("\n["));
|
|
if(!lpTemp)
|
|
break;
|
|
|
|
lpTemp += 2;
|
|
if((scmpi_n(lpTemp, lpSection, dwSectionNameSize) == 0) && (lpTemp[dwSectionNameSize] == ']'))
|
|
{
|
|
bInSection = 1;
|
|
lpSectionStart = lpTemp = schr(lpTemp, '\n')+1;
|
|
break;
|
|
}
|
|
}while(lpTemp < lpEnd);
|
|
|
|
if(!bInSection)
|
|
{
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return; //not possible, usually.
|
|
}
|
|
|
|
do
|
|
{
|
|
if(*lpTemp == '[')
|
|
{
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return;
|
|
}
|
|
else if(bInSection && (*(LPWORD)lpTemp != '//'))
|
|
{
|
|
if((scmpi_n(lpTemp, lpKey, dwKeyNameSize) == 0) && (lpTemp[dwKeyNameSize] == '='))
|
|
{
|
|
TSTR lpNextLine = schr(lpTemp, '\n')+1;
|
|
XFile file(strFileName, XFILE_WRITE, XFILE_CREATEALWAYS);
|
|
file.Write("\xEF\xBB\xBF", 3);
|
|
file.WriteAsUTF8(&lpActualFileData[2], DWORD(lpTemp-lpActualFileData-2));
|
|
file.WriteAsUTF8(lpNextLine, slen(lpNextLine)-2);
|
|
file.Close();
|
|
|
|
if(LoadFile(XFILE_OPENEXISTING))
|
|
LoadData();
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return;
|
|
}
|
|
}
|
|
|
|
lpTemp = schr(lpTemp, '\n')+1;
|
|
}while(lpTemp < lpEnd);
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
}
|
|
|
|
void ConfigFile::AddKey(CTSTR lpSection, CTSTR lpKey, CTSTR newvalue)
|
|
{
|
|
assert(lpSection);
|
|
assert(lpKey);
|
|
TSTR lpTemp = lpActualFileData, lpEnd = &lpActualFileData[dwLength], lpSectionStart;
|
|
DWORD dwSectionNameSize = slen(lpSection), dwKeyNameSize = slen(lpKey);
|
|
BOOL bInSection = 0;
|
|
|
|
do
|
|
{
|
|
lpTemp = sstr(lpTemp, TEXT("\n["));
|
|
if(!lpTemp)
|
|
break;
|
|
|
|
lpTemp += 2;
|
|
if((scmpi_n(lpTemp, lpSection, dwSectionNameSize) == 0) && (lpTemp[dwSectionNameSize] == ']'))
|
|
{
|
|
bInSection = 1;
|
|
lpSectionStart = lpTemp = schr(lpTemp, '\n')+1;
|
|
break;
|
|
}
|
|
}while(lpTemp < lpEnd);
|
|
|
|
if(!bInSection)
|
|
{
|
|
XFile file(strFileName, XFILE_WRITE, XFILE_CREATEALWAYS);
|
|
file.Write("\xEF\xBB\xBF", 3);
|
|
file.WriteAsUTF8(&lpActualFileData[2], dwLength-4);
|
|
file.Write("\r\n[", 3);
|
|
file.WriteAsUTF8(lpSection, dwSectionNameSize);
|
|
file.Write("]\r\n", 3);
|
|
file.WriteAsUTF8(lpKey, dwKeyNameSize);
|
|
file.Write("=", 1);
|
|
file.WriteAsUTF8(newvalue, slen(newvalue));
|
|
file.Write("\r\n", 2);
|
|
file.Close();
|
|
|
|
if(LoadFile(XFILE_OPENEXISTING))
|
|
LoadData();
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return;
|
|
}
|
|
|
|
TSTR lpLastItem = NULL;
|
|
|
|
do
|
|
{
|
|
if(*lpTemp == '[')
|
|
{
|
|
XFile file(strFileName, XFILE_WRITE, XFILE_CREATEALWAYS);
|
|
file.Write("\xEF\xBB\xBF", 3);
|
|
file.WriteAsUTF8(&lpActualFileData[2], DWORD(lpSectionStart-lpActualFileData-2));
|
|
file.WriteAsUTF8(lpKey, dwKeyNameSize);
|
|
file.Write("=", 1);
|
|
file.WriteAsUTF8(newvalue, slen(newvalue));
|
|
file.Write("\r\n", 2);
|
|
file.WriteAsUTF8(lpSectionStart, slen(lpSectionStart)-2);
|
|
file.Close();
|
|
|
|
if(LoadFile(XFILE_OPENEXISTING))
|
|
LoadData();
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return;
|
|
}
|
|
else if(*(LPWORD)lpTemp == '//')
|
|
{
|
|
lpTemp = schr(lpTemp, '\n')+1;
|
|
continue;
|
|
}
|
|
else if(bInSection)
|
|
{
|
|
if((scmpi_n(lpTemp, lpKey, dwKeyNameSize) == 0) && (lpTemp[dwKeyNameSize] == '='))
|
|
{
|
|
lpLastItem = schr(lpTemp, '\n')+1;
|
|
}
|
|
else if(lpLastItem)
|
|
{
|
|
lpTemp = lpLastItem;
|
|
XFile file(strFileName, XFILE_WRITE, XFILE_CREATEALWAYS);
|
|
file.Write("\xEF\xBB\xBF", 3);
|
|
file.WriteAsUTF8(&lpActualFileData[2], DWORD(lpTemp-lpActualFileData-2));
|
|
file.WriteAsUTF8(lpKey, dwKeyNameSize);
|
|
file.Write("=", 1);
|
|
file.WriteAsUTF8(newvalue, slen(newvalue));
|
|
file.Write("\r\n", 2);
|
|
file.WriteAsUTF8(lpTemp, slen(lpTemp)-2);
|
|
file.Close();
|
|
|
|
if(LoadFile(XFILE_OPENEXISTING))
|
|
LoadData();
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
return;
|
|
}
|
|
}
|
|
|
|
lpTemp = schr(lpTemp, '\n')+1;
|
|
}while(lpTemp < lpEnd);
|
|
|
|
XFile file(strFileName, XFILE_WRITE, XFILE_CREATEALWAYS);
|
|
file.Write("\xEF\xBB\xBF", 3);
|
|
file.WriteAsUTF8(&lpActualFileData[2], DWORD(lpSectionStart-lpActualFileData-2));
|
|
file.WriteAsUTF8(lpKey, dwKeyNameSize);
|
|
file.Write("=", 1);
|
|
file.WriteAsUTF8(newvalue, slen(newvalue));
|
|
file.Write("\r\n", 2);
|
|
file.WriteAsUTF8(lpSectionStart, slen(lpSectionStart)-2);
|
|
file.Close();
|
|
|
|
if(LoadFile(XFILE_OPENEXISTING))
|
|
LoadData();
|
|
|
|
OSLeaveMutex(hHorribleThreadSafetyMutex);
|
|
}
|