add AR code file parser and shit

This commit is contained in:
Arisotura
2020-08-13 00:20:34 +02:00
parent 28b8f614ee
commit 4cefff2528
7 changed files with 248 additions and 154 deletions

176
src/ARCodeFile.cpp Normal file
View File

@ -0,0 +1,176 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS 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 3 of the License, or (at your option)
any later version.
melonDS 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 melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <string.h>
#include "ARCodeFile.h"
#include "Platform.h"
// TODO: import codes from other sources (usrcheat.dat, ...)
// TODO: more user-friendly error reporting
ARCodeFile::ARCodeFile(const char* filename)
{
memset(Filename, 0, sizeof(Filename));
strncpy(Filename, filename, 1023);
Error = false;
Categories.clear();
FILE* f = Platform::OpenFile(Filename, "r");
if (!f) return;
bool isincat = false;
ARCodeCat curcat;
bool isincode = false;
ARCode curcode;
char linebuf[1024];
while (!feof(f))
{
fgets(linebuf, 1024, f);
linebuf[1023] = '\0';
char* start = linebuf;
while (start[0]==' ' || start[0]=='\t')
start++;
if (start[0]=='#' || start[0]=='\r' || start[0]=='\n' || start[0]=='\0')
continue;
if (!strncasecmp(start, "CAT", 3))
{
char catname[128];
int ret = sscanf(start, "CAT %127[^\n]", catname);
catname[127] = '\0';
if (ret < 1)
{
printf("AR: malformed CAT line: %s\n", start);
fclose(f);
Error = true;
return;
}
if (isincode) curcat.Codes.push_back(curcode);
isincode = false;
if (isincat) Categories.push_back(curcat);
isincat = true;
memcpy(curcat.Name, catname, 128);
curcat.Codes.clear();
}
else if (!strncasecmp(start, "CODE", 4))
{
int enable;
char codename[128];
int ret = sscanf(start, "CODE %d %127[^\n]", &enable, codename);
codename[127] = '\0';
if (ret < 2)
{
printf("AR: malformed CODE line: %s\n", start);
fclose(f);
Error = true;
return;
}
if (!isincat)
{
printf("AR: encountered CODE line with no category started\n");
fclose(f);
Error = true;
return;
}
if (isincode) curcat.Codes.push_back(curcode);
isincode = true;
memcpy(curcode.Name, codename, 128);
curcode.Enabled = enable!=0;
curcode.CodeLen = 0;
}
else
{
u32 c0, c1;
int ret = sscanf(start, "%08X %08X", &c0, &c1);
if (ret < 2)
{
printf("AR: malformed data line: %s\n", start);
fclose(f);
Error = true;
return;
}
if (!isincode)
{
printf("AR: encountered data line with no code started\n");
fclose(f);
Error = true;
return;
}
if (curcode.CodeLen >= 2*64)
{
printf("AR: code too long!\n");
fclose(f);
Error = true;
return;
}
u32 idx = curcode.CodeLen;
curcode.Code[idx+0] = c0;
curcode.Code[idx+1] = c1;
curcode.CodeLen += 2;
}
}
if (isincode) curcat.Codes.push_back(curcode);
if (isincat) Categories.push_back(curcat);
fclose(f);
printf("PARSED OUTPUT\n");
for (ARCodeCatList::iterator it = Categories.begin(); it != Categories.end(); it++)
{
ARCodeCat& cat = *it;
printf("CAT %s\n", cat.Name);
for (ARCodeList::iterator jt = cat.Codes.begin(); jt != cat.Codes.end(); jt++)
{
ARCode& code = *jt;
printf("CODE %d %s\n", code.Enabled, code.Name);
for (u32 i = 0; i < code.CodeLen; i+=2)
{
printf("%08X %08X\n", code.Code[i], code.Code[i+1]);
}
}
}
}
ARCodeFile::~ARCodeFile()
{
//
}

View File

@ -16,18 +16,48 @@
with melonDS. If not, see http://www.gnu.org/licenses/. with melonDS. If not, see http://www.gnu.org/licenses/.
*/ */
#ifndef ARCODELIST_H #ifndef ARCODEFILE_H
#define ARCODELIST_H #define ARCODEFILE_H
#include <vector>
#include "types.h" #include "types.h"
#define ARCL_MAJOR 1 typedef struct
#define ARCL_MINOR 1 {
char Name[128];
bool Enabled;
u32 CodeLen;
u32 Code[2*64];
class ARCodeList } ARCode;
typedef std::vector<ARCode> ARCodeList;
typedef struct
{
char Name[128];
ARCodeList Codes;
} ARCodeCat;
typedef std::vector<ARCodeCat> ARCodeCatList;
class ARCodeFile
{ {
public: public:
// ARCodeFile(const char* filename);
~ARCodeFile();
bool Error;
bool Save();
ARCodeCatList Categories;
private:
char Filename[1024];
}; };
#endif // ARCODELIST_H #endif // ARCODEFILE_H

View File

@ -1,38 +0,0 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS 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 3 of the License, or (at your option)
any later version.
melonDS 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 melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include "ARCodeList.h"
/*
Action Replay code list format
header:
00 - magic MLAR
04 - version major
06 - version minor
08 - length
0C - number of codes
code header:
00 - magic MLCD
04 - name length
08 - code length
0C - enable flag
10 - code data (UTF8 name then actual code)
*/

View File

@ -25,119 +25,30 @@
namespace AREngine namespace AREngine
{ {
typedef struct // AR code file - frontend is responsible for managing this
{ ARCodeFile* CodeFile;
u32 Code[2 * 64]; // TODO: more sensible size for this? allocate on demand?
bool Enabled;
} CheatEntry;
// TODO: more sensible size for this? allocate on demand?
CheatEntry CheatCodes[64];
u32 NumCheatCodes;
void ParseTextCode(char* text, int tlen, u32* code, int clen) // or whatever this should be named?
{
u32 cur_word = 0;
u32 ndigits = 0;
u32 nin = 0;
u32 nout = 0;
char c;
while ((c = *text++) != '\0')
{
u32 val;
if (c >= '0' && c <= '9')
val = c - '0';
else if (c >= 'a' && c <= 'f')
val = c - 'a' + 0xA;
else if (c >= 'A' && c <= 'F')
val = c - 'A' + 0xA;
else
continue;
cur_word <<= 4;
cur_word |= val;
ndigits++;
if (ndigits >= 8)
{
if (nout >= clen)
{
printf("AR: code too long!\n");
return;
}
*code++ = cur_word;
nout++;
ndigits = 0;
cur_word = 0;
}
nin++;
if (nin >= tlen) break;
}
if (nout & 1)
{
printf("AR: code was missing one word\n");
if (nout >= clen)
{
printf("AR: code too long!\n");
return;
}
*code++ = 0;
}
}
bool Init() bool Init()
{ {
CodeFile = nullptr;
return true; return true;
} }
void DeInit() void DeInit()
{ {
//
} }
void Reset() void Reset()
{ {
memset(CheatCodes, 0, sizeof(CheatCodes)); CodeFile = nullptr;
NumCheatCodes = 0; }
// TODO: acquire codes from a sensible source!
CheatEntry* entry = &CheatCodes[0];
u32* ptr = &entry->Code[0];
/*char* test = R"(9209D09A 00000000 void SetCodeFile(ARCodeFile* file)
6209B468 00000000 {
B209B468 00000000 CodeFile = file;
10000672 000003FF
D2000000 00000000
9209D09A 00000000
94000130 FCBF0000
6209B468 00000000
B209B468 00000000
200006B3 00000001
200006B4 00000001
D2000000 00000000
9209D09A 00000000
94000130 FC7F0000
6209B468 00000000
B209B468 00000000
10000672 00000000
D2000000 00000000)";
ParseTextCode(test, entry->Code, 2*64);
printf("PARSED CODE:\n");
for (int i = 0; i < 2*64; i+=2)
{
printf("%08X %08X\n", entry->Code[i], entry->Code[i+1]);
}
entry->Enabled = true;
NumCheatCodes++;*/
} }
@ -147,9 +58,9 @@ D2000000 00000000)";
case ((x)+0x08): case ((x)+0x09): case ((x)+0x0A): case ((x)+0x0B): \ case ((x)+0x08): case ((x)+0x09): case ((x)+0x0A): case ((x)+0x0B): \
case ((x)+0x0C): case ((x)+0x0D): case ((x)+0x0E): case ((x)+0x0F) case ((x)+0x0C): case ((x)+0x0D): case ((x)+0x0E): case ((x)+0x0F)
void RunCheat(CheatEntry* entry) void RunCheat(ARCode& arcode)
{ {
u32* code = &entry->Code[0]; u32* code = &arcode.Code[0];
u32 offset = 0; u32 offset = 0;
u32 datareg = 0; u32 datareg = 0;
@ -166,9 +77,11 @@ void RunCheat(CheatEntry* entry)
for (;;) for (;;)
{ {
if (code >= &arcode.Code[arcode.CodeLen])
break;
u32 a = *code++; u32 a = *code++;
u32 b = *code++; u32 b = *code++;
if ((a|b) == 0) break;
u8 op = a >> 24; u8 op = a >> 24;
@ -179,7 +92,7 @@ void RunCheat(CheatEntry* entry)
if ((op & 0xF0) == 0xE0) if ((op & 0xF0) == 0xE0)
{ {
for (u32 i = 0; i < b; i += 8) for (u32 i = 0; i < b; i += 8)
*code += 2; code += 2;
} }
continue; continue;
@ -428,7 +341,7 @@ void RunCheat(CheatEntry* entry)
if (bytesleft > 0) if (bytesleft > 0)
{ {
u8* leftover = (u8*)code; u8* leftover = (u8*)code;
*code += 2; code += 2;
if (bytesleft >= 4) if (bytesleft >= 4)
{ {
NDS::ARM7Write32(dstaddr, *(u32*)leftover); dstaddr += 4; NDS::ARM7Write32(dstaddr, *(u32*)leftover); dstaddr += 4;
@ -477,13 +390,19 @@ void RunCheat(CheatEntry* entry)
void RunCheats() void RunCheats()
{ {
// TODO: make it disableable in general if (!CodeFile) return;
for (u32 i = 0; i < NumCheatCodes; i++) for (ARCodeCatList::iterator i = CodeFile->Categories.begin(); i != CodeFile->Categories.end(); i++)
{ {
CheatEntry* entry = &CheatCodes[i]; ARCodeCat& cat = *i;
if (entry->Enabled)
RunCheat(entry); for (ARCodeList::iterator j = cat.Codes.begin(); j != cat.Codes.end(); j++)
{
ARCode& code = *j;
if (code.Enabled)
RunCheat(code);
}
} }
} }

View File

@ -19,6 +19,8 @@
#ifndef ARENGINE_H #ifndef ARENGINE_H
#define ARENGINE_H #define ARENGINE_H
#include "ARCodeFile.h"
namespace AREngine namespace AREngine
{ {
@ -26,6 +28,8 @@ bool Init();
void DeInit(); void DeInit();
void Reset(); void Reset();
void SetCodeFile(ARCodeFile* file);
void RunCheats(); void RunCheats();
} }

View File

@ -3,7 +3,7 @@ project(core)
set (CMAKE_CXX_STANDARD 14) set (CMAKE_CXX_STANDARD 14)
add_library(core STATIC add_library(core STATIC
ARCodeList.cpp ARCodeFile.cpp
AREngine.cpp AREngine.cpp
ARM.cpp ARM.cpp
ARM_InstrTable.h ARM_InstrTable.h

View File

@ -74,6 +74,9 @@
<height>0</height> <height>0</height>
</size> </size>
</property> </property>
<property name="headerHidden">
<bool>true</bool>
</property>
</widget> </widget>
</item> </item>
<item> <item>