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/.
*/
#ifndef ARCODELIST_H
#define ARCODELIST_H
#ifndef ARCODEFILE_H
#define ARCODEFILE_H
#include <vector>
#include "types.h"
#define ARCL_MAJOR 1
#define ARCL_MINOR 1
typedef struct
{
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:
//
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
{
typedef struct
{
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;
}
}
// AR code file - frontend is responsible for managing this
ARCodeFile* CodeFile;
bool Init()
{
CodeFile = nullptr;
return true;
}
void DeInit()
{
//
}
void Reset()
{
memset(CheatCodes, 0, sizeof(CheatCodes));
NumCheatCodes = 0;
CodeFile = nullptr;
}
// TODO: acquire codes from a sensible source!
CheatEntry* entry = &CheatCodes[0];
u32* ptr = &entry->Code[0];
/*char* test = R"(9209D09A 00000000
6209B468 00000000
B209B468 00000000
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++;*/
void SetCodeFile(ARCodeFile* file)
{
CodeFile = file;
}
@ -147,9 +58,9 @@ D2000000 00000000)";
case ((x)+0x08): case ((x)+0x09): case ((x)+0x0A): case ((x)+0x0B): \
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 datareg = 0;
@ -166,9 +77,11 @@ void RunCheat(CheatEntry* entry)
for (;;)
{
if (code >= &arcode.Code[arcode.CodeLen])
break;
u32 a = *code++;
u32 b = *code++;
if ((a|b) == 0) break;
u8 op = a >> 24;
@ -179,7 +92,7 @@ void RunCheat(CheatEntry* entry)
if ((op & 0xF0) == 0xE0)
{
for (u32 i = 0; i < b; i += 8)
*code += 2;
code += 2;
}
continue;
@ -428,7 +341,7 @@ void RunCheat(CheatEntry* entry)
if (bytesleft > 0)
{
u8* leftover = (u8*)code;
*code += 2;
code += 2;
if (bytesleft >= 4)
{
NDS::ARM7Write32(dstaddr, *(u32*)leftover); dstaddr += 4;
@ -477,13 +390,19 @@ void RunCheat(CheatEntry* entry)
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];
if (entry->Enabled)
RunCheat(entry);
ARCodeCat& cat = *i;
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
#define ARENGINE_H
#include "ARCodeFile.h"
namespace AREngine
{
@ -26,6 +28,8 @@ bool Init();
void DeInit();
void Reset();
void SetCodeFile(ARCodeFile* file);
void RunCheats();
}

View File

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

View File

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