2020-06-09 09:57:52 +00:00
|
|
|
#include <Common\path.h>
|
|
|
|
#include <Common\IniFileClass.h>
|
|
|
|
#include <Common\StdString.h>
|
|
|
|
#include <Project64-core\N64System\CheatFile.h>
|
2019-04-18 07:27:20 +00:00
|
|
|
#include <algorithm>
|
|
|
|
#include <set>
|
|
|
|
#include <windows.h>
|
|
|
|
|
|
|
|
struct compareKeyValueItem
|
|
|
|
{
|
|
|
|
inline bool operator() (CIniFileBase::KeyValueItem & struct1, const CIniFileBase::KeyValueItem & struct2)
|
|
|
|
{
|
|
|
|
std::string a = *struct1.first;
|
|
|
|
std::string b = *struct2.first;
|
|
|
|
if (_stricmp(a.c_str(), "Name") == 0)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (_stricmp(b.c_str(), "Name") == 0)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (a.length() > 5 && _strnicmp(a.c_str(), "cheat", 5) == 0 &&
|
|
|
|
b.length() > 5 && _strnicmp(b.c_str(), "cheat", 5) == 0)
|
|
|
|
{
|
|
|
|
int i1 = atoi(&(*struct1.first)[5]);
|
|
|
|
int i2 = atoi(&(*struct2.first)[5]);
|
|
|
|
if (i1 != i2)
|
|
|
|
{
|
|
|
|
return i1 < i2;
|
|
|
|
}
|
|
|
|
char Buffer[40];
|
|
|
|
int number_len = strlen(_itoa(i1, Buffer, 10));
|
|
|
|
if (strlen(&a[5 + number_len]) == 0)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (strlen(&b[5 + number_len]) == 0)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return _stricmp(&a[5 + number_len], &b[5 + number_len]) <= 0;
|
|
|
|
}
|
|
|
|
return _stricmp(a.c_str(), b.c_str()) <= 0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void CustomSortData(CIniFileBase::KeyValueVector & data)
|
|
|
|
{
|
|
|
|
std::sort(data.begin(), data.end(), compareKeyValueItem());
|
|
|
|
}
|
|
|
|
|
|
|
|
void SplitFile(const char * FileName, const char * Target)
|
|
|
|
{
|
|
|
|
if (!CPath(Target,"").DirectoryCreate())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CPath SearchDir(Target, "*.*");
|
|
|
|
if (SearchDir.FindFirst())
|
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
|
|
|
SearchDir.Delete();
|
|
|
|
} while (SearchDir.FindNext());
|
|
|
|
}
|
|
|
|
|
|
|
|
CIniFile::SectionList Sections;
|
|
|
|
CIniFile CheatIniFile(FileName);
|
|
|
|
CheatIniFile.GetVectorOfSections(Sections);
|
|
|
|
|
|
|
|
for (size_t i = 0, n = Sections.size(); i < n; i++)
|
|
|
|
{
|
|
|
|
const char * Section = Sections[i].c_str();
|
|
|
|
|
|
|
|
CIniFile::KeyValueData data;
|
|
|
|
CheatIniFile.GetKeyValueData(Section, data);
|
|
|
|
|
|
|
|
stdstr Name = CheatIniFile.GetString(Section, "Name", "");
|
|
|
|
Name.Trim("\t =");
|
|
|
|
if (Name.length() == 0)
|
|
|
|
{
|
|
|
|
Name = CheatIniFile.GetString(Section, "Good Name", Section);
|
|
|
|
Name.Trim("\t =");
|
|
|
|
}
|
|
|
|
Name.Replace("\\", "-");
|
|
|
|
Name.Replace("/", "-");
|
2020-06-08 07:22:41 +00:00
|
|
|
Name.Replace(":", " -");
|
2019-04-18 07:27:20 +00:00
|
|
|
if (Name.length() == 0)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
CPath GameFileName(Target, stdstr_f("%s.%s", Name.c_str(), CPath(FileName).GetExtension().c_str()).c_str());
|
|
|
|
CIniFile GameIniFile(GameFileName);
|
|
|
|
if (!GameIniFile.IsFileOpen())
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
GameIniFile.SetAutoFlush(false);
|
|
|
|
GameIniFile.SetCustomSort(CustomSortData);
|
|
|
|
|
|
|
|
for (CIniFile::KeyValueData::const_iterator itr = data.begin(); itr != data.end(); itr++)
|
|
|
|
{
|
|
|
|
stdstr DataLine(itr->second);
|
|
|
|
DataLine.Trim("\t =");
|
|
|
|
if (strcmp(itr->first.c_str(), "Good Name") == 0)
|
|
|
|
{
|
|
|
|
GameIniFile.SaveString(Section, "Name", DataLine.c_str());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GameIniFile.SaveString(Section, itr->first.c_str(), DataLine.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
GameIniFile.FlushChanges();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef std::map<std::string, std::string> Files;
|
|
|
|
|
|
|
|
void RegionSection(CFile &TargetIniFile, Files &files, const char * Region, const char * RegionCode)
|
|
|
|
{
|
|
|
|
stdstr_f LineData = stdstr_f("//--------------- %s Region Cheat Codes ---------------\r\n\r\n", Region);
|
|
|
|
TargetIniFile.Write(LineData.c_str(), (int)LineData.length());
|
|
|
|
|
|
|
|
bool first = true;
|
|
|
|
for (Files::const_iterator itr = files.begin(); itr != files.end(); itr++)
|
|
|
|
{
|
|
|
|
CIniFile GameIniFile(itr->second.c_str());
|
|
|
|
GameIniFile.SetCustomSort(CustomSortData);
|
|
|
|
CIniFile::SectionList Sections;
|
|
|
|
GameIniFile.GetVectorOfSections(Sections);
|
|
|
|
|
|
|
|
bool found = false;
|
|
|
|
stdstr_f searchStr(":%s", RegionCode);
|
|
|
|
for (size_t i = 0, n = Sections.size(); i < n; i++)
|
|
|
|
{
|
|
|
|
const char * Section = Sections[i].c_str();
|
|
|
|
const char * pos = strstr(Section, searchStr.c_str());
|
|
|
|
if (pos == NULL)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0, n = Sections.size(); i < n; i++)
|
|
|
|
{
|
|
|
|
const char * Section = Sections[i].c_str();
|
|
|
|
|
|
|
|
CIniFile::KeyValueData data;
|
|
|
|
GameIniFile.GetKeyValueData(Section, data);
|
|
|
|
|
|
|
|
CIniFile::KeyValueVector data2;
|
|
|
|
for (CIniFile::KeyValueData::const_iterator DataItr = data.begin(); DataItr != data.end(); DataItr++)
|
|
|
|
{
|
|
|
|
data2.push_back(CIniFile::KeyValueItem(&DataItr->first, &DataItr->second));
|
|
|
|
}
|
|
|
|
std::sort(data2.begin(), data2.end(), compareKeyValueItem());
|
|
|
|
|
|
|
|
if (first)
|
|
|
|
{
|
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LineData = stdstr_f("\r\n//----\r\n\r\n");
|
|
|
|
TargetIniFile.Write(LineData.c_str(), (int)LineData.length());
|
|
|
|
}
|
|
|
|
LineData = stdstr_f("[%s]\r\n", Section);
|
|
|
|
TargetIniFile.Write(LineData.c_str(), (int)LineData.length());
|
|
|
|
|
|
|
|
for (CIniFile::KeyValueVector::const_iterator DataItr = data2.begin(); DataItr != data2.end(); DataItr++)
|
|
|
|
{
|
|
|
|
LineData = stdstr_f("%s=%s\r\n", DataItr->first->c_str(), DataItr->second->c_str());
|
|
|
|
TargetIniFile.Write(LineData.c_str(), (int)LineData.length());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void JoinFile(const char * Directory, const char * Target)
|
|
|
|
{
|
|
|
|
Files files;
|
|
|
|
CPath SearchDir(Directory, "*.*");
|
|
|
|
if (SearchDir.FindFirst())
|
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
|
|
|
CIniFile GameIniFile(SearchDir);
|
|
|
|
|
|
|
|
CIniFile::SectionList Sections;
|
|
|
|
GameIniFile.GetVectorOfSections(Sections);
|
|
|
|
for (size_t i = 0, n = Sections.size(); i < n; i++)
|
|
|
|
{
|
|
|
|
const char * Section = Sections[i].c_str();
|
|
|
|
stdstr Name = GameIniFile.GetString(Section, "Name", Section);
|
|
|
|
Name.Trim("\t =");
|
|
|
|
if (Name.size() > 0)
|
|
|
|
{
|
|
|
|
files.insert(Files::value_type(Name, SearchDir));
|
2020-06-09 09:57:52 +00:00
|
|
|
break;
|
2019-04-18 07:27:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (SearchDir.FindNext());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CPath(Target).Exists())
|
|
|
|
{
|
|
|
|
CPath(Target).Delete();
|
|
|
|
};
|
|
|
|
|
|
|
|
CFile TargetIniFile;
|
|
|
|
if (!TargetIniFile.Open(Target, CFileBase::modeReadWrite | CFileBase::modeCreate | CFileBase::shareDenyWrite))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (strcmp(CPath(Target).GetExtension().c_str(), "cht") == 0)
|
|
|
|
{
|
|
|
|
stdstr_f LineData = stdstr_f("// Project64 Official Cheats Database\r\n");
|
|
|
|
TargetIniFile.Write(LineData.c_str(), (int)LineData.length());
|
|
|
|
LineData = stdstr_f("// Not for use with PJ64 v1.6 or previous\r\n");
|
|
|
|
TargetIniFile.Write(LineData.c_str(), (int)LineData.length());
|
|
|
|
LineData = stdstr_f("// ----------------------------------------------------\r\n\r\n");
|
|
|
|
TargetIniFile.Write(LineData.c_str(), (int)LineData.length());
|
|
|
|
|
|
|
|
CPath MetaFileName(Directory, "Meta.cht");
|
|
|
|
CIniFile MetaIniFile(MetaFileName);
|
|
|
|
if (MetaIniFile.IsFileOpen())
|
|
|
|
{
|
|
|
|
CIniFile::KeyValueData data;
|
|
|
|
MetaIniFile.GetKeyValueData("Meta", data);
|
|
|
|
|
|
|
|
LineData = stdstr_f("[Meta]\r\n");
|
|
|
|
TargetIniFile.Write(LineData.c_str(), (int)LineData.length());
|
|
|
|
for (CIniFile::KeyValueData::const_iterator itr = data.begin(); itr != data.end(); itr++)
|
|
|
|
{
|
|
|
|
stdstr DataLine(itr->second);
|
|
|
|
DataLine.Trim("\t =");
|
|
|
|
LineData = stdstr_f("%s=%s\r\n",itr->first.c_str(), DataLine.c_str());
|
|
|
|
TargetIniFile.Write(LineData.c_str(), (int)LineData.length());
|
|
|
|
}
|
|
|
|
LineData = stdstr_f("\r\n");
|
|
|
|
TargetIniFile.Write(LineData.c_str(), (int)LineData.length());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
RegionSection(TargetIniFile, files, "(J)", "4A");
|
|
|
|
RegionSection(TargetIniFile, files, "(JU)", "41");
|
|
|
|
RegionSection(TargetIniFile, files, "(U)", "45");
|
|
|
|
RegionSection(TargetIniFile, files, "PAL (E)", "50");
|
|
|
|
RegionSection(TargetIniFile, files, "PAL (A)", "55");
|
|
|
|
RegionSection(TargetIniFile, files, "PAL (F)", "46");
|
|
|
|
RegionSection(TargetIniFile, files, "PAL (G)", "44");
|
|
|
|
RegionSection(TargetIniFile, files, "PAL (I)", "49");
|
|
|
|
RegionSection(TargetIniFile, files, "PAL (S)", "53");
|
|
|
|
RegionSection(TargetIniFile, files, "PAL(FGD)", "58");
|
|
|
|
RegionSection(TargetIniFile, files, "Demo", "0");
|
|
|
|
}
|
|
|
|
|
2020-06-01 22:49:07 +00:00
|
|
|
void UpdateNames(const char* Directory, const char* RdbFile)
|
|
|
|
{
|
|
|
|
CIniFile RdbIni(RdbFile);
|
|
|
|
|
|
|
|
Files files;
|
|
|
|
CPath SearchDir(Directory, "*.cht");
|
|
|
|
if (SearchDir.FindFirst())
|
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
|
|
|
CIniFile CheatFile(SearchDir);
|
|
|
|
CIniFile::SectionList Sections;
|
|
|
|
CheatFile.GetVectorOfSections(Sections);
|
|
|
|
CheatFile.SetCustomSort(CustomSortData);
|
|
|
|
for (size_t i = 0, n = Sections.size(); i < n; i++)
|
|
|
|
{
|
|
|
|
const char * Section = Sections[i].c_str();
|
|
|
|
std::string Name = RdbIni.GetString(Section, "Good Name", "");
|
|
|
|
if (Name.empty())
|
|
|
|
{
|
|
|
|
Name = RdbIni.GetString(Section, "Internal Name", "");
|
|
|
|
}
|
|
|
|
if (Name.empty())
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
CheatFile.SaveString(Section, "Name", Name.c_str());
|
|
|
|
}
|
|
|
|
} while (SearchDir.FindNext());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-02 08:35:57 +00:00
|
|
|
uint32_t ConvertXP64Address(uint32_t Address)
|
|
|
|
{
|
|
|
|
uint32_t tmpAddress;
|
|
|
|
|
|
|
|
tmpAddress = (Address ^ 0x68000000) & 0xFF000000;
|
|
|
|
tmpAddress += ((Address + 0x002B0000) ^ 0x00810000) & 0x00FF0000;
|
|
|
|
tmpAddress += ((Address + 0x00002B00) ^ 0x00008200) & 0x0000FF00;
|
|
|
|
tmpAddress += ((Address + 0x0000002B) ^ 0x00000083) & 0x000000FF;
|
|
|
|
return tmpAddress;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t ConvertXP64ValueHi(uint8_t Value)
|
|
|
|
{
|
|
|
|
return (Value + 0x2B) ^ 0x84;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t ConvertXP64ValueLo(uint8_t Value)
|
|
|
|
{
|
|
|
|
return (Value + 0x2B) ^ 0x85;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t ConvertXP64Value(uint16_t Value)
|
|
|
|
{
|
|
|
|
uint16_t tmpValue;
|
|
|
|
|
|
|
|
tmpValue = ((Value + 0x2B00) ^ 0x8400) & 0xFF00;
|
|
|
|
tmpValue += ((Value + 0x002B) ^ 0x0085) & 0x00FF;
|
|
|
|
return tmpValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ConvertCheatOptions(const char * OptionValue, std::string& Options)
|
|
|
|
{
|
|
|
|
const char * ReadPos = strchr(Options.c_str(), '$');
|
|
|
|
std::string NewOptions;
|
|
|
|
if (ReadPos)
|
|
|
|
{
|
|
|
|
ReadPos += 1;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
NewOptions += NewOptions.empty() ? "$" : ",$";
|
|
|
|
const char* End = strchr(ReadPos, ',');
|
|
|
|
std::string Item = End != NULL ? std::string(ReadPos, End - ReadPos) : ReadPos;
|
|
|
|
ReadPos = strchr(ReadPos, '$');
|
|
|
|
if (ReadPos != NULL)
|
|
|
|
{
|
|
|
|
ReadPos += 1;
|
|
|
|
}
|
|
|
|
const char* Name = strchr(Item.c_str(), ' ');
|
|
|
|
if (Name == NULL)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
Name += 1;
|
|
|
|
if (strcmp(OptionValue, "????") == 0)
|
|
|
|
{
|
2020-06-09 09:57:52 +00:00
|
|
|
uint16_t CodeValue = ConvertXP64Value((uint16_t)strtoul(Item.c_str(), 0, 16));
|
2020-06-02 08:35:57 +00:00
|
|
|
NewOptions += stdstr_f("%04X %s", CodeValue, stdstr(Name).Trim().c_str());
|
|
|
|
}
|
|
|
|
else if (strcmp(OptionValue, "??XX") == 0)
|
|
|
|
{
|
2020-06-09 09:57:52 +00:00
|
|
|
uint8_t CodeValue = ConvertXP64ValueHi((uint8_t)strtoul(Item.c_str(), 0, 16));
|
2020-06-02 08:35:57 +00:00
|
|
|
NewOptions += stdstr_f("%02X %s", CodeValue, stdstr(Name).Trim().c_str());
|
|
|
|
}
|
|
|
|
else if (strcmp(OptionValue, "XX??") == 0)
|
|
|
|
{
|
2020-06-09 09:57:52 +00:00
|
|
|
uint8_t CodeValue = ConvertXP64ValueLo((uint8_t)strtoul(Item.c_str(), 0, 16));
|
2020-06-02 08:35:57 +00:00
|
|
|
NewOptions += stdstr_f("%02X %s", CodeValue, stdstr(Name).Trim().c_str());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} while (ReadPos);
|
|
|
|
}
|
|
|
|
Options = NewOptions;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ConvertCheatEntry(std::string& CheatEntry, std::string& CheatOptions)
|
|
|
|
{
|
|
|
|
typedef std::vector<std::string> CodeEntries;
|
|
|
|
|
|
|
|
size_t StartOfName = CheatEntry.find("\"");
|
|
|
|
if (StartOfName == std::string::npos)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
size_t EndOfName = CheatEntry.find("\"", StartOfName + 1);
|
|
|
|
if (EndOfName == std::string::npos)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
std::string Name = stdstr(CheatEntry.substr(StartOfName + 1, EndOfName - StartOfName - 1)).Trim("\t ,");
|
|
|
|
CodeEntries Entries;
|
|
|
|
|
|
|
|
const char* CheatString = &CheatEntry.c_str()[EndOfName + 2];
|
|
|
|
const char* ReadPos = CheatString;
|
|
|
|
bool ConvertOptions = false;
|
|
|
|
std::string OptionValue;
|
|
|
|
|
|
|
|
while (ReadPos)
|
|
|
|
{
|
|
|
|
uint32_t CodeCommand = strtoul(ReadPos, 0, 16);
|
|
|
|
ReadPos = strchr(ReadPos, ' ');
|
|
|
|
if (ReadPos == NULL)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ReadPos += 1;
|
|
|
|
std::string ValueStr = ReadPos;
|
|
|
|
const char* ValuePos = ReadPos;
|
|
|
|
ReadPos = strchr(ReadPos, ',');
|
|
|
|
if (ReadPos != NULL)
|
|
|
|
{
|
|
|
|
ValueStr.resize(ReadPos - ValuePos);
|
|
|
|
ReadPos++;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (CodeCommand & 0xFF000000)
|
|
|
|
{
|
|
|
|
case 0xE8000000:
|
|
|
|
CodeCommand = (ConvertXP64Address(CodeCommand) & 0xFFFFFF) | 0x80000000;
|
|
|
|
if (strchr(ValueStr.c_str(), '?') != NULL)
|
|
|
|
{
|
|
|
|
if (strncmp(ValueStr.c_str(), "????", 4) == 0)
|
|
|
|
{
|
|
|
|
Entries.push_back(stdstr_f("%08X ????", CodeCommand));
|
|
|
|
if (!OptionValue.empty() && OptionValue != "????")
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
OptionValue = "????";
|
|
|
|
}
|
|
|
|
else if (ValueStr.length() == 4 && strncmp(&ValueStr.c_str()[2], "??", 2) == 0)
|
|
|
|
{
|
|
|
|
Entries.push_back(stdstr_f("%08X %02X??", CodeCommand, ConvertXP64ValueHi((uint8_t)strtoul(ValueStr.c_str(), 0, 16))));
|
|
|
|
if (!OptionValue.empty() && OptionValue != "XX??")
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
OptionValue = "XX??";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ConvertOptions = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Entries.push_back(stdstr_f("%08X %04X", CodeCommand, ConvertXP64Value((uint16_t)strtoul(ValueStr.c_str(), 0, 16))));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0xE9000000:
|
|
|
|
CodeCommand = (ConvertXP64Address(CodeCommand) & 0xFFFFFF) | 0x81000000;
|
|
|
|
if (strchr(ValueStr.c_str(), '?') != NULL)
|
|
|
|
{
|
|
|
|
if (strncmp(ValueStr.c_str(), "????", 4) == 0)
|
|
|
|
{
|
|
|
|
Entries.push_back(stdstr_f("%08X ????", CodeCommand));
|
|
|
|
if (!OptionValue.empty() && OptionValue != "????")
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
OptionValue = "????";
|
|
|
|
}
|
|
|
|
else if (ValueStr.length() == 4 && strncmp(&ValueStr.c_str()[2], "??", 2) == 0)
|
|
|
|
{
|
|
|
|
Entries.push_back(stdstr_f("%08X %02X??", CodeCommand, ConvertXP64ValueHi((uint8_t)strtoul(ValueStr.c_str(), 0, 16))));
|
|
|
|
if (!OptionValue.empty() && OptionValue != "XX??")
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
OptionValue = "XX??";
|
|
|
|
}
|
|
|
|
else if (ValueStr.length() == 4 && strncmp(&ValueStr.c_str()[0], "??", 2) == 0)
|
|
|
|
{
|
|
|
|
Entries.push_back(stdstr_f("%08X ??%02X", CodeCommand, ConvertXP64ValueLo((uint8_t)strtoul(&(ValueStr.c_str()[2]), 0, 16))));
|
|
|
|
if (!OptionValue.empty() && OptionValue != "??XX")
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
OptionValue = "??XX";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ConvertOptions = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Entries.push_back(stdstr_f("%08X %04X", CodeCommand, ConvertXP64Value((uint16_t)strtoul(ValueStr.c_str(), 0, 16))));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x10000000:
|
|
|
|
Entries.push_back(stdstr_f("%08X %s", (CodeCommand & 0xFFFFFF) | 0x80000000, ValueStr.c_str()));
|
|
|
|
if (strchr(ValueStr.c_str(), '?') != NULL)
|
|
|
|
{
|
|
|
|
if (strncmp(ValueStr.c_str(), "????", 4) == 0)
|
|
|
|
{
|
|
|
|
if (!OptionValue.empty() && OptionValue != "????")
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
OptionValue = "????";
|
|
|
|
}
|
|
|
|
else if (ValueStr.length() == 4 && strncmp(&ValueStr.c_str()[2], "??", 2) == 0)
|
|
|
|
{
|
|
|
|
if (!OptionValue.empty() && OptionValue != "XX??")
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
OptionValue = "XX??";
|
|
|
|
}
|
|
|
|
else if (ValueStr.length() == 4 && strncmp(&ValueStr.c_str()[0], "??", 2) == 0)
|
|
|
|
{
|
|
|
|
if (!OptionValue.empty() && OptionValue != "??XX")
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
OptionValue = "??XX";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ConvertOptions = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x11000000:
|
|
|
|
Entries.push_back(stdstr_f("%08X %s", (CodeCommand & 0xFFFFFF) | 0x81000000, ValueStr.c_str()));
|
|
|
|
if (strchr(ValueStr.c_str(), '?') != NULL)
|
|
|
|
{
|
|
|
|
if (strncmp(ValueStr.c_str(), "????", 4) == 0)
|
|
|
|
{
|
|
|
|
if (!OptionValue.empty() && OptionValue != "????")
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
OptionValue = "????";
|
|
|
|
}
|
|
|
|
else if (ValueStr.length() == 4 && strncmp(&ValueStr.c_str()[2], "??", 2) == 0)
|
|
|
|
{
|
|
|
|
if (!OptionValue.empty() && OptionValue != "XX??")
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
OptionValue = "XX??";
|
|
|
|
}
|
|
|
|
else if (ValueStr.length() == 4 && strncmp(&ValueStr.c_str()[0], "??", 2) == 0)
|
|
|
|
{
|
|
|
|
if (!OptionValue.empty() && OptionValue != "??XX")
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
OptionValue = "??XX";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ConvertOptions = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x80000000:
|
|
|
|
case 0x81000000:
|
|
|
|
case 0x88000000:
|
|
|
|
case 0x89000000:
|
|
|
|
case 0xA0000000:
|
|
|
|
case 0xA1000000:
|
|
|
|
case 0x50000000:
|
|
|
|
case 0xD0000000:
|
|
|
|
case 0xD1000000:
|
|
|
|
case 0xD2000000:
|
|
|
|
case 0xD3000000:
|
|
|
|
Entries.push_back(stdstr_f("%08X %s", CodeCommand, ValueStr.c_str()));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Name.length() == 0 || Entries.size() == 0)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (ConvertOptions && !ConvertCheatOptions(OptionValue.c_str(), CheatOptions))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
CheatEntry = stdstr_f("\"%s\"", Name.c_str());
|
|
|
|
for (CodeEntries::const_iterator itr = Entries.begin(); itr != Entries.end(); itr++)
|
|
|
|
{
|
|
|
|
CheatEntry += stdstr_f(",%s", itr->c_str());
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void convertGS(const char* Directory)
|
|
|
|
{
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
MaxCheats = 50000,
|
|
|
|
};
|
|
|
|
|
|
|
|
CPath SearchDir(Directory, "*.cht");
|
|
|
|
if (SearchDir.FindFirst())
|
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
|
|
|
CIniFile CheatFile(SearchDir);
|
|
|
|
CIniFile::SectionList Sections;
|
|
|
|
CheatFile.GetVectorOfSections(Sections);
|
|
|
|
CheatFile.SetCustomSort(CustomSortData);
|
|
|
|
for (size_t i = 0, n = Sections.size(); i < n; i++)
|
|
|
|
{
|
|
|
|
const char * Section = Sections[i].c_str();
|
|
|
|
for (uint32_t cheat = 0; cheat < MaxCheats; cheat++)
|
|
|
|
{
|
|
|
|
std::string CheatEntry = CheatFile.GetString(Section, stdstr_f("Cheat%d", cheat).c_str(), "");
|
|
|
|
if (CheatEntry.empty())
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
std::string CheatOptions = CheatFile.GetString(Section, stdstr_f("Cheat%d_O", cheat).c_str(), "");
|
|
|
|
if (ConvertCheatEntry(CheatEntry, CheatOptions))
|
|
|
|
{
|
|
|
|
CheatFile.SaveString(Section, stdstr_f("Cheat%d", cheat).c_str(), CheatEntry.c_str());
|
|
|
|
if (!CheatOptions.empty())
|
|
|
|
{
|
|
|
|
CheatFile.SaveString(Section, stdstr_f("Cheat%d_O", cheat).c_str(), CheatOptions.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (SearchDir.FindNext());
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-06-09 09:57:52 +00:00
|
|
|
bool ParseCheatEntry(const stdstr & CheatEntry, const stdstr& CheatOptions, CCheatDetails & Details)
|
|
|
|
{
|
|
|
|
bool HaveOptions;
|
|
|
|
|
|
|
|
size_t StartOfName = CheatEntry.find("\"");
|
|
|
|
if (StartOfName == std::string::npos)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
size_t EndOfName = CheatEntry.find("\"", StartOfName + 1);
|
|
|
|
if (EndOfName == std::string::npos)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
Details.SetName(CheatEntry.substr(StartOfName + 1, EndOfName - StartOfName - 1).c_str());
|
|
|
|
const char * CheatString = &CheatEntry.c_str()[EndOfName + 2];
|
|
|
|
HaveOptions = false;
|
|
|
|
|
|
|
|
const char * ReadPos = CheatString;
|
|
|
|
bool FirstEntry = true;
|
|
|
|
CCheatDetails::CodeEntries Entries;
|
|
|
|
CCheatDetails::CodeOptions Options;
|
|
|
|
std::string OptionValue;
|
|
|
|
|
|
|
|
while (ReadPos)
|
|
|
|
{
|
|
|
|
uint32_t CodeCommand = strtoul(ReadPos, 0, 16);
|
|
|
|
ReadPos = strchr(ReadPos, ' ');
|
|
|
|
if (ReadPos == NULL)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ReadPos += 1;
|
|
|
|
std::string ValueStr = ReadPos;
|
|
|
|
const char * ValuePos = ReadPos;
|
|
|
|
ReadPos = strchr(ReadPos, ',');
|
|
|
|
if (ReadPos != NULL)
|
|
|
|
{
|
|
|
|
ValueStr.resize(ReadPos - ValuePos);
|
|
|
|
ReadPos++;
|
|
|
|
}
|
|
|
|
|
|
|
|
//validate Code Entry
|
|
|
|
switch (CodeCommand & 0xFF000000)
|
|
|
|
{
|
|
|
|
case 0x80000000:
|
|
|
|
case 0x81000000:
|
|
|
|
case 0x88000000:
|
|
|
|
case 0x89000000:
|
|
|
|
case 0xA0000000:
|
|
|
|
case 0xA1000000:
|
|
|
|
case 0xD0000000:
|
|
|
|
case 0xD1000000:
|
|
|
|
case 0xD2000000:
|
|
|
|
case 0xD3000000:
|
|
|
|
if (strchr(ValueStr.c_str(), '?') != NULL)
|
|
|
|
{
|
|
|
|
if (CheatOptions.empty())
|
|
|
|
{
|
|
|
|
DebugBreak();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp(ValueStr.c_str(), "????", 4) == 0)
|
|
|
|
{
|
|
|
|
if (!OptionValue.empty() && OptionValue != "????")
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
OptionValue = "????";
|
|
|
|
}
|
|
|
|
else if (ValueStr.length() == 4 && strncmp(&ValueStr.c_str()[2], "??", 2) == 0)
|
|
|
|
{
|
|
|
|
if (!OptionValue.empty() && OptionValue != "XX??")
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
OptionValue = "XX??";
|
|
|
|
}
|
|
|
|
else if (ValueStr.length() == 4 && strncmp(&ValueStr.c_str()[0], "??", 2) == 0)
|
|
|
|
{
|
|
|
|
if (!OptionValue.empty() && OptionValue != "??XX")
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
OptionValue = "??XX";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x50000000:
|
|
|
|
if (strchr(ValueStr.c_str(), '?') != NULL)
|
|
|
|
{
|
|
|
|
DebugBreak();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
DebugBreak();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
CCheatDetails::CodeEntry Entry;
|
|
|
|
Entry.Command = CodeCommand;
|
|
|
|
Entry.Value = ValueStr;
|
|
|
|
Entries.push_back(Entry);
|
|
|
|
|
|
|
|
FirstEntry = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!OptionValue.empty())
|
|
|
|
{
|
|
|
|
uint32_t OptionLen = OptionValue == "????" ? 4 : 2;
|
|
|
|
ReadPos = strchr(CheatOptions.c_str(), '$');
|
|
|
|
if (ReadPos)
|
|
|
|
{
|
|
|
|
ReadPos += 1;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
const char* End = strchr(ReadPos, ',');
|
|
|
|
std::string Item = End != NULL ? std::string(ReadPos, End - ReadPos) : ReadPos;
|
|
|
|
ReadPos = strchr(ReadPos, '$');
|
|
|
|
if (ReadPos != NULL)
|
|
|
|
{
|
|
|
|
ReadPos += 1;
|
|
|
|
}
|
|
|
|
const char* Name = strchr(Item.c_str(), ' ');
|
|
|
|
if (Name == NULL)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (((uint32_t)(Name - Item.c_str())) != OptionLen)
|
|
|
|
{
|
|
|
|
DebugBreak();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
Name += 1;
|
|
|
|
CCheatDetails::CodeOption Option;
|
|
|
|
Option.Name = stdstr(Name).Trim().c_str();
|
|
|
|
if (OptionValue == "????")
|
|
|
|
{
|
|
|
|
Option.Value = (uint16_t)strtoul(Item.c_str(), 0, 16);
|
|
|
|
}
|
|
|
|
else if (OptionValue == "??XX" || OptionValue == "XX??")
|
|
|
|
{
|
|
|
|
Option.Value = (uint8_t)strtoul(Item.c_str(), 0, 16);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DebugBreak();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
Options.push_back(Option);
|
|
|
|
} while (ReadPos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!Details.SetEntries(Entries))
|
|
|
|
{
|
|
|
|
DebugBreak();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
Details.SetOptions(Options);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ConvertNew(const char * Src, const char * Dest)
|
|
|
|
{
|
|
|
|
CPath SrcDir(Src, ""), DestDir(Dest, "");
|
|
|
|
if (SrcDir == DestDir)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (DestDir.DirectoryExists())
|
|
|
|
{
|
|
|
|
DestDir.Delete();
|
|
|
|
};
|
|
|
|
DestDir.DirectoryCreate();
|
|
|
|
|
|
|
|
CPath SearchDir(Src, "*.cht");
|
|
|
|
if (SearchDir.FindFirst())
|
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
|
|
|
CPath OutFile(Dest, SearchDir.GetNameExtension());
|
|
|
|
if (OutFile.Exists())
|
|
|
|
{
|
|
|
|
OutFile.Delete();
|
|
|
|
}
|
|
|
|
CCheatFile DstCheatFile(OutFile);
|
|
|
|
|
|
|
|
CIniFile SrcIniFile(SearchDir);
|
|
|
|
CIniFile::SectionList Sections;
|
|
|
|
SrcIniFile.GetVectorOfSections(Sections);
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
MaxCheats = 50000,
|
|
|
|
};
|
|
|
|
|
|
|
|
for (size_t i = 0, n = Sections.size(); i < n; i++)
|
|
|
|
{
|
|
|
|
const char * Section = Sections[i].c_str();
|
|
|
|
std::string GameName = SrcIniFile.GetString(Section, "Name", "");
|
|
|
|
DstCheatFile.SetName(Section, GameName.c_str());
|
|
|
|
for (uint32_t cheat = 0; cheat < MaxCheats; cheat++)
|
|
|
|
{
|
|
|
|
std::string CheatEntry = SrcIniFile.GetString(Section, stdstr_f("Cheat%d", cheat).c_str(), "");
|
|
|
|
if (CheatEntry.empty())
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
std::string CheatOptions = SrcIniFile.GetString(Section, stdstr_f("Cheat%d_O", cheat).c_str(), "");
|
|
|
|
CCheatDetails Details;
|
|
|
|
if (!ParseCheatEntry(CheatEntry, CheatOptions, Details))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
std::string CheatNote = SrcIniFile.GetString(Section, stdstr_f("Cheat%d_N", cheat).c_str(), "");
|
|
|
|
if (!CheatNote.empty())
|
|
|
|
{
|
|
|
|
Details.SetNote(CheatNote.c_str());
|
|
|
|
}
|
|
|
|
DstCheatFile.SetCode(Section, Details);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (SearchDir.FindNext());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-18 07:27:20 +00:00
|
|
|
int WINAPI WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpszArgs*/, int /*nWinMode*/)
|
|
|
|
{
|
|
|
|
if (__argc == 4 && strcmp(__argv[1], "-split") == 0 && CPath(__argv[2]).Exists())
|
|
|
|
{
|
|
|
|
SplitFile(__argv[2], __argv[3]);
|
|
|
|
}
|
|
|
|
if (__argc == 4 && strcmp(__argv[1], "-join") == 0 && CPath(__argv[2],"").DirectoryExists())
|
|
|
|
{
|
|
|
|
JoinFile(__argv[2], __argv[3]);
|
|
|
|
}
|
2020-06-01 22:49:07 +00:00
|
|
|
if (__argc == 4 && strcmp(__argv[1], "-updateNames") == 0 && CPath(__argv[2], "").DirectoryExists() && CPath(__argv[3]).Exists())
|
|
|
|
{
|
|
|
|
UpdateNames(__argv[2], __argv[3]);
|
|
|
|
}
|
2020-06-02 08:35:57 +00:00
|
|
|
if (__argc == 3 && strcmp(__argv[1], "-convertGS") == 0 && CPath(__argv[2], "").DirectoryExists())
|
|
|
|
{
|
|
|
|
convertGS(__argv[2]);
|
|
|
|
}
|
2020-06-09 09:57:52 +00:00
|
|
|
if (__argc == 4 && strcmp(__argv[1], "-convertNew") == 0 && CPath(__argv[2], "").DirectoryExists())
|
|
|
|
{
|
|
|
|
ConvertNew(__argv[2], __argv[3]);
|
|
|
|
}
|
2020-06-01 22:49:07 +00:00
|
|
|
return 0;
|
2019-04-18 07:27:20 +00:00
|
|
|
}
|