/* 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 . */ #include #include "cheatSystem.h" #include "NDSSystem.h" #include "mem.h" #include "MMU.h" #include "debug.h" #include "utils/xstring.h" #ifndef _MSC_VER #include #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(addr, lo); } } break; case 0x01: // 1XXXXXXX 0000YYYY half[XXXXXXX+offset] = YYYY addr = hi + offset; _MMU_write16(addr, lo); break; case 0x02: // 2XXXXXXX 000000YY byte[XXXXXXX+offset] = YY addr = hi + offset; _MMU_write08(addr, lo); break; case 0x03: // 3XXXXXXX YYYYYYYY IF YYYYYYYY > word[XXXXXXX] ;unsigned if (hi == 0) hi = offset; // V1.54+ val = _MMU_read32(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(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(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(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(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(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(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(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(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(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(addr, datareg); offset += 4; break; case 0x7: // D7000000 XXXXXXXX half[XXXXXXXX+offset]=datareg, offset=offset+2 addr = lo + offset; _MMU_write16(addr, datareg); offset += 2; break; case 0x8: // D8000000 XXXXXXXX byte[XXXXXXXX+offset]=datareg, offset=offset+1 addr = lo + offset; _MMU_write08(addr, datareg); offset += 1; break; case 0x9: // D9000000 XXXXXXXX datareg = word[XXXXXXXX+offset] addr = lo + offset; datareg = _MMU_read32(addr); break; case 0xA: // DA000000 XXXXXXXX datareg = half[XXXXXXXX+offset] addr = lo + offset; datareg = _MMU_read16(addr); break; case 0xB: // DB000000 XXXXXXXX datareg = byte[XXXXXXXX+offset] ;bugged on pre-v1.54 addr = lo + offset; datareg = _MMU_read08(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(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(offset+t); _MMU_write08(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(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(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(addr,val); break; case 1: _MMU_write16(addr,val); break; case 2: { u32 tmp = _MMU_read32(addr); tmp &= 0xFF000000; tmp |= (val & 0x00FFFFFF); _MMU_write32(addr,tmp); break; } case 3: _MMU_write32(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<> 3); u32 offs = (i % 8); if (statMem[addr] & (3<> 3); u32 offs = (i % 8); if (statMem[addr] & (0x7<> 3); u32 offs = (i % 8); if (statMem[addr] & (0xF<> 3); u32 offs = (i % 8); if (statMem[addr] & (1< 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<> 3); u32 offs = (i % 8); if (statMem[addr] & (3< 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<> 3); u32 offs = (i % 8); if (statMem[addr] & (7< (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<> 3); u32 offs = (i % 8); if (statMem[addr] & (0xF< 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<> 3); u32 offs = (i % 8); if (statMem[addr] & (stepMem<> 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; }