1427 lines
32 KiB
C++
1427 lines
32 KiB
C++
/*
|
|
Copyright (C) 2009-2012 DeSmuME team
|
|
|
|
This file 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 file 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 the this software. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include "cheatSystem.h"
|
|
#include "NDSSystem.h"
|
|
#include "mem.h"
|
|
#include "MMU.h"
|
|
#include "debug.h"
|
|
#include "utils/xstring.h"
|
|
|
|
#ifndef _MSC_VER
|
|
#include <stdint.h>
|
|
#endif
|
|
|
|
CHEATS *cheats = NULL;
|
|
CHEATSEARCH *cheatSearch = NULL;
|
|
|
|
void CHEATS::clear()
|
|
{
|
|
list.resize(0);
|
|
currentGet = 0;
|
|
}
|
|
|
|
void CHEATS::init(char *path)
|
|
{
|
|
clear();
|
|
strcpy((char *)filename, path);
|
|
|
|
load();
|
|
}
|
|
|
|
BOOL CHEATS::add(u8 size, u32 address, u32 val, char *description, BOOL enabled)
|
|
{
|
|
size_t num = list.size();
|
|
list.push_back(CHEATS_LIST());
|
|
list[num].code[0][0] = address & 0x00FFFFFF;
|
|
list[num].code[0][1] = val;
|
|
list[num].num = 1;
|
|
list[num].type = 0;
|
|
list[num].size = size;
|
|
this->setDescription(description, num);
|
|
list[num].enabled = enabled;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CHEATS::update(u8 size, u32 address, u32 val, char *description, BOOL enabled, u32 pos)
|
|
{
|
|
if (pos >= list.size()) return FALSE;
|
|
list[pos].code[0][0] = address & 0x00FFFFFF;
|
|
list[pos].code[0][1] = val;
|
|
list[pos].num = 1;
|
|
list[pos].type = 0;
|
|
list[pos].size = size;
|
|
this->setDescription(description, pos);
|
|
list[pos].enabled = enabled;
|
|
return TRUE;
|
|
}
|
|
|
|
void CHEATS::ARparser(CHEATS_LIST& list)
|
|
{
|
|
u8 type = 0;
|
|
u8 subtype = 0;
|
|
u32 hi = 0;
|
|
u32 lo = 0;
|
|
u32 addr = 0;
|
|
u32 val = 0;
|
|
// AR temporary vars & flags
|
|
u32 offset = 0;
|
|
u32 datareg = 0;
|
|
u32 loopcount = 0;
|
|
u32 counter = 0;
|
|
u32 if_flag = 0;
|
|
s32 loopbackline = 0;
|
|
u32 loop_flag = 0;
|
|
|
|
for (int i=0; i < list.num; i++)
|
|
{
|
|
type = list.code[i][0] >> 28;
|
|
subtype = (list.code[i][0] >> 24) & 0x0F;
|
|
|
|
hi = list.code[i][0] & 0x0FFFFFFF;
|
|
lo = list.code[i][1];
|
|
|
|
if (if_flag > 0)
|
|
{
|
|
if ( (type == 0x0D) && (subtype == 0)) if_flag--; // ENDIF
|
|
if ( (type == 0x0D) && (subtype == 2)) // NEXT & Flush
|
|
{
|
|
if (loop_flag)
|
|
i = (loopbackline-1);
|
|
else
|
|
{
|
|
offset = 0;
|
|
datareg = 0;
|
|
loopcount = 0;
|
|
counter = 0;
|
|
if_flag = 0;
|
|
loop_flag = 0;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
switch (type)
|
|
{
|
|
case 0x00:
|
|
{
|
|
if (hi==0)
|
|
{
|
|
//manual hook
|
|
}
|
|
else
|
|
if ((hi==0x0000AA99) && (lo==0)) // 0000AA99 00000000 parameter bytes 9..10 for above code (padded with 00s)
|
|
{
|
|
//parameter bytes 9..10 for above code (padded with 00s)
|
|
}
|
|
else // 0XXXXXXX YYYYYYYY word[XXXXXXX+offset] = YYYYYYYY
|
|
{
|
|
addr = hi + offset;
|
|
_MMU_write32<ARMCPU_ARM9,MMU_AT_DEBUG>(addr, lo);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 0x01: // 1XXXXXXX 0000YYYY half[XXXXXXX+offset] = YYYY
|
|
addr = hi + offset;
|
|
_MMU_write16<ARMCPU_ARM9,MMU_AT_DEBUG>(addr, lo);
|
|
break;
|
|
|
|
case 0x02: // 2XXXXXXX 000000YY byte[XXXXXXX+offset] = YY
|
|
addr = hi + offset;
|
|
_MMU_write08<ARMCPU_ARM9,MMU_AT_DEBUG>(addr, lo);
|
|
break;
|
|
|
|
case 0x03: // 3XXXXXXX YYYYYYYY IF YYYYYYYY > word[XXXXXXX] ;unsigned
|
|
if (hi == 0) hi = offset; // V1.54+
|
|
val = _MMU_read32<ARMCPU_ARM9,MMU_AT_DEBUG>(hi);
|
|
if ( lo > val )
|
|
{
|
|
if (if_flag > 0) if_flag--;
|
|
}
|
|
else
|
|
{
|
|
if_flag++;
|
|
}
|
|
break;
|
|
|
|
case 0x04: // 4XXXXXXX YYYYYYYY IF YYYYYYYY < word[XXXXXXX] ;unsigned
|
|
if ((hi == 0x04332211) && (lo == 88776655)) //44332211 88776655 parameter bytes 1..8 for above code (example)
|
|
{
|
|
break;
|
|
}
|
|
if (hi == 0) hi = offset; // V1.54+
|
|
val = _MMU_read32<ARMCPU_ARM9,MMU_AT_DEBUG>(hi);
|
|
if ( lo < val )
|
|
{
|
|
if (if_flag > 0) if_flag--;
|
|
}
|
|
else
|
|
{
|
|
if_flag++;
|
|
}
|
|
break;
|
|
|
|
case 0x05: // 5XXXXXXX YYYYYYYY IF YYYYYYYY = word[XXXXXXX]
|
|
if (hi == 0) hi = offset; // V1.54+
|
|
val = _MMU_read32<ARMCPU_ARM9,MMU_AT_DEBUG>(hi);
|
|
if ( lo == val )
|
|
{
|
|
if (if_flag > 0) if_flag--;
|
|
}
|
|
else
|
|
{
|
|
if_flag++;
|
|
}
|
|
break;
|
|
|
|
case 0x06: // 6XXXXXXX YYYYYYYY IF YYYYYYYY <> word[XXXXXXX]
|
|
if (hi == 0) hi = offset; // V1.54+
|
|
val = _MMU_read32<ARMCPU_ARM9,MMU_AT_DEBUG>(hi);
|
|
if ( lo != val )
|
|
{
|
|
if (if_flag > 0) if_flag--;
|
|
}
|
|
else
|
|
{
|
|
if_flag++;
|
|
}
|
|
break;
|
|
|
|
case 0x07: // 7XXXXXXX ZZZZYYYY IF YYYY > ((not ZZZZ) AND half[XXXXXXX])
|
|
if (hi == 0) hi = offset; // V1.54+
|
|
val = _MMU_read16<ARMCPU_ARM9,MMU_AT_DEBUG>(hi);
|
|
if ( (lo & 0xFFFF) > ( (~(lo >> 16)) & val) )
|
|
{
|
|
if (if_flag > 0) if_flag--;
|
|
}
|
|
else
|
|
{
|
|
if_flag++;
|
|
}
|
|
break;
|
|
|
|
case 0x08: // 8XXXXXXX ZZZZYYYY IF YYYY < ((not ZZZZ) AND half[XXXXXXX])
|
|
if (hi == 0) hi = offset; // V1.54+
|
|
val = _MMU_read16<ARMCPU_ARM9,MMU_AT_DEBUG>(hi);
|
|
if ( (lo & 0xFFFF) < ( (~(lo >> 16)) & val) )
|
|
{
|
|
if (if_flag > 0) if_flag--;
|
|
}
|
|
else
|
|
{
|
|
if_flag++;
|
|
}
|
|
break;
|
|
|
|
case 0x09: // 9XXXXXXX ZZZZYYYY IF YYYY = ((not ZZZZ) AND half[XXXXXXX])
|
|
if (hi == 0) hi = offset; // V1.54+
|
|
val = _MMU_read16<ARMCPU_ARM9,MMU_AT_DEBUG>(hi);
|
|
if ( (lo & 0xFFFF) == ( (~(lo >> 16)) & val) )
|
|
{
|
|
if (if_flag > 0) if_flag--;
|
|
}
|
|
else
|
|
{
|
|
if_flag++;
|
|
}
|
|
break;
|
|
|
|
case 0x0A: // AXXXXXXX ZZZZYYYY IF YYYY <> ((not ZZZZ) AND half[XXXXXXX])
|
|
if (hi == 0) hi = offset; // V1.54+
|
|
val = _MMU_read16<ARMCPU_ARM9,MMU_AT_DEBUG>(hi);
|
|
if ( (lo & 0xFFFF) != ( (~(lo >> 16)) & val) )
|
|
{
|
|
if (if_flag > 0) if_flag--;
|
|
}
|
|
else
|
|
{
|
|
if_flag++;
|
|
}
|
|
break;
|
|
|
|
case 0x0B: // BXXXXXXX 00000000 offset = word[XXXXXXX+offset]
|
|
addr = hi + offset;
|
|
offset = _MMU_read32<ARMCPU_ARM9,MMU_AT_DEBUG>(addr);;
|
|
break;
|
|
|
|
case 0x0C:
|
|
switch (subtype)
|
|
{
|
|
case 0x0: // C0000000 YYYYYYYY FOR loopcount=0 to YYYYYYYY ;execute Y+1 times
|
|
if (loopcount < (lo+1))
|
|
loop_flag = 1;
|
|
else
|
|
loop_flag = 0;
|
|
loopcount++;
|
|
loopbackline = i;
|
|
break;
|
|
|
|
case 0x4: // C4000000 00000000 offset = address of the C4000000 code ; V1.54
|
|
printf("AR: untested code C4\n");
|
|
break;
|
|
|
|
case 0x5: // C5000000 XXXXYYYY counter=counter+1, IF (counter AND YYYY) = XXXX ; V1.54
|
|
counter++;
|
|
if ( (counter & (lo & 0xFFFF)) == ((lo >> 8) & 0xFFFF) )
|
|
{
|
|
if (if_flag > 0) if_flag--;
|
|
}
|
|
else
|
|
{
|
|
if_flag++;
|
|
}
|
|
break;
|
|
|
|
case 0x6: // C6000000 XXXXXXXX [XXXXXXXX]=offset ; V1.54
|
|
_MMU_write32<ARMCPU_ARM9,MMU_AT_DEBUG>(lo, offset);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 0x0D:
|
|
{
|
|
switch (subtype)
|
|
{
|
|
case 0x0: // D0000000 00000000 ENDIF
|
|
break;
|
|
|
|
case 0x1: // D1000000 00000000 NEXT loopcount
|
|
if (loop_flag)
|
|
i = (loopbackline-1);
|
|
break;
|
|
|
|
case 0x2: // D2000000 00000000 NEXT loopcount, and then FLUSH everything
|
|
if (loop_flag)
|
|
i = (loopbackline-1);
|
|
else
|
|
{
|
|
offset = 0;
|
|
datareg = 0;
|
|
loopcount = 0;
|
|
counter = 0;
|
|
if_flag = 0;
|
|
loop_flag = 0;
|
|
}
|
|
break;
|
|
|
|
case 0x3: // D3000000 XXXXXXXX offset = XXXXXXXX
|
|
offset = lo;
|
|
break;
|
|
|
|
case 0x4: // D4000000 XXXXXXXX datareg = datareg + XXXXXXXX
|
|
datareg += lo;
|
|
break;
|
|
|
|
case 0x5: // D5000000 XXXXXXXX datareg = XXXXXXXX
|
|
datareg = lo;
|
|
break;
|
|
|
|
case 0x6: // D6000000 XXXXXXXX word[XXXXXXXX+offset]=datareg, offset=offset+4
|
|
addr = lo + offset;
|
|
_MMU_write32<ARMCPU_ARM9,MMU_AT_DEBUG>(addr, datareg);
|
|
offset += 4;
|
|
break;
|
|
|
|
case 0x7: // D7000000 XXXXXXXX half[XXXXXXXX+offset]=datareg, offset=offset+2
|
|
addr = lo + offset;
|
|
_MMU_write16<ARMCPU_ARM9,MMU_AT_DEBUG>(addr, datareg);
|
|
offset += 2;
|
|
break;
|
|
|
|
case 0x8: // D8000000 XXXXXXXX byte[XXXXXXXX+offset]=datareg, offset=offset+1
|
|
addr = lo + offset;
|
|
_MMU_write08<ARMCPU_ARM9,MMU_AT_DEBUG>(addr, datareg);
|
|
offset += 1;
|
|
break;
|
|
|
|
case 0x9: // D9000000 XXXXXXXX datareg = word[XXXXXXXX+offset]
|
|
addr = lo + offset;
|
|
datareg = _MMU_read32<ARMCPU_ARM9,MMU_AT_DEBUG>(addr);
|
|
break;
|
|
|
|
case 0xA: // DA000000 XXXXXXXX datareg = half[XXXXXXXX+offset]
|
|
addr = lo + offset;
|
|
datareg = _MMU_read16<ARMCPU_ARM9,MMU_AT_DEBUG>(addr);
|
|
break;
|
|
|
|
case 0xB: // DB000000 XXXXXXXX datareg = byte[XXXXXXXX+offset] ;bugged on pre-v1.54
|
|
addr = lo + offset;
|
|
datareg = _MMU_read08<ARMCPU_ARM9,MMU_AT_DEBUG>(addr);
|
|
break;
|
|
|
|
case 0xC: // DC000000 XXXXXXXX offset = offset + XXXXXXXX
|
|
offset += lo;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 0xE: // EXXXXXXX YYYYYYYY Copy YYYYYYYY parameter bytes to [XXXXXXXX+offset...]
|
|
{
|
|
u8 *tmp_code = (u8*)(list.code[i+1]);
|
|
u32 addr = hi+offset;
|
|
u32 maxByteReadLocation = ((2 * 4) * (MAX_XX_CODE - i - 1)) - 1; // 2 = 2 array dimensions, 4 = 4 bytes per array element
|
|
|
|
if (lo <= maxByteReadLocation)
|
|
{
|
|
for (u32 t = 0; t < lo; t++)
|
|
{
|
|
u8 tmp = tmp_code[t];
|
|
_MMU_write08<ARMCPU_ARM9,MMU_AT_DEBUG>(addr, tmp);
|
|
addr++;
|
|
}
|
|
}
|
|
|
|
i += (lo / 8);
|
|
}
|
|
break;
|
|
|
|
case 0xF: // FXXXXXXX YYYYYYYY Copy YYYYYYYY bytes from [offset..] to [XXXXXXX...]
|
|
for (u32 t = 0; t < lo; t++)
|
|
{
|
|
u8 tmp = _MMU_read08<ARMCPU_ARM9,MMU_AT_DEBUG>(offset+t);
|
|
_MMU_write08<ARMCPU_ARM9,MMU_AT_DEBUG>(hi+t, tmp);
|
|
}
|
|
break;
|
|
default: PROGINFO("AR: ERROR unknown command 0x%2X at %08X:%08X\n", type, hi, lo); break;
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL CHEATS::add_AR_Direct(CHEATS_LIST cheat)
|
|
{
|
|
size_t num = list.size();
|
|
list.push_back(cheat);
|
|
list[num].type = 1;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CHEATS::add_AR(char *code, char *description, BOOL enabled)
|
|
{
|
|
//if (num == MAX_CHEAT_LIST) return FALSE;
|
|
size_t num = list.size();
|
|
|
|
CHEATS_LIST temp;
|
|
if (!CHEATS::XXCodeFromString(&temp, code)) return FALSE;
|
|
|
|
list.push_back(temp);
|
|
|
|
list[num].type = 1;
|
|
|
|
this->setDescription(description, num);
|
|
list[num].enabled = enabled;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CHEATS::update_AR(char *code, char *description, BOOL enabled, u32 pos)
|
|
{
|
|
if (pos >= list.size()) return FALSE;
|
|
|
|
if (code != NULL)
|
|
{
|
|
if (!CHEATS::XXCodeFromString(this->getItemByIndex(pos), code)) return FALSE;
|
|
this->setDescription(description, pos);
|
|
list[pos].type = 1;
|
|
}
|
|
|
|
list[pos].enabled = enabled;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CHEATS::add_CB(char *code, char *description, BOOL enabled)
|
|
{
|
|
//if (num == MAX_CHEAT_LIST) return FALSE;
|
|
size_t num = list.size();
|
|
|
|
if (!CHEATS::XXCodeFromString(this->getItemByIndex(num), code)) return FALSE;
|
|
|
|
list[num].type = 2;
|
|
|
|
this->setDescription(description, num);
|
|
list[num].enabled = enabled;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CHEATS::update_CB(char *code, char *description, BOOL enabled, u32 pos)
|
|
{
|
|
if (pos >= list.size()) return FALSE;
|
|
|
|
if (code != NULL)
|
|
{
|
|
if (!CHEATS::XXCodeFromString(this->getItemByIndex(pos), code)) return FALSE;
|
|
list[pos].type = 2;
|
|
this->setDescription(description, pos);
|
|
}
|
|
list[pos].enabled = enabled;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CHEATS::remove(u32 pos)
|
|
{
|
|
if (pos >= list.size()) return FALSE;
|
|
if (list.size() == 0) return FALSE;
|
|
|
|
list.erase(list.begin()+pos);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void CHEATS::getListReset()
|
|
{
|
|
currentGet = 0;
|
|
return;
|
|
}
|
|
|
|
BOOL CHEATS::getList(CHEATS_LIST *cheat)
|
|
{
|
|
BOOL result = FALSE;
|
|
|
|
if (currentGet >= this->list.size())
|
|
{
|
|
this->getListReset();
|
|
return result;
|
|
}
|
|
|
|
result = this->get(cheat, currentGet++);
|
|
|
|
return result;
|
|
}
|
|
|
|
CHEATS_LIST* CHEATS::getListPtr()
|
|
{
|
|
return &this->list[0];
|
|
}
|
|
|
|
BOOL CHEATS::get(CHEATS_LIST *cheat, u32 pos)
|
|
{
|
|
CHEATS_LIST *item = this->getItemByIndex(pos);
|
|
if (item == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
*cheat = *item;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
CHEATS_LIST* CHEATS::getItemByIndex(const u32 pos)
|
|
{
|
|
if (pos >= this->getSize())
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return &this->list[pos];
|
|
}
|
|
|
|
u32 CHEATS::getSize()
|
|
{
|
|
return list.size();
|
|
}
|
|
|
|
void CHEATS::setDescription(const char *description, u32 pos)
|
|
{
|
|
strncpy(list[pos].description, description, sizeof(list[pos].description));
|
|
list[pos].description[sizeof(list[pos].description) - 1] = '\0';
|
|
}
|
|
|
|
BOOL CHEATS::save()
|
|
{
|
|
const char *types[] = {"DS", "AR", "CB"};
|
|
std::string cheatLineStr = "";
|
|
FILE *flist = fopen((char *)filename, "w");
|
|
|
|
if (flist)
|
|
{
|
|
fprintf(flist, "; DeSmuME cheats file. VERSION %i.%03i\n", CHEAT_VERSION_MAJOR, CHEAT_VERSION_MINOR);
|
|
fprintf(flist, "Name=%s\n", gameInfo.ROMname);
|
|
fprintf(flist, "Serial=%s\n", gameInfo.ROMserial);
|
|
fputs("\n; cheats list\n", flist);
|
|
for (size_t i = 0; i < list.size(); i++)
|
|
{
|
|
if (list[i].num == 0) continue;
|
|
|
|
char buf1[8] = {0};
|
|
sprintf(buf1, "%s %c ", types[list[i].type], list[i].enabled?'1':'0');
|
|
cheatLineStr = buf1;
|
|
|
|
for (int t = 0; t < list[i].num; t++)
|
|
{
|
|
char buf2[10] = { 0 };
|
|
|
|
u32 adr = list[i].code[t][0];
|
|
if (list[i].type == 0)
|
|
{
|
|
//size of the cheat is written out as adr highest nybble
|
|
adr &= 0x0FFFFFFF;
|
|
adr |= (list[i].size << 28);
|
|
}
|
|
sprintf(buf2, "%08X", adr);
|
|
cheatLineStr += buf2;
|
|
|
|
sprintf(buf2, "%08X", list[i].code[t][1]);
|
|
cheatLineStr += buf2;
|
|
if (t < (list[i].num - 1))
|
|
cheatLineStr += ",";
|
|
}
|
|
|
|
cheatLineStr += " ;";
|
|
cheatLineStr += trim(list[i].description);
|
|
fprintf(flist, "%s\n", cheatLineStr.c_str());
|
|
}
|
|
fputs("\n", flist);
|
|
fclose(flist);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
char *CHEATS::clearCode(char *s)
|
|
{
|
|
char *buf = s;
|
|
if (!s) return NULL;
|
|
if (!*s) return s;
|
|
|
|
for (u32 i = 0; i < strlen(s); i++)
|
|
{
|
|
if (s[i] == ';') break;
|
|
if (strchr(hexValid, s[i]))
|
|
{
|
|
*buf = s[i];
|
|
buf++;
|
|
}
|
|
}
|
|
*buf = 0;
|
|
return s;
|
|
}
|
|
|
|
BOOL CHEATS::load()
|
|
{
|
|
FILE *flist = fopen((char *)filename, "r");
|
|
if (flist == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
size_t readSize = (MAX_XX_CODE * 17) + sizeof(list[0].description) + 7;
|
|
if (readSize < CHEAT_FILE_MIN_FGETS_BUFFER)
|
|
{
|
|
readSize = CHEAT_FILE_MIN_FGETS_BUFFER;
|
|
}
|
|
|
|
char *buf = (char *)malloc(readSize);
|
|
if (buf == NULL)
|
|
{
|
|
fclose(flist);
|
|
return FALSE;
|
|
}
|
|
|
|
readSize *= sizeof(*buf);
|
|
|
|
std::string codeStr = "";
|
|
u32 last = 0;
|
|
u32 line = 0;
|
|
|
|
INFO("Load cheats: %s\n", filename);
|
|
clear();
|
|
last = 0; line = 0;
|
|
while (!feof(flist))
|
|
{
|
|
CHEATS_LIST tmp_cht;
|
|
line++; // only for debug
|
|
memset(buf, 0, readSize);
|
|
if (fgets(buf, readSize, flist) == NULL) {
|
|
//INFO("Cheats: Failed to read from flist at line %i\n", line);
|
|
continue;
|
|
}
|
|
trim(buf);
|
|
if ((strlen(buf) == 0) || (buf[0] == ';')) continue;
|
|
if(!strncasecmp(buf,"name=",5)) continue;
|
|
if(!strncasecmp(buf,"serial=",7)) continue;
|
|
|
|
memset(&tmp_cht, 0, sizeof(tmp_cht));
|
|
if ((buf[0] == 'D') && (buf[1] == 'S')) // internal
|
|
tmp_cht.type = 0;
|
|
else
|
|
if ((buf[0] == 'A') && (buf[1] == 'R')) // Action Replay
|
|
tmp_cht.type = 1;
|
|
else
|
|
if ((buf[0] == 'B') && (buf[1] == 'S')) // Codebreaker
|
|
tmp_cht.type = 2;
|
|
else
|
|
continue;
|
|
// TODO: CB not supported
|
|
if (tmp_cht.type == 3)
|
|
{
|
|
INFO("Cheats: Codebreaker code no supported at line %i\n", line);
|
|
continue;
|
|
}
|
|
|
|
codeStr = (char *)(buf + 5);
|
|
codeStr = clearCode((char *)codeStr.c_str());
|
|
|
|
if (codeStr.empty() || (codeStr.length() % 16 != 0))
|
|
{
|
|
INFO("Cheats: Syntax error at line %i\n", line);
|
|
continue;
|
|
}
|
|
|
|
tmp_cht.enabled = (buf[3] == '0')?FALSE:TRUE;
|
|
u32 descr_pos = (u32)(std::max<s32>(strchr((char*)buf, ';') - buf, 0));
|
|
if (descr_pos != 0)
|
|
{
|
|
strncpy(tmp_cht.description, (buf + descr_pos + 1), sizeof(tmp_cht.description));
|
|
tmp_cht.description[sizeof(tmp_cht.description) - 1] = '\0';
|
|
}
|
|
|
|
tmp_cht.num = codeStr.length() / 16;
|
|
if ((tmp_cht.type == 0) && (tmp_cht.num > 1))
|
|
{
|
|
INFO("Cheats: Too many values for internal cheat\n", line);
|
|
continue;
|
|
}
|
|
for (int i = 0; i < tmp_cht.num; i++)
|
|
{
|
|
char tmp_buf[9] = {0};
|
|
|
|
strncpy(tmp_buf, &codeStr[i * 16], 8);
|
|
sscanf_s(tmp_buf, "%x", &tmp_cht.code[i][0]);
|
|
|
|
if (tmp_cht.type == 0)
|
|
{
|
|
tmp_cht.size = std::min<u32>(3, ((tmp_cht.code[i][0] & 0xF0000000) >> 28));
|
|
tmp_cht.code[i][0] &= 0x00FFFFFF;
|
|
}
|
|
|
|
strncpy(tmp_buf, &codeStr[(i * 16) + 8], 8);
|
|
sscanf_s(tmp_buf, "%x", &tmp_cht.code[i][1]);
|
|
}
|
|
|
|
list.push_back(tmp_cht);
|
|
last++;
|
|
}
|
|
|
|
free(buf);
|
|
buf = NULL;
|
|
|
|
fclose(flist);
|
|
INFO("Added %i cheat codes\n", list.size());
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void CHEATS::process()
|
|
{
|
|
if (CommonSettings.cheatsDisable) return;
|
|
if (list.size() == 0) return;
|
|
size_t num = list.size();
|
|
for (size_t i = 0; i < num; i++)
|
|
{
|
|
if (!list[i].enabled) continue;
|
|
|
|
switch (list[i].type)
|
|
{
|
|
case 0: // internal cheat system
|
|
{
|
|
//INFO("list at 0x02|%06X value %i (size %i)\n",list[i].code[0], list[i].lo[0], list[i].size);
|
|
u32 addr = list[i].code[0][0] | 0x02000000;
|
|
u32 val = list[i].code[0][1];
|
|
switch (list[i].size)
|
|
{
|
|
case 0:
|
|
_MMU_write08<ARMCPU_ARM9,MMU_AT_DEBUG>(addr,val);
|
|
break;
|
|
case 1:
|
|
_MMU_write16<ARMCPU_ARM9,MMU_AT_DEBUG>(addr,val);
|
|
break;
|
|
case 2:
|
|
{
|
|
u32 tmp = _MMU_read32<ARMCPU_ARM9,MMU_AT_DEBUG>(addr);
|
|
tmp &= 0xFF000000;
|
|
tmp |= (val & 0x00FFFFFF);
|
|
_MMU_write32<ARMCPU_ARM9,MMU_AT_DEBUG>(addr,tmp);
|
|
break;
|
|
}
|
|
case 3:
|
|
_MMU_write32<ARMCPU_ARM9,MMU_AT_DEBUG>(addr,val);
|
|
break;
|
|
}
|
|
break;
|
|
} //end case 0 internal cheat system
|
|
|
|
case 1: // Action Replay
|
|
ARparser(list[i]);
|
|
break;
|
|
case 2: // Codebreaker
|
|
break;
|
|
default: continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CHEATS::getXXcodeString(CHEATS_LIST list, char *res_buf)
|
|
{
|
|
char buf[50] = { 0 };
|
|
|
|
for (int i=0; i < list.num; i++)
|
|
{
|
|
sprintf(buf, "%08X %08X\n", list.code[i][0], list.code[i][1]);
|
|
strcat(res_buf, buf);
|
|
}
|
|
}
|
|
|
|
BOOL CHEATS::XXCodeFromString(CHEATS_LIST *cheatItem, const std::string codeString)
|
|
{
|
|
return CHEATS::XXCodeFromString(cheatItem, codeString.c_str());
|
|
}
|
|
|
|
BOOL CHEATS::XXCodeFromString(CHEATS_LIST *cheatItem, const char *codeString)
|
|
{
|
|
BOOL result = FALSE;
|
|
|
|
if (cheatItem == NULL || codeString == NULL)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
int count = 0;
|
|
u16 t = 0;
|
|
char tmp_buf[sizeof(cheatItem->code) * 2 + 1];
|
|
memset(tmp_buf, 0, sizeof(tmp_buf));
|
|
|
|
size_t code_len = strlen(codeString);
|
|
// remove wrong chars
|
|
for (size_t i=0; i < code_len; i++)
|
|
{
|
|
char c = codeString[i];
|
|
//apparently 100% of pokemon codes were typed with the letter O in place of zero in some places
|
|
//so let's try to adjust for that here
|
|
static const char *AR_Valid = "Oo0123456789ABCDEFabcdef";
|
|
if (strchr(AR_Valid, c))
|
|
{
|
|
if(c=='o' || c=='O') c='0';
|
|
tmp_buf[t++] = c;
|
|
}
|
|
}
|
|
|
|
size_t len = strlen(tmp_buf);
|
|
if ((len % 16) != 0) return result; // error
|
|
|
|
// TODO: syntax check
|
|
count = (len / 16);
|
|
for (int i=0; i < count; i++)
|
|
{
|
|
char buf[9] = {0};
|
|
memcpy(buf, tmp_buf+(i*16), 8);
|
|
sscanf(buf, "%x", &cheatItem->code[i][0]);
|
|
memcpy(buf, tmp_buf+(i*16) + 8, 8);
|
|
sscanf(buf, "%x", &cheatItem->code[i][1]);
|
|
}
|
|
|
|
cheatItem->num = count;
|
|
cheatItem->size = 0;
|
|
|
|
result = TRUE;
|
|
|
|
return result;
|
|
}
|
|
|
|
// ========================================== search
|
|
BOOL CHEATSEARCH::start(u8 type, u8 size, u8 sign)
|
|
{
|
|
if (statMem) return FALSE;
|
|
if (mem) return FALSE;
|
|
|
|
statMem = new u8 [ ( 4 * 1024 * 1024 ) / 8 ];
|
|
memset(statMem, 0xFF, ( 4 * 1024 * 1024 ) / 8);
|
|
|
|
// comparative search type (need 8Mb RAM !!! (4+4))
|
|
mem = new u8 [ ( 4 * 1024 * 1024 ) ];
|
|
memcpy(mem, MMU.MMU_MEM[0][0x20], ( 4 * 1024 * 1024 ) );
|
|
|
|
_type = type;
|
|
_size = size;
|
|
_sign = sign;
|
|
amount = 0;
|
|
lastRecord = 0;
|
|
|
|
//INFO("Cheat search system is inited (type %s)\n", type?"comparative":"exact");
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CHEATSEARCH::close()
|
|
{
|
|
if (statMem)
|
|
{
|
|
delete [] statMem;
|
|
statMem = NULL;
|
|
}
|
|
|
|
if (mem)
|
|
{
|
|
delete [] mem;
|
|
mem = NULL;
|
|
}
|
|
amount = 0;
|
|
lastRecord = 0;
|
|
//INFO("Cheat search system is closed\n");
|
|
return FALSE;
|
|
}
|
|
|
|
u32 CHEATSEARCH::search(u32 val)
|
|
{
|
|
amount = 0;
|
|
|
|
switch (_size)
|
|
{
|
|
case 0: // 1 byte
|
|
for (u32 i = 0; i < (4 * 1024 * 1024); i++)
|
|
{
|
|
u32 addr = (i >> 3);
|
|
u32 offs = (i % 8);
|
|
if (statMem[addr] & (1<<offs))
|
|
{
|
|
if ( T1ReadByte(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i) == val )
|
|
{
|
|
statMem[addr] |= (1<<offs);
|
|
amount++;
|
|
continue;
|
|
}
|
|
statMem[addr] &= ~(1<<offs);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 1: // 2 bytes
|
|
for (u32 i = 0; i < (4 * 1024 * 1024); i+=2)
|
|
{
|
|
u32 addr = (i >> 3);
|
|
u32 offs = (i % 8);
|
|
if (statMem[addr] & (3<<offs))
|
|
{
|
|
if ( T1ReadWord(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i) == val )
|
|
{
|
|
statMem[addr] |= (3<<offs);
|
|
amount++;
|
|
continue;
|
|
}
|
|
statMem[addr] &= ~(3<<offs);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 2: // 3 bytes
|
|
for (u32 i = 0; i < (4 * 1024 * 1024); i+=3)
|
|
{
|
|
u32 addr = (i >> 3);
|
|
u32 offs = (i % 8);
|
|
if (statMem[addr] & (0x7<<offs))
|
|
{
|
|
if ( (T1ReadLong(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i) & 0x00FFFFFF) == val )
|
|
{
|
|
statMem[addr] |= (0x7<<offs);
|
|
amount++;
|
|
continue;
|
|
}
|
|
statMem[addr] &= ~(0x7<<offs);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 3: // 4 bytes
|
|
for (u32 i = 0; i < (4 * 1024 * 1024); i+=4)
|
|
{
|
|
u32 addr = (i >> 3);
|
|
u32 offs = (i % 8);
|
|
if (statMem[addr] & (0xF<<offs))
|
|
{
|
|
if ( T1ReadLong(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i) == val )
|
|
{
|
|
statMem[addr] |= (0xF<<offs);
|
|
amount++;
|
|
continue;
|
|
}
|
|
statMem[addr] &= ~(0xF<<offs);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return (amount);
|
|
}
|
|
|
|
u32 CHEATSEARCH::search(u8 comp)
|
|
{
|
|
BOOL res = FALSE;
|
|
|
|
amount = 0;
|
|
|
|
switch (_size)
|
|
{
|
|
case 0: // 1 byte
|
|
for (u32 i = 0; i < (4 * 1024 * 1024); i++)
|
|
{
|
|
u32 addr = (i >> 3);
|
|
u32 offs = (i % 8);
|
|
if (statMem[addr] & (1<<offs))
|
|
{
|
|
switch (comp)
|
|
{
|
|
case 0: res=(T1ReadByte(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i) > T1ReadByte(mem, i)); break;
|
|
case 1: res=(T1ReadByte(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i) < T1ReadByte(mem, i)); break;
|
|
case 2: res=(T1ReadByte(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i) == T1ReadByte(mem, i)); break;
|
|
case 3: res=(T1ReadByte(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i) != T1ReadByte(mem, i)); break;
|
|
default: res = FALSE; break;
|
|
}
|
|
if ( res )
|
|
{
|
|
statMem[addr] |= (1<<offs);
|
|
amount++;
|
|
continue;
|
|
}
|
|
statMem[addr] &= ~(1<<offs);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 1: // 2 bytes
|
|
for (u32 i = 0; i < (4 * 1024 * 1024); i+=2)
|
|
{
|
|
u32 addr = (i >> 3);
|
|
u32 offs = (i % 8);
|
|
if (statMem[addr] & (3<<offs))
|
|
{
|
|
switch (comp)
|
|
{
|
|
case 0: res=(T1ReadWord(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i) > T1ReadWord(mem, i)); break;
|
|
case 1: res=(T1ReadWord(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i) < T1ReadWord(mem, i)); break;
|
|
case 2: res=(T1ReadWord(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i) == T1ReadWord(mem, i)); break;
|
|
case 3: res=(T1ReadWord(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i) != T1ReadWord(mem, i)); break;
|
|
default: res = FALSE; break;
|
|
}
|
|
if ( res )
|
|
{
|
|
statMem[addr] |= (3<<offs);
|
|
amount++;
|
|
continue;
|
|
}
|
|
statMem[addr] &= ~(3<<offs);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 2: // 3 bytes
|
|
for (u32 i = 0; i < (4 * 1024 * 1024); i+=3)
|
|
{
|
|
u32 addr = (i >> 3);
|
|
u32 offs = (i % 8);
|
|
if (statMem[addr] & (7<<offs))
|
|
{
|
|
switch (comp)
|
|
{
|
|
case 0: res=((T1ReadLong(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i) & 0x00FFFFFF) > (T1ReadLong(mem, i) & 0x00FFFFFF) ); break;
|
|
case 1: res=((T1ReadLong(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i) & 0x00FFFFFF) < (T1ReadLong(mem, i) & 0x00FFFFFF) ); break;
|
|
case 2: res=((T1ReadLong(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i) & 0x00FFFFFF) == (T1ReadLong(mem, i) & 0x00FFFFFF) ); break;
|
|
case 3: res=((T1ReadLong(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i) & 0x00FFFFFF) != (T1ReadLong(mem, i) & 0x00FFFFFF) ); break;
|
|
default: res = FALSE; break;
|
|
}
|
|
if ( res )
|
|
{
|
|
statMem[addr] |= (7<<offs);
|
|
amount++;
|
|
continue;
|
|
}
|
|
statMem[addr] &= ~(7<<offs);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 3: // 4 bytes
|
|
for (u32 i = 0; i < (4 * 1024 * 1024); i+=4)
|
|
{
|
|
u32 addr = (i >> 3);
|
|
u32 offs = (i % 8);
|
|
if (statMem[addr] & (0xF<<offs))
|
|
{
|
|
switch (comp)
|
|
{
|
|
case 0: res=(T1ReadLong(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i) > T1ReadLong(mem, i)); break;
|
|
case 1: res=(T1ReadLong(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i) < T1ReadLong(mem, i)); break;
|
|
case 2: res=(T1ReadLong(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i) == T1ReadLong(mem, i)); break;
|
|
case 3: res=(T1ReadLong(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i) != T1ReadLong(mem, i)); break;
|
|
default: res = FALSE; break;
|
|
}
|
|
if ( res )
|
|
{
|
|
statMem[addr] |= (0xF<<offs);
|
|
amount++;
|
|
continue;
|
|
}
|
|
statMem[addr] &= ~(0xF<<offs);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
memcpy(mem, MMU.MMU_MEM[0][0x20], ( 4 * 1024 * 1024 ) );
|
|
|
|
return (amount);
|
|
}
|
|
|
|
u32 CHEATSEARCH::getAmount()
|
|
{
|
|
return (amount);
|
|
}
|
|
|
|
BOOL CHEATSEARCH::getList(u32 *address, u32 *curVal)
|
|
{
|
|
u8 step = (_size+1);
|
|
u8 stepMem = 1;
|
|
switch (_size)
|
|
{
|
|
case 1: stepMem = 0x3; break;
|
|
case 2: stepMem = 0x7; break;
|
|
case 3: stepMem = 0xF; break;
|
|
}
|
|
|
|
for (u32 i = lastRecord; i < (4 * 1024 * 1024); i+=step)
|
|
{
|
|
u32 addr = (i >> 3);
|
|
u32 offs = (i % 8);
|
|
if (statMem[addr] & (stepMem<<offs))
|
|
{
|
|
*address = i;
|
|
lastRecord = i+step;
|
|
|
|
switch (_size)
|
|
{
|
|
case 0: *curVal=(u32)T1ReadByte(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i); return TRUE;
|
|
case 1: *curVal=(u32)T1ReadWord(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i); return TRUE;
|
|
case 2: *curVal=(u32)T1ReadLong(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i) & 0x00FFFFFF; return TRUE;
|
|
case 3: *curVal=(u32)T1ReadLong(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i); return TRUE;
|
|
default: return TRUE;
|
|
}
|
|
}
|
|
}
|
|
lastRecord = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
void CHEATSEARCH::getListReset()
|
|
{
|
|
lastRecord = 0;
|
|
}
|
|
|
|
// ========================================================================= Export
|
|
void CHEATSEXPORT::R4decrypt(u8 *buf, u32 len, u32 n)
|
|
{
|
|
size_t r = 0;
|
|
while (r < len)
|
|
{
|
|
size_t i ;
|
|
u16 key = n ^ 0x484A;
|
|
for (i = 0 ; i < 512 && i < len - r ; i ++)
|
|
{
|
|
u8 _xor = 0;
|
|
if (key & 0x4000) _xor |= 0x80;
|
|
if (key & 0x1000) _xor |= 0x40;
|
|
if (key & 0x0800) _xor |= 0x20;
|
|
if (key & 0x0200) _xor |= 0x10;
|
|
if (key & 0x0080) _xor |= 0x08;
|
|
if (key & 0x0040) _xor |= 0x04;
|
|
if (key & 0x0002) _xor |= 0x02;
|
|
if (key & 0x0001) _xor |= 0x01;
|
|
|
|
u32 k = ((buf[i] << 8) ^ key) << 16;
|
|
u32 x = k;
|
|
for (u8 j = 1; j < 32; j ++)
|
|
x ^= k >> j;
|
|
key = 0x0000;
|
|
if (BIT_N(x, 23)) key |= 0x8000;
|
|
if (BIT_N(k, 22)) key |= 0x4000;
|
|
if (BIT_N(k, 21)) key |= 0x2000;
|
|
if (BIT_N(k, 20)) key |= 0x1000;
|
|
if (BIT_N(k, 19)) key |= 0x0800;
|
|
if (BIT_N(k, 18)) key |= 0x0400;
|
|
if (BIT_N(k, 17) != BIT_N(x, 31)) key |= 0x0200;
|
|
if (BIT_N(k, 16) != BIT_N(x, 30)) key |= 0x0100;
|
|
if (BIT_N(k, 30) != BIT_N(k, 29)) key |= 0x0080;
|
|
if (BIT_N(k, 29) != BIT_N(k, 28)) key |= 0x0040;
|
|
if (BIT_N(k, 28) != BIT_N(k, 27)) key |= 0x0020;
|
|
if (BIT_N(k, 27) != BIT_N(k, 26)) key |= 0x0010;
|
|
if (BIT_N(k, 26) != BIT_N(k, 25)) key |= 0x0008;
|
|
if (BIT_N(k, 25) != BIT_N(k, 24)) key |= 0x0004;
|
|
if (BIT_N(k, 25) != BIT_N(x, 26)) key |= 0x0002;
|
|
if (BIT_N(k, 24) != BIT_N(x, 25)) key |= 0x0001;
|
|
buf[i] ^= _xor;
|
|
}
|
|
|
|
buf+= 512;
|
|
r += 512;
|
|
n += 1;
|
|
}
|
|
}
|
|
|
|
bool CHEATSEXPORT::load(char *path)
|
|
{
|
|
error = 0;
|
|
|
|
fp = fopen(path, "rb");
|
|
if (!fp)
|
|
{
|
|
printf("Error open database\n");
|
|
error = 1;
|
|
return false;
|
|
}
|
|
|
|
const char *headerID = "R4 CheatCode";
|
|
char buf[255] = {0};
|
|
fread(buf, 1, strlen(headerID), fp);
|
|
if (strncmp(buf, headerID, strlen(headerID)) != 0)
|
|
{
|
|
// check encrypted
|
|
R4decrypt((u8 *)buf, strlen(headerID), 0);
|
|
if (strcmp(buf, headerID) != 0)
|
|
{
|
|
error = 2;
|
|
return false;
|
|
}
|
|
encrypted = true;
|
|
}
|
|
|
|
fseek(fp, 0, SEEK_END);
|
|
fsize = ftell(fp);
|
|
fseek(fp, 0, SEEK_SET);
|
|
|
|
if (!search())
|
|
{
|
|
printf("ERROR: cheat in database not found\n");
|
|
error = 3;
|
|
return false;
|
|
}
|
|
|
|
if (!getCodes())
|
|
{
|
|
printf("ERROR: export cheats failed\n");
|
|
error = 4;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
void CHEATSEXPORT::close()
|
|
{
|
|
if (fp)
|
|
fclose(fp);
|
|
if (cheats)
|
|
{
|
|
delete [] cheats;
|
|
cheats = NULL;
|
|
}
|
|
}
|
|
|
|
bool CHEATSEXPORT::search()
|
|
{
|
|
if (!fp) return false;
|
|
|
|
u32 pos = 0x0100;
|
|
FAT_R4 fat_tmp = {0};
|
|
u8 buf[512] = {0};
|
|
|
|
CRC = 0;
|
|
encOffset = 0;
|
|
u32 t = 0;
|
|
memset(date, 0, sizeof(date));
|
|
if (encrypted)
|
|
{
|
|
fseek(fp, 0, SEEK_SET);
|
|
fread(&buf[0], 1, 512, fp);
|
|
R4decrypt((u8 *)&buf[0], 512, 0);
|
|
memcpy(&date[0], &buf[0x10], 16);
|
|
}
|
|
else
|
|
{
|
|
fseek(fp, 0x10, SEEK_SET);
|
|
fread(&date, 16, 1, fp);
|
|
fseek(fp, pos, SEEK_SET);
|
|
fread(&fat_tmp, sizeof(fat), 1, fp);
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
if (encrypted)
|
|
{
|
|
memcpy(&fat, &buf[pos % 512], sizeof(fat));
|
|
pos += sizeof(fat);
|
|
if ((pos>>9) > t)
|
|
{
|
|
t++;
|
|
fread(&buf[0], 1, 512, fp);
|
|
R4decrypt((u8 *)&buf[0], 512, t);
|
|
}
|
|
memcpy(&fat_tmp, &buf[pos % 512], sizeof(fat_tmp)); // next
|
|
}
|
|
else
|
|
{
|
|
memcpy(&fat, &fat_tmp, sizeof(fat));
|
|
fread(&fat_tmp, sizeof(fat_tmp), 1, fp);
|
|
|
|
}
|
|
//printf("serial: %s, offset %08X\n", fat.serial, fat.addr);
|
|
if (memcmp(gameInfo.header.gameCode, &fat.serial[0], 4) == 0)
|
|
{
|
|
dataSize = fat_tmp.addr?(fat_tmp.addr - fat.addr):0;
|
|
if (encrypted)
|
|
{
|
|
encOffset = fat.addr % 512;
|
|
dataSize += encOffset;
|
|
}
|
|
if (!dataSize) return false;
|
|
CRC = fat.CRC;
|
|
char buf[5] = {0};
|
|
memcpy(&buf, &fat.serial[0], 4);
|
|
printf("Cheats: found %s CRC %08X at 0x%08llX, size %i byte(s)\n", buf, fat.CRC, fat.addr, dataSize - encOffset);
|
|
return true;
|
|
}
|
|
|
|
if (fat.addr == 0) break;
|
|
}
|
|
|
|
memset(&fat, 0, sizeof(FAT_R4));
|
|
return false;
|
|
}
|
|
|
|
bool CHEATSEXPORT::getCodes()
|
|
{
|
|
if (!fp) return false;
|
|
|
|
u32 pos = 0;
|
|
u32 pos_cht = 0;
|
|
|
|
u8 *data = new u8 [dataSize+8];
|
|
if (!data) return false;
|
|
memset(data, 0, dataSize+8);
|
|
|
|
fseek(fp, fat.addr - encOffset, SEEK_SET);
|
|
|
|
if (fread(data, 1, dataSize, fp) != dataSize)
|
|
{
|
|
delete [] data;
|
|
data = NULL;
|
|
return false;
|
|
}
|
|
|
|
if (encrypted)
|
|
R4decrypt(data, dataSize, fat.addr >> 9);
|
|
|
|
intptr_t ptrMask = (~0 << 2);
|
|
u8 *gameTitlePtr = (u8 *)data + encOffset;
|
|
|
|
memset(gametitle, 0, CHEAT_DB_GAME_TITLE_SIZE);
|
|
memcpy(gametitle, gameTitlePtr, strlen((const char *)gameTitlePtr));
|
|
|
|
u32 *cmd = (u32 *)(((intptr_t)gameTitlePtr + strlen((const char *)gameTitlePtr) + 4) & ptrMask);
|
|
numCheats = cmd[0] & 0x0FFFFFFF;
|
|
cmd += 9;
|
|
cheats = new CHEATS_LIST[numCheats];
|
|
memset(cheats, 0, sizeof(CHEATS_LIST) * numCheats);
|
|
|
|
while (pos < numCheats)
|
|
{
|
|
u32 folderNum = 1;
|
|
u8 *folderName = NULL;
|
|
u8 *folderNote = NULL;
|
|
if ((*cmd & 0xF0000000) == 0x10000000) // Folder
|
|
{
|
|
folderNum = (*cmd & 0x00FFFFFF);
|
|
folderName = (u8*)((intptr_t)cmd + 4);
|
|
folderNote = (u8*)((intptr_t)folderName + strlen((char*)folderName) + 1);
|
|
pos++;
|
|
cmd = (u32 *)(((intptr_t)folderName + strlen((char*)folderName) + 1 + strlen((char*)folderNote) + 1 + 3) & ptrMask);
|
|
}
|
|
|
|
for (u32 i = 0; i < folderNum; i++) // in folder
|
|
{
|
|
u8 *cheatName = (u8 *)((intptr_t)cmd + 4);
|
|
u8 *cheatNote = (u8 *)((intptr_t)cheatName + strlen((char*)cheatName) + 1);
|
|
u32 *cheatData = (u32 *)(((intptr_t)cheatNote + strlen((char*)cheatNote) + 1 + 3) & ptrMask);
|
|
u32 cheatDataLen = *cheatData++;
|
|
u32 numberCodes = cheatDataLen / 2;
|
|
|
|
if (numberCodes <= MAX_XX_CODE)
|
|
{
|
|
std::string descriptionStr = "";
|
|
|
|
if ( folderName && *folderName )
|
|
{
|
|
descriptionStr += (char *)folderName;
|
|
descriptionStr += ": ";
|
|
}
|
|
|
|
descriptionStr += (char *)cheatName;
|
|
|
|
if ( cheatNote && *cheatNote )
|
|
{
|
|
descriptionStr += " | ";
|
|
descriptionStr += (char *)cheatNote;
|
|
}
|
|
|
|
strncpy(cheats[pos_cht].description, descriptionStr.c_str(), sizeof(cheats[pos_cht].description));
|
|
cheats[pos_cht].description[sizeof(cheats[pos_cht].description) - 1] = '\0';
|
|
|
|
cheats[pos_cht].num = numberCodes;
|
|
cheats[pos_cht].type = 1;
|
|
|
|
for(u32 j = 0, t = 0; j < numberCodes; j++, t+=2 )
|
|
{
|
|
cheats[pos_cht].code[j][0] = (u32)*(cheatData+t);
|
|
//printf("%i: %08X ", j, cheats[pos_cht].code[j][0]);
|
|
cheats[pos_cht].code[j][1] = (u32)*(cheatData+t+1);
|
|
//printf("%08X\n", cheats[pos_cht].code[j][1]);
|
|
|
|
}
|
|
pos_cht++;
|
|
}
|
|
|
|
pos++;
|
|
cmd = (u32 *)((intptr_t)cmd + ((*cmd + 1)*4));
|
|
}
|
|
|
|
};
|
|
|
|
delete [] data;
|
|
|
|
numCheats = pos_cht;
|
|
//for (int i = 0; i < numCheats; i++)
|
|
// printf("%i: %s\n", i, cheats[i].description);
|
|
|
|
return true;
|
|
}
|
|
|
|
CHEATS_LIST *CHEATSEXPORT::getCheats()
|
|
{
|
|
return cheats;
|
|
}
|
|
u32 CHEATSEXPORT::getCheatsNum()
|
|
{
|
|
return numCheats;
|
|
}
|