Add ability to translate cheats to new format
This commit is contained in:
parent
b2184fbeef
commit
1b00f9a8f6
|
@ -2,7 +2,6 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
|
||||||
CIniFileBase::CIniFileBase(CFileBase & FileObject, const char * FileName) :
|
CIniFileBase::CIniFileBase(CFileBase & FileObject, const char * FileName) :
|
||||||
m_lastSectionSearch(0),
|
m_lastSectionSearch(0),
|
||||||
m_CurrentSectionFilePos(0),
|
m_CurrentSectionFilePos(0),
|
||||||
|
|
|
@ -51,6 +51,9 @@
|
||||||
<ProjectReference Include="..\Common\Common.vcxproj">
|
<ProjectReference Include="..\Common\Common.vcxproj">
|
||||||
<Project>{b4a4b994-9111-42b1-93c2-6f1ca8bc4421}</Project>
|
<Project>{b4a4b994-9111-42b1-93c2-6f1ca8bc4421}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\Project64-core\Project64-core.vcxproj">
|
||||||
|
<Project>{00c7b43a-ded7-4df0-b072-9a5783ef866d}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
</Project>
|
</Project>
|
|
@ -1,6 +1,7 @@
|
||||||
#include <Common/path.h>
|
#include <Common\path.h>
|
||||||
#include <Common/IniFileClass.h>
|
#include <Common\IniFileClass.h>
|
||||||
#include <Common/StdString.h>
|
#include <Common\StdString.h>
|
||||||
|
#include <Project64-core\N64System\CheatFile.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
@ -207,9 +208,9 @@ void JoinFile(const char * Directory, const char * Target)
|
||||||
if (Name.size() > 0)
|
if (Name.size() > 0)
|
||||||
{
|
{
|
||||||
files.insert(Files::value_type(Name, SearchDir));
|
files.insert(Files::value_type(Name, SearchDir));
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} while (SearchDir.FindNext());
|
} while (SearchDir.FindNext());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -354,17 +355,17 @@ bool ConvertCheatOptions(const char * OptionValue, std::string& Options)
|
||||||
Name += 1;
|
Name += 1;
|
||||||
if (strcmp(OptionValue, "????") == 0)
|
if (strcmp(OptionValue, "????") == 0)
|
||||||
{
|
{
|
||||||
uint16_t CodeValue = ConvertXP64Value(strtoul(Item.c_str(), 0, 16));
|
uint16_t CodeValue = ConvertXP64Value((uint16_t)strtoul(Item.c_str(), 0, 16));
|
||||||
NewOptions += stdstr_f("%04X %s", CodeValue, stdstr(Name).Trim().c_str());
|
NewOptions += stdstr_f("%04X %s", CodeValue, stdstr(Name).Trim().c_str());
|
||||||
}
|
}
|
||||||
else if (strcmp(OptionValue, "??XX") == 0)
|
else if (strcmp(OptionValue, "??XX") == 0)
|
||||||
{
|
{
|
||||||
uint8_t CodeValue = ConvertXP64ValueHi(strtoul(Item.c_str(), 0, 16));
|
uint8_t CodeValue = ConvertXP64ValueHi((uint8_t)strtoul(Item.c_str(), 0, 16));
|
||||||
NewOptions += stdstr_f("%02X %s", CodeValue, stdstr(Name).Trim().c_str());
|
NewOptions += stdstr_f("%02X %s", CodeValue, stdstr(Name).Trim().c_str());
|
||||||
}
|
}
|
||||||
else if (strcmp(OptionValue, "XX??") == 0)
|
else if (strcmp(OptionValue, "XX??") == 0)
|
||||||
{
|
{
|
||||||
uint8_t CodeValue = ConvertXP64ValueLo(strtoul(Item.c_str(), 0, 16));
|
uint8_t CodeValue = ConvertXP64ValueLo((uint8_t)strtoul(Item.c_str(), 0, 16));
|
||||||
NewOptions += stdstr_f("%02X %s", CodeValue, stdstr(Name).Trim().c_str());
|
NewOptions += stdstr_f("%02X %s", CodeValue, stdstr(Name).Trim().c_str());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -641,6 +642,240 @@ void convertGS(const char* Directory)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int WINAPI WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpszArgs*/, int /*nWinMode*/)
|
int WINAPI WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpszArgs*/, int /*nWinMode*/)
|
||||||
{
|
{
|
||||||
if (__argc == 4 && strcmp(__argv[1], "-split") == 0 && CPath(__argv[2]).Exists())
|
if (__argc == 4 && strcmp(__argv[1], "-split") == 0 && CPath(__argv[2]).Exists())
|
||||||
|
@ -659,5 +894,9 @@ int WINAPI WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /
|
||||||
{
|
{
|
||||||
convertGS(__argv[2]);
|
convertGS(__argv[2]);
|
||||||
}
|
}
|
||||||
|
if (__argc == 4 && strcmp(__argv[1], "-convertNew") == 0 && CPath(__argv[2], "").DirectoryExists())
|
||||||
|
{
|
||||||
|
ConvertNew(__argv[2], __argv[3]);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,508 @@
|
||||||
|
/****************************************************************************
|
||||||
|
* *
|
||||||
|
* Project64 - A Nintendo 64 emulator. *
|
||||||
|
* http://www.pj64-emu.com/ *
|
||||||
|
* Copyright (C) 2012 Project64. All rights reserved. *
|
||||||
|
* *
|
||||||
|
* License: *
|
||||||
|
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
|
||||||
|
* *
|
||||||
|
****************************************************************************/
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include <Project64-core\N64System\CheatFile.h>
|
||||||
|
|
||||||
|
#include <Windows.h> // for DebugBreak should be removed
|
||||||
|
|
||||||
|
CCheatDetails::CCheatDetails() :
|
||||||
|
m_CodeOptionSize(0),
|
||||||
|
m_Valid(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCheatDetails::SetName(const char * Name)
|
||||||
|
{
|
||||||
|
m_Name = Name != NULL ? Name : "";
|
||||||
|
m_Name = stdstr(m_Name).Trim("\t ,");
|
||||||
|
CheckValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCheatDetails::SetNote(const char * Note)
|
||||||
|
{
|
||||||
|
m_Note = Note != NULL ? Note : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CCheatDetails::SetEntries(const CodeEntries & Entries)
|
||||||
|
{
|
||||||
|
std::string OptionValue;
|
||||||
|
uint32_t CodeOptionSize = 0;
|
||||||
|
for (CodeEntries::const_iterator itr = Entries.begin(); itr != Entries.end(); itr++)
|
||||||
|
{
|
||||||
|
switch (itr->Command & 0xFF000000)
|
||||||
|
{
|
||||||
|
case 0x80000000:
|
||||||
|
case 0x81000000:
|
||||||
|
case 0x88000000:
|
||||||
|
case 0x89000000:
|
||||||
|
case 0xA0000000:
|
||||||
|
case 0xA1000000:
|
||||||
|
case 0xD0000000:
|
||||||
|
case 0xD1000000:
|
||||||
|
case 0xD2000000:
|
||||||
|
case 0xD3000000:
|
||||||
|
if (strchr(itr->Value.c_str(), '?') != NULL)
|
||||||
|
{
|
||||||
|
if (strncmp(itr->Value.c_str(), "????", 4) == 0)
|
||||||
|
{
|
||||||
|
if (!OptionValue.empty() && OptionValue != "????")
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
OptionValue = "????";
|
||||||
|
CodeOptionSize = 4;
|
||||||
|
}
|
||||||
|
else if (itr->Value.length() == 4 && strncmp(&itr->Value.c_str()[2], "??", 2) == 0)
|
||||||
|
{
|
||||||
|
if (!OptionValue.empty() && OptionValue != "XX??")
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
OptionValue = "XX??";
|
||||||
|
CodeOptionSize = 2;
|
||||||
|
}
|
||||||
|
else if (itr->Value.length() == 4 && strncmp(&itr->Value.c_str()[0], "??", 2) == 0)
|
||||||
|
{
|
||||||
|
if (!OptionValue.empty() && OptionValue != "??XX")
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
OptionValue = "??XX";
|
||||||
|
CodeOptionSize = 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x50000000:
|
||||||
|
if (strchr(itr->Value.c_str(), '?') != NULL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_Entries = Entries;
|
||||||
|
m_OptionValue = OptionValue;
|
||||||
|
m_CodeOptionSize = CodeOptionSize;
|
||||||
|
CheckValid();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCheatDetails::SetOptions(const CodeOptions & Options)
|
||||||
|
{
|
||||||
|
m_Options = Options;
|
||||||
|
CheckValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCheatDetails::CheckValid(void)
|
||||||
|
{
|
||||||
|
m_Valid = false;
|
||||||
|
if (m_Name.empty() || m_Entries.size() == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_Options.size() > 0 && m_OptionValue.empty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_Valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CCheatFile::CCheatFile(const char * FileName) :
|
||||||
|
m_lastSectionSearch(0),
|
||||||
|
m_CurrentSectionFilePos(0),
|
||||||
|
m_CurrentSectionDirty(false),
|
||||||
|
m_ReadOnly(false),
|
||||||
|
m_FileName(FileName),
|
||||||
|
m_LineFeed("\r\n")
|
||||||
|
{
|
||||||
|
OpenCheatFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
CCheatFile::~CCheatFile()
|
||||||
|
{
|
||||||
|
SaveCurrentSection();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCheatFile::SetName(const char * Section, const char * Name)
|
||||||
|
{
|
||||||
|
CGuard Guard(m_CS);
|
||||||
|
if (!m_File.IsOpen())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!MoveToSection(Section, true))
|
||||||
|
{
|
||||||
|
m_CurrentSection = Section;
|
||||||
|
m_CurrentSectionData.clear();
|
||||||
|
m_CurrentSectionFilePos = -1;
|
||||||
|
}
|
||||||
|
if (strcmp(m_SectionName.c_str(), Name) != 0)
|
||||||
|
{
|
||||||
|
m_SectionName = Name;
|
||||||
|
m_CurrentSectionDirty = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCheatFile::SetCode(const char * Section, const CCheatDetails & Details)
|
||||||
|
{
|
||||||
|
CGuard Guard(m_CS);
|
||||||
|
if (!m_File.IsOpen() || m_ReadOnly)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_CurrentSectionDirty = true;
|
||||||
|
if (!MoveToSection(Section, true))
|
||||||
|
{
|
||||||
|
m_CurrentSection = Section;
|
||||||
|
m_CurrentSectionData.clear();
|
||||||
|
m_CurrentSectionFilePos = -1;
|
||||||
|
}
|
||||||
|
KeyValueList::iterator itr = m_CurrentSectionData.find(Details.GetName());
|
||||||
|
if (itr != m_CurrentSectionData.end())
|
||||||
|
{
|
||||||
|
itr->second = Details;
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_CurrentSectionData.insert(KeyValueList::value_type(Details.GetName(), Details));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCheatFile::OpenCheatFile(void)
|
||||||
|
{
|
||||||
|
m_ReadOnly = false;
|
||||||
|
if (!m_File.Open(m_FileName.c_str(), CFileBase::modeReadWrite | CFileBase::shareDenyWrite))
|
||||||
|
{
|
||||||
|
if (!m_File.Open(m_FileName.c_str(), CFileBase::modeRead))
|
||||||
|
{
|
||||||
|
if (!m_File.Open(m_FileName.c_str(), CFileBase::modeReadWrite | CFileBase::modeCreate | CFileBase::shareDenyWrite))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_ReadOnly = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_File.Seek(0, CFileBase::begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CCheatFile::MoveToSection(const char * lpSectionName, bool ChangeCurrentSection)
|
||||||
|
{
|
||||||
|
if (strcmp(lpSectionName, m_CurrentSection.c_str()) == 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (ChangeCurrentSection)
|
||||||
|
{
|
||||||
|
SaveCurrentSection();
|
||||||
|
m_CurrentSection = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<char> Data;
|
||||||
|
char *Input = NULL;
|
||||||
|
int MaxDataSize = 0, DataSize = 0, ReadPos = 0, result;
|
||||||
|
|
||||||
|
FILELOC_ITR iter = m_SectionsPos.find(std::string(lpSectionName));
|
||||||
|
bool bFoundSection = false;
|
||||||
|
if (iter != m_SectionsPos.end())
|
||||||
|
{
|
||||||
|
if (ChangeCurrentSection)
|
||||||
|
{
|
||||||
|
m_CurrentSection = iter->first;
|
||||||
|
m_CurrentSectionFilePos = iter->second;
|
||||||
|
}
|
||||||
|
m_File.Seek(iter->second, CFileBase::begin);
|
||||||
|
bFoundSection = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_File.Seek(m_lastSectionSearch, CFileBase::begin);
|
||||||
|
|
||||||
|
//long Fpos;
|
||||||
|
uint8_t pUTF8[3];
|
||||||
|
pUTF8[0] = 0xef;
|
||||||
|
pUTF8[1] = 0xbb;
|
||||||
|
pUTF8[2] = 0xbf;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
result = GetStringFromFile(Input, Data, MaxDataSize, DataSize, ReadPos);
|
||||||
|
if (result <= 1) { continue; }
|
||||||
|
if (strlen(CleanLine(Input)) <= 1) { continue; }
|
||||||
|
|
||||||
|
//We Only care about sections
|
||||||
|
char * CurrentSection = Input;
|
||||||
|
|
||||||
|
if (m_lastSectionSearch == 0 && !memcmp(CurrentSection, pUTF8, 3))
|
||||||
|
{
|
||||||
|
CurrentSection += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CurrentSection[0] != '[') { continue; }
|
||||||
|
int lineEndPos = (int)strlen(CurrentSection) - 1;
|
||||||
|
if (CurrentSection[lineEndPos] != ']') { continue; }
|
||||||
|
//take off the ']' from the end of the string
|
||||||
|
CurrentSection[lineEndPos] = 0;
|
||||||
|
CurrentSection += 1;
|
||||||
|
m_lastSectionSearch = (m_File.GetPosition() - DataSize) + ReadPos;
|
||||||
|
m_SectionsPos.insert(FILELOC::value_type(CurrentSection, m_lastSectionSearch));
|
||||||
|
|
||||||
|
if (_stricmp(lpSectionName, CurrentSection) != 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ChangeCurrentSection)
|
||||||
|
{
|
||||||
|
m_CurrentSection = lpSectionName;
|
||||||
|
m_CurrentSectionFilePos = m_lastSectionSearch;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_File.Seek(m_lastSectionSearch, CFileBase::begin);
|
||||||
|
}
|
||||||
|
bFoundSection = true;
|
||||||
|
break;
|
||||||
|
} while (result >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bFoundSection && strcmp(lpSectionName, "default") == 0)
|
||||||
|
{
|
||||||
|
m_SectionsPos.insert(FILELOC::value_type(lpSectionName, 0));
|
||||||
|
if (ChangeCurrentSection)
|
||||||
|
{
|
||||||
|
m_CurrentSection = lpSectionName;
|
||||||
|
m_CurrentSectionFilePos = 0;
|
||||||
|
}
|
||||||
|
m_File.Seek(m_lastSectionSearch, CFileBase::begin);
|
||||||
|
bFoundSection = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bFoundSection && ChangeCurrentSection)
|
||||||
|
{
|
||||||
|
m_CurrentSectionData.clear();
|
||||||
|
/*do
|
||||||
|
{
|
||||||
|
result = GetStringFromFile(Input, Data, MaxDataSize, DataSize, ReadPos);
|
||||||
|
if (result <= 1) { continue; }
|
||||||
|
if (strlen(CleanLine(Input)) <= 1) { continue; }
|
||||||
|
if (Input[0] == '[') { break; }
|
||||||
|
char * Pos = strchr(Input, '=');
|
||||||
|
if (Pos == NULL) { continue; }
|
||||||
|
char * Value = &Pos[1];
|
||||||
|
|
||||||
|
char * Pos1 = Pos - 1;
|
||||||
|
while (((*Pos1 == ' ') || (*Pos1 == '\t')) && (Pos1 > Input))
|
||||||
|
{
|
||||||
|
Pos1--;
|
||||||
|
}
|
||||||
|
Pos1[1] = 0;
|
||||||
|
|
||||||
|
m_CurrentSectionData.insert(KeyValueList::value_type(Input, Value));
|
||||||
|
} while (result >= 0);*/
|
||||||
|
}
|
||||||
|
return bFoundSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCheatFile::SaveCurrentSection(void)
|
||||||
|
{
|
||||||
|
if (!m_CurrentSectionDirty)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_CurrentSectionDirty = false;
|
||||||
|
|
||||||
|
std::string Section;
|
||||||
|
if (!m_SectionName.empty())
|
||||||
|
{
|
||||||
|
Section = stdstr_f("Name=%s%s%s", m_SectionName.c_str(), m_LineFeed, m_LineFeed);
|
||||||
|
}
|
||||||
|
for (KeyValueList::iterator iter = m_CurrentSectionData.begin(); iter != m_CurrentSectionData.end(); iter++)
|
||||||
|
{
|
||||||
|
CCheatDetails & details = iter->second;
|
||||||
|
if (details.GetName().empty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Section += stdstr_f("$%s%s", details.GetName().c_str(), m_LineFeed);
|
||||||
|
if (!details.GetNote().empty())
|
||||||
|
{
|
||||||
|
Section += stdstr_f("Note=%s%s", details.GetNote().c_str(), m_LineFeed);
|
||||||
|
}
|
||||||
|
const CCheatDetails::CodeEntries & Entries = details.GetEntries();
|
||||||
|
for (size_t i = 0, n = Entries.size(); i < n; i++)
|
||||||
|
{
|
||||||
|
Section += stdstr_f("%08X %s%s", Entries[i].Command, Entries[i].Value.c_str(), m_LineFeed);
|
||||||
|
}
|
||||||
|
const CCheatDetails::CodeOptions & Options = details.GetOptions();
|
||||||
|
for (size_t i = 0, n = Options.size(); i < n; i++)
|
||||||
|
{
|
||||||
|
if (details.CodeOptionSize() == 4)
|
||||||
|
{
|
||||||
|
Section += stdstr_f("%04X %s%s", Options[i].Value, Options[i].Name.c_str(), m_LineFeed);
|
||||||
|
}
|
||||||
|
else if (details.CodeOptionSize() == 2)
|
||||||
|
{
|
||||||
|
Section += stdstr_f("%02X %s%s", Options[i].Value, Options[i].Name.c_str(), m_LineFeed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Section += m_LineFeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lineFeedLen = (int)strlen(m_LineFeed);
|
||||||
|
if (m_CurrentSectionFilePos == -1)
|
||||||
|
{
|
||||||
|
m_File.Seek(0, CFileBase::end);
|
||||||
|
|
||||||
|
int len = (int)m_CurrentSection.length() + (lineFeedLen * 2) + 5;
|
||||||
|
std::unique_ptr<char> SectionName(new char[len]);
|
||||||
|
if (m_File.GetLength() < (int)strlen(m_LineFeed))
|
||||||
|
{
|
||||||
|
sprintf(SectionName.get(), "[%s]%s", m_CurrentSection.c_str(), m_LineFeed);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sprintf(SectionName.get(), "%s[%s]%s", m_LineFeed, m_CurrentSection.c_str(), m_LineFeed);
|
||||||
|
}
|
||||||
|
m_File.Write(SectionName.get(), (int)strlen(SectionName.get()));
|
||||||
|
m_CurrentSectionFilePos = m_File.GetPosition();
|
||||||
|
m_SectionsPos.insert(FILELOC::value_type(m_CurrentSection, m_CurrentSectionFilePos));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DebugBreak();
|
||||||
|
}
|
||||||
|
m_File.Write(Section.data(), Section.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
int CCheatFile::GetStringFromFile(char * & String, std::unique_ptr<char> &Data, int & MaxDataSize, int & DataSize, int & ReadPos)
|
||||||
|
{
|
||||||
|
enum { BufferIncrease = 0x2000 };
|
||||||
|
if (MaxDataSize == 0)
|
||||||
|
{
|
||||||
|
ReadPos = 0;
|
||||||
|
MaxDataSize = BufferIncrease;
|
||||||
|
Data.reset(new char[MaxDataSize]);
|
||||||
|
DataSize = m_File.Read(&Data.get()[DataSize], MaxDataSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
|
||||||
|
for (count = ReadPos; count < DataSize; count++)
|
||||||
|
{
|
||||||
|
if (Data.get()[count] == '\n')
|
||||||
|
{
|
||||||
|
int len = (count - ReadPos) + 1;
|
||||||
|
String = &Data.get()[ReadPos];
|
||||||
|
String[len - 1] = 0;
|
||||||
|
ReadPos = count + 1;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ReadPos != 0)
|
||||||
|
{
|
||||||
|
if ((DataSize - ReadPos) > 0)
|
||||||
|
{
|
||||||
|
memmove(Data.get(), &Data.get()[ReadPos], DataSize - ReadPos);
|
||||||
|
}
|
||||||
|
DataSize -= ReadPos;
|
||||||
|
ReadPos = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//Increase buffer size
|
||||||
|
int NewMaxDataSize = MaxDataSize + BufferIncrease;
|
||||||
|
char * NewBuffer = new char[NewMaxDataSize];
|
||||||
|
if (NewBuffer == NULL)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(NewBuffer, Data.get(), DataSize);
|
||||||
|
MaxDataSize = NewMaxDataSize;
|
||||||
|
Data.reset(NewBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
int dwRead = m_File.Read(&Data.get()[DataSize], MaxDataSize - DataSize);
|
||||||
|
if (dwRead == 0)
|
||||||
|
{
|
||||||
|
if (DataSize > 0)
|
||||||
|
{
|
||||||
|
int len = DataSize + 1;
|
||||||
|
String = &Data.get()[ReadPos];
|
||||||
|
String[len - 1] = 0;
|
||||||
|
DataSize = 0;
|
||||||
|
ReadPos = 0;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
DataSize += dwRead;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * CCheatFile::CleanLine(char * Line)
|
||||||
|
{
|
||||||
|
char * Pos = Line;
|
||||||
|
|
||||||
|
//Remove any comment from the line
|
||||||
|
while (Pos != NULL)
|
||||||
|
{
|
||||||
|
Pos = strchr(Pos, '/');
|
||||||
|
if (Pos != NULL)
|
||||||
|
{
|
||||||
|
if (Pos[1] == '/')
|
||||||
|
{
|
||||||
|
if (Pos > Line)
|
||||||
|
{
|
||||||
|
char * Pos_1 = Pos - 1;
|
||||||
|
|
||||||
|
if (Pos_1[0] != ':')
|
||||||
|
{
|
||||||
|
Pos[0] = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Pos += 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Pos[0] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Pos += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//strip any spaces or line feeds from the end of the line
|
||||||
|
for (int count = (int)strlen(&Line[0]) - 1; count >= 0; count--)
|
||||||
|
{
|
||||||
|
if (Line[count] != ' ' && Line[count] != '\r') { break; }
|
||||||
|
Line[count] = 0;
|
||||||
|
}
|
||||||
|
return Line;
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
/****************************************************************************
|
||||||
|
* *
|
||||||
|
* Project64 - A Nintendo 64 emulator. *
|
||||||
|
* http://www.pj64-emu.com/ *
|
||||||
|
* Copyright (C) 2012 Project64. All rights reserved. *
|
||||||
|
* *
|
||||||
|
* License: *
|
||||||
|
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
|
||||||
|
* *
|
||||||
|
****************************************************************************/
|
||||||
|
#pragma once
|
||||||
|
#include <Common\FileClass.h>
|
||||||
|
#include <Common\CriticalSection.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class CCheatDetails
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct CodeEntry
|
||||||
|
{
|
||||||
|
uint32_t Command;
|
||||||
|
std::string Value;
|
||||||
|
};
|
||||||
|
struct CodeOption
|
||||||
|
{
|
||||||
|
std::string Name;
|
||||||
|
uint16_t Value;
|
||||||
|
};
|
||||||
|
typedef std::vector<CodeEntry> CodeEntries;
|
||||||
|
typedef std::vector<CodeOption> CodeOptions;
|
||||||
|
|
||||||
|
CCheatDetails();
|
||||||
|
void SetName(const char * Name);
|
||||||
|
void SetNote(const char * Note);
|
||||||
|
bool SetEntries(const CodeEntries & Entries);
|
||||||
|
void SetOptions(const CodeOptions & Options);
|
||||||
|
|
||||||
|
inline const std::string GetName(void) const { return m_Name; }
|
||||||
|
inline const std::string GetNote(void) const { return m_Note; }
|
||||||
|
inline const CodeEntries & GetEntries(void) const { return m_Entries; }
|
||||||
|
inline const CodeOptions & GetOptions(void) const { return m_Options; }
|
||||||
|
inline uint32_t CodeOptionSize(void) const { return m_CodeOptionSize; }
|
||||||
|
inline bool Valid (void) const { return m_Valid; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void CheckValid();
|
||||||
|
|
||||||
|
std::string m_Name;
|
||||||
|
std::string m_Note;
|
||||||
|
CodeEntries m_Entries;
|
||||||
|
CodeOptions m_Options;
|
||||||
|
std::string m_OptionValue;
|
||||||
|
uint32_t m_CodeOptionSize;
|
||||||
|
bool m_Valid;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CCheatFile
|
||||||
|
{
|
||||||
|
struct insensitive_compare
|
||||||
|
{
|
||||||
|
bool operator() (const std::string & a, const std::string & b) const
|
||||||
|
{
|
||||||
|
return _stricmp(a.c_str(), b.c_str()) < 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::map<std::string, CCheatDetails, insensitive_compare> KeyValueList;
|
||||||
|
typedef std::map<std::string, long> FILELOC;
|
||||||
|
typedef FILELOC::iterator FILELOC_ITR;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CCheatFile(const char * FileName);
|
||||||
|
~CCheatFile();
|
||||||
|
|
||||||
|
void SetName(const char * Section, const char * Name);
|
||||||
|
void SetCode(const char * Section, const CCheatDetails & Details);
|
||||||
|
|
||||||
|
private:
|
||||||
|
CCheatFile();
|
||||||
|
CCheatFile(const CCheatFile&);
|
||||||
|
CCheatFile& operator=(const CCheatFile&);
|
||||||
|
|
||||||
|
void OpenCheatFile(void);
|
||||||
|
bool MoveToSection(const char * lpSectionName, bool ChangeCurrentSection);
|
||||||
|
void SaveCurrentSection(void);
|
||||||
|
int GetStringFromFile(char * & String, std::unique_ptr<char> &Data, int & MaxDataSize, int & DataSize, int & ReadPos);
|
||||||
|
const char * CleanLine(char * Line);
|
||||||
|
|
||||||
|
CriticalSection m_CS;
|
||||||
|
std::string m_CurrentSection;
|
||||||
|
std::string m_SectionName;
|
||||||
|
KeyValueList m_CurrentSectionData;
|
||||||
|
long m_lastSectionSearch;
|
||||||
|
FILELOC m_SectionsPos;
|
||||||
|
int m_CurrentSectionFilePos;
|
||||||
|
bool m_CurrentSectionDirty;
|
||||||
|
bool m_ReadOnly;
|
||||||
|
CFile m_File;
|
||||||
|
std::string m_FileName;
|
||||||
|
const char * m_LineFeed;
|
||||||
|
};
|
|
@ -45,6 +45,7 @@
|
||||||
<ClCompile Include="MemoryExceptionFilter.cpp" />
|
<ClCompile Include="MemoryExceptionFilter.cpp" />
|
||||||
<ClCompile Include="Multilanguage\LanguageClass.cpp" />
|
<ClCompile Include="Multilanguage\LanguageClass.cpp" />
|
||||||
<ClCompile Include="N64System\CheatClass.cpp" />
|
<ClCompile Include="N64System\CheatClass.cpp" />
|
||||||
|
<ClCompile Include="N64System\CheatFile.cpp" />
|
||||||
<ClCompile Include="N64System\EmulationThread.cpp" />
|
<ClCompile Include="N64System\EmulationThread.cpp" />
|
||||||
<ClCompile Include="N64System\FramePerSecondClass.cpp" />
|
<ClCompile Include="N64System\FramePerSecondClass.cpp" />
|
||||||
<ClCompile Include="N64System\Interpreter\InterpreterCPU.cpp" />
|
<ClCompile Include="N64System\Interpreter\InterpreterCPU.cpp" />
|
||||||
|
@ -140,6 +141,7 @@
|
||||||
<ClInclude Include="Multilanguage.h" />
|
<ClInclude Include="Multilanguage.h" />
|
||||||
<ClInclude Include="Multilanguage\LanguageClass.h" />
|
<ClInclude Include="Multilanguage\LanguageClass.h" />
|
||||||
<ClInclude Include="N64System\CheatClass.h" />
|
<ClInclude Include="N64System\CheatClass.h" />
|
||||||
|
<ClInclude Include="N64System\CheatFile.h" />
|
||||||
<ClInclude Include="N64System\FramePerSecondClass.h" />
|
<ClInclude Include="N64System\FramePerSecondClass.h" />
|
||||||
<ClInclude Include="N64System\Interpreter\InterpreterCPU.h" />
|
<ClInclude Include="N64System\Interpreter\InterpreterCPU.h" />
|
||||||
<ClInclude Include="N64System\Interpreter\InterpreterOps32.h" />
|
<ClInclude Include="N64System\Interpreter\InterpreterOps32.h" />
|
||||||
|
|
|
@ -342,6 +342,9 @@
|
||||||
<ClCompile Include="Settings\SettingType\SettingsType-Enhancements.cpp">
|
<ClCompile Include="Settings\SettingType\SettingsType-Enhancements.cpp">
|
||||||
<Filter>Source Files\Settings\SettingType</Filter>
|
<Filter>Source Files\Settings\SettingType</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="N64System\CheatFile.cpp">
|
||||||
|
<Filter>Source Files\N64 System</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="stdafx.h">
|
<ClInclude Include="stdafx.h">
|
||||||
|
@ -662,6 +665,9 @@
|
||||||
<ClInclude Include="Settings\SettingType\SettingsType-Enhancements.h">
|
<ClInclude Include="Settings\SettingType\SettingsType-Enhancements.h">
|
||||||
<Filter>Header Files\Settings\SettingType</Filter>
|
<Filter>Header Files\Settings\SettingType</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="N64System\CheatFile.h">
|
||||||
|
<Filter>Header Files\N64 System</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="ClassDiagram.cd" />
|
<None Include="ClassDiagram.cd" />
|
||||||
|
|
Loading…
Reference in New Issue