// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. // Copyright (C) 1999-2003 Forgotten // Copyright (C) 2005 Forgotten and the VBA development team // This program 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, or(at your option) // any later version. // // This program 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 this program; if not, write to the Free Software Foundation, // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #include #include #include #include "../System.h" #include "../NLS.h" #include "../Util.h" #include "gbCheats.h" #include "gbGlobals.h" #include "gb.h" gbCheat gbCheatList[100]; int gbCheatNumber = 0; int gbNextCheat = 0; bool gbCheatMap[0x10000]; extern bool cheatsEnabled; #define GBCHEAT_IS_HEX(a) ( ((a)>='A' && (a) <='F') || ((a) >='0' && (a) <= '9')) #define GBCHEAT_HEX_VALUE(a) ( (a) >= 'A' ? (a) - 'A' + 10 : (a) - '0') void gbCheatUpdateMap() { memset(gbCheatMap, 0, 0x10000); for(int i = 0; i < gbCheatNumber; i++) { if(gbCheatList[i].enabled) gbCheatMap[gbCheatList[i].address] = true; } } void gbCheatsSaveGame(gzFile gzFile) { utilWriteInt(gzFile, gbCheatNumber); if(gbCheatNumber>0) utilGzWrite(gzFile, &gbCheatList[0], sizeof(gbCheat)*gbCheatNumber); } void gbCheatsReadGame(gzFile gzFile, int version) { if(version <= 8) { int gbGgOn = utilReadInt(gzFile); if(gbGgOn) { int n = utilReadInt(gzFile); gbXxCheat tmpCheat; for(int i = 0; i < n; i++) { utilGzRead(gzFile,&tmpCheat, sizeof(gbXxCheat)); gbAddGgCheat(tmpCheat.cheatCode, tmpCheat.cheatDesc); } } int gbGsOn = utilReadInt(gzFile); if(gbGsOn) { int n = utilReadInt(gzFile); gbXxCheat tmpCheat; for(int i = 0; i < n; i++) { utilGzRead(gzFile,&tmpCheat, sizeof(gbXxCheat)); gbAddGsCheat(tmpCheat.cheatCode, tmpCheat.cheatDesc); } } } else { gbCheatNumber = utilReadInt(gzFile); if(gbCheatNumber>0) { utilGzRead(gzFile, &gbCheatList[0], sizeof(gbCheat)*gbCheatNumber); } } gbCheatUpdateMap(); } void gbCheatsSaveCheatList(const char *file) { if(gbCheatNumber == 0) return; FILE *f = fopen(file, "wb"); if(f == NULL) return; int version = 1; fwrite(&version, 1, sizeof(version), f); int type = 1; fwrite(&type, 1, sizeof(type), f); fwrite(&gbCheatNumber, 1, sizeof(gbCheatNumber), f); fwrite(gbCheatList, 1, sizeof(gbCheatList), f); fclose(f); } bool gbCheatsLoadCheatList(const char *file) { gbCheatNumber = 0; gbCheatUpdateMap(); int count = 0; FILE *f = fopen(file, "rb"); if(f == NULL) return false; int version = 0; if(fread(&version, 1, sizeof(version), f) != sizeof(version)) { fclose(f); return false; } if(version != 1) { systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_VERSION, N_("Unsupported cheat list version %d"), version); fclose(f); return false; } int type = 0; if(fread(&type, 1, sizeof(type), f) != sizeof(type)) { fclose(f); return false; } if(type != 1) { systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_TYPE, N_("Unsupported cheat list type %d"), type); fclose(f); return false; } if(fread(&count, 1, sizeof(count), f) != sizeof(count)) { fclose(f); return false; } if(fread(gbCheatList, 1, sizeof(gbCheatList), f) != sizeof(gbCheatList)) { fclose(f); return false; } gbCheatNumber = count; gbCheatUpdateMap(); return true; } bool gbVerifyGsCode(const char *code) { size_t len = strlen(code); if(len == 0) return true; if(len != 8) return false; for(int i = 0; i < 8; i++) if(!GBCHEAT_IS_HEX(code[i])) return false; int address = GBCHEAT_HEX_VALUE(code[6]) << 12 | GBCHEAT_HEX_VALUE(code[7]) << 8 | GBCHEAT_HEX_VALUE(code[4]) << 4 | GBCHEAT_HEX_VALUE(code[5]); return true; } bool gbAddGsCheat(const char *code, const char *desc) { if(gbCheatNumber > 99) { systemMessage(MSG_MAXIMUM_NUMBER_OF_CHEATS, N_("Maximum number of cheats reached.")); return false; } if(!gbVerifyGsCode(code)) { systemMessage(MSG_INVALID_GAMESHARK_CODE, N_("Invalid GameShark code: %s"), code); return false; } int i = gbCheatNumber; strcpy(gbCheatList[i].cheatCode, code); strcpy(gbCheatList[i].cheatDesc, desc); gbCheatList[i].code = GBCHEAT_HEX_VALUE(code[0]) << 4 | GBCHEAT_HEX_VALUE(code[1]); gbCheatList[i].value = GBCHEAT_HEX_VALUE(code[2]) << 4 | GBCHEAT_HEX_VALUE(code[3]); gbCheatList[i].address = GBCHEAT_HEX_VALUE(code[6]) << 12 | GBCHEAT_HEX_VALUE(code[7]) << 8 | GBCHEAT_HEX_VALUE(code[4]) << 4 | GBCHEAT_HEX_VALUE(code[5]); gbCheatList[i].compare = 0; gbCheatList[i].enabled = true; int gsCode = gbCheatList[i].code; if ((gsCode !=1) && ((gsCode & 0xF0) !=0x80) && ((gsCode & 0xF0) !=0x90) && ((gsCode & 0xF0) !=0xA0) && ((gsCode) !=0xF0) && ((gsCode) !=0xF1)) systemMessage(MSG_WRONG_GAMESHARK_CODE, N_("Wrong GameShark code type : %s"), code); else if (((gsCode & 0xF0) ==0xA0) || ((gsCode) ==0xF0) || ((gsCode) ==0xF1)) systemMessage(MSG_UNSUPPORTED_GAMESHARK_CODE, N_("Unsupported GameShark code type : %s"), code); gbCheatNumber++; return true; } bool gbVerifyGgCode(const char *code) { size_t len = strlen(code); if(len != 11 && len != 7 && len != 6 && len != 0) return false; if(len == 0) return true; if(!GBCHEAT_IS_HEX(code[0])) return false; if(!GBCHEAT_IS_HEX(code[1])) return false; if(!GBCHEAT_IS_HEX(code[2])) return false; if(code[3] != '-') return false; if(!GBCHEAT_IS_HEX(code[4])) return false; if(!GBCHEAT_IS_HEX(code[5])) return false; if(!GBCHEAT_IS_HEX(code[6])) return false; if(code[7] != 0) { if(code[7] != '-') return false; if(code[8] != 0) { if(!GBCHEAT_IS_HEX(code[8])) return false; if(!GBCHEAT_IS_HEX(code[9])) return false; if(!GBCHEAT_IS_HEX(code[10])) return false; } } // int replace = (GBCHEAT_HEX_VALUE(code[0]) << 4) + // GBCHEAT_HEX_VALUE(code[1]); int address = (GBCHEAT_HEX_VALUE(code[2]) << 8) + (GBCHEAT_HEX_VALUE(code[4]) << 4) + (GBCHEAT_HEX_VALUE(code[5])) + ((GBCHEAT_HEX_VALUE(code[6]) ^ 0x0f) << 12); if(address >= 0x8000 && address <= 0x9fff) return false; if(address >= 0xc000) return false; if(code[7] == 0 || code[8] == '0') return true; int compare = (GBCHEAT_HEX_VALUE(code[8]) << 4) + (GBCHEAT_HEX_VALUE(code[10])); compare = compare ^ 0xff; compare = (compare >> 2) | ( (compare << 6) & 0xc0); compare ^= 0x45; int cloak = (GBCHEAT_HEX_VALUE(code[8])) ^ (GBCHEAT_HEX_VALUE(code[9])); if(cloak >=1 && cloak <= 7) return false; return true; } bool gbAddGgCheat(const char *code, const char *desc) { if(gbCheatNumber > 99) { systemMessage(MSG_MAXIMUM_NUMBER_OF_CHEATS, N_("Maximum number of cheats reached.")); return false; } if(!gbVerifyGgCode(code)) { systemMessage(MSG_INVALID_GAMEGENIE_CODE, N_("Invalid GameGenie code: %s"), code); return false; } int i = gbCheatNumber; size_t len = strlen(code); strcpy(gbCheatList[i].cheatCode, code); strcpy(gbCheatList[i].cheatDesc, desc); gbCheatList[i].code = 0x101; gbCheatList[i].value = (GBCHEAT_HEX_VALUE(code[0]) << 4) + GBCHEAT_HEX_VALUE(code[1]); gbCheatList[i].address = (GBCHEAT_HEX_VALUE(code[2]) << 8) + (GBCHEAT_HEX_VALUE(code[4]) << 4) + (GBCHEAT_HEX_VALUE(code[5])) + ((GBCHEAT_HEX_VALUE(code[6]) ^ 0x0f) << 12); gbCheatList[i].compare = 0; if(len != 7 && len != 8) { int compare = (GBCHEAT_HEX_VALUE(code[8]) << 4) + (GBCHEAT_HEX_VALUE(code[10])); compare = compare ^ 0xff; compare = (compare >> 2) | ( (compare << 6) & 0xc0); compare ^= 0x45; gbCheatList[i].compare = compare; //gbCheatList[i].code = 0; gbCheatList[i].code = 0x100; // fix for compare value } gbCheatList[i].enabled = true; gbCheatMap[gbCheatList[i].address] = true; gbCheatNumber++; return true; } void gbCheatRemove(int i) { if(i < 0 || i >= gbCheatNumber) { systemMessage(MSG_INVALID_CHEAT_TO_REMOVE, N_("Invalid cheat to remove %d"), i); return; } if((i+1) < gbCheatNumber) { memcpy(&gbCheatList[i], &gbCheatList[i+1], sizeof(gbCheat)* (gbCheatNumber-i-1)); } gbCheatNumber--; gbCheatUpdateMap(); } void gbCheatRemoveAll() { gbCheatNumber = 0; gbCheatUpdateMap(); } void gbCheatEnable(int i) { if(i >=0 && i < gbCheatNumber) { if(!gbCheatList[i].enabled) { gbCheatList[i].enabled = true; gbCheatUpdateMap(); } } } void gbCheatDisable(int i) { if(i >=0 && i < gbCheatNumber) { if(gbCheatList[i].enabled) { gbCheatList[i].enabled = false; gbCheatUpdateMap(); } } } bool gbCheatReadGSCodeFile(const char *fileName) { FILE *file = fopen(fileName, "rb"); if(!file) { systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); return false; } fseek(file, 0x18, SEEK_SET); int count = 0; fread(&count, 1, 2, file); int dummy = 0; gbCheatRemoveAll(); char desc[13]; char code[9]; int i; for(i = 0; i < count; i++) { fread(&dummy, 1, 2, file); fread(desc, 1, 12, file); desc[12] = 0; fread(code, 1, 8, file); code[8] = 0; gbAddGsCheat(code, desc); } for(i = 0; i < gbCheatNumber; i++) gbCheatDisable(i); fclose(file); return true; } // Used to emulated GG codes u8 gbCheatRead(u16 address) { if(!cheatsEnabled) return gbMemoryMap[address>>12][address & 0xFFF]; for(int i = 0; i < gbCheatNumber; i++) { if(gbCheatList[i].enabled && gbCheatList[i].address == address) { switch(gbCheatList[i].code) { case 0x100: // GameGenie support if(gbMemoryMap[address>>12][address&0xFFF] == gbCheatList[i].compare) return gbCheatList[i].value; break; case 0x101: // GameGenie 6 digits code support return gbCheatList[i].value; break; } } } return gbMemoryMap[address>>12][address&0xFFF]; } // Used to emulate GS codes. void gbCheatWrite(bool reboot) { if(cheatsEnabled) { u16 address = 0; if (gbNextCheat >= gbCheatNumber) gbNextCheat = 0; for(int i = gbNextCheat; i < gbCheatNumber; i++) { if(gbCheatList[i].enabled) { address = gbCheatList[i].address; if ((!reboot) && (address >= 0x8000) && !((address>=0xA000) && (address<0xC000))) { // These codes are executed one per one, at each Vblank switch(gbCheatList[i].code) { case 0x01: gbWriteMemory(address, gbCheatList[i].value); gbNextCheat = i+1; return; case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97: case 0x98: case 0x99: case 0x9A: case 0x9B: case 0x9C: case 0x9D: case 0x9E: case 0x9F: int oldbank = gbMemory[0xff70]; gbWriteMemory(0xff70, gbCheatList[i].code & 0xf); gbWriteMemory(address, gbCheatList[i].value); gbWriteMemory(0xff70, oldbank); gbNextCheat = i+1; return; } } else // These codes are only executed when the game is booted { switch(gbCheatList[i].code & 0xF0) { case 0x80: gbWriteMemory(0x0000, 0x0A); gbWriteMemory(0x4000, gbCheatList[i].value & 0xF); gbWriteMemory(address, gbCheatList[i].value); gbNextCheat = i+1; return; } } } } } }