rlm@1: #include rlm@1: #include rlm@1: #include rlm@1: #include rlm@1: rlm@1: #include "../NLS.h" rlm@1: #include "../common/System.h" rlm@1: #include "../common/Util.h" rlm@1: rlm@1: #include "gbCheats.h" rlm@1: #include "gbGlobals.h" rlm@1: rlm@1: gbCheat gbCheatList[100]; rlm@1: int gbCheatNumber = 0; rlm@1: bool gbCheatMap[0x10000]; rlm@1: rlm@1: extern bool8 cheatsEnabled; rlm@1: rlm@1: #define GBCHEAT_IS_HEX(a) (((a) >= 'A' && (a) <= 'F') || ((a) >= '0' && (a) <= '9')) rlm@1: #define GBCHEAT_HEX_VALUE(a) ((a) >= 'A' ? (a) - 'A' + 10 : (a) - '0') rlm@1: rlm@1: void gbCheatUpdateMap() rlm@1: { rlm@19: memset(gbCheatMap, 0, 0x10000); rlm@1: rlm@19: for (int i = 0; i < gbCheatNumber; i++) rlm@19: { rlm@19: if (gbCheatList[i].enabled) rlm@19: gbCheatMap[gbCheatList[i].address] = true; rlm@19: } rlm@1: } rlm@1: rlm@1: void gbCheatsSaveGame(gzFile gzFile) rlm@1: { rlm@19: utilWriteInt(gzFile, gbCheatNumber); rlm@19: if (gbCheatNumber) rlm@19: utilGzWrite(gzFile, &gbCheatList[0], sizeof(gbCheat)*gbCheatNumber); rlm@1: } rlm@1: rlm@1: void gbCheatsReadGame(gzFile gzFile, int version) rlm@1: { rlm@19: if (version <= 8) rlm@19: { rlm@19: int gbGgOn = utilReadInt(gzFile); rlm@19: rlm@19: if (gbGgOn) rlm@1: { rlm@19: int n = utilReadInt(gzFile); rlm@19: gbXxCheat tmpCheat; rlm@19: for (int i = 0; i < n; i++) rlm@19: { rlm@19: utilGzRead(gzFile, &tmpCheat, sizeof(gbXxCheat)); rlm@19: gbAddGgCheat(tmpCheat.cheatCode, tmpCheat.cheatDesc); rlm@19: } rlm@1: } rlm@1: rlm@19: int gbGsOn = utilReadInt(gzFile); rlm@19: rlm@19: if (gbGsOn) rlm@19: { rlm@19: int n = utilReadInt(gzFile); rlm@19: gbXxCheat tmpCheat; rlm@19: for (int i = 0; i < n; i++) rlm@19: { rlm@19: utilGzRead(gzFile, &tmpCheat, sizeof(gbXxCheat)); rlm@19: gbAddGsCheat(tmpCheat.cheatCode, tmpCheat.cheatDesc); rlm@19: } rlm@19: } rlm@19: } rlm@19: else rlm@19: { rlm@19: gbCheatNumber = utilReadInt(gzFile); rlm@19: rlm@19: if (gbCheatNumber) rlm@19: { rlm@19: utilGzRead(gzFile, &gbCheatList[0], sizeof(gbCheat)*gbCheatNumber); rlm@19: } rlm@19: } rlm@19: rlm@19: gbCheatUpdateMap(); rlm@1: } rlm@1: rlm@1: void gbCheatsSaveCheatList(const char *file) rlm@1: { rlm@19: if (gbCheatNumber == 0) rlm@19: return; rlm@19: FILE *f = fopen(file, "wb"); rlm@19: if (f == NULL) rlm@19: return; rlm@19: int version = 1; rlm@19: fwrite(&version, 1, sizeof(version), f); rlm@19: int type = 1; rlm@19: fwrite(&type, 1, sizeof(type), f); rlm@19: fwrite(&gbCheatNumber, 1, sizeof(gbCheatNumber), f); rlm@19: fwrite(gbCheatList, 1, sizeof(gbCheatList), f); rlm@19: fclose(f); rlm@1: } rlm@1: rlm@1: bool gbCheatsLoadCheatList(const char *file) rlm@1: { rlm@19: gbCheatNumber = 0; rlm@1: rlm@19: gbCheatUpdateMap(); rlm@1: rlm@19: int count = 0; rlm@1: rlm@19: FILE *f = fopen(file, "rb"); rlm@1: rlm@19: if (f == NULL) rlm@19: return false; rlm@1: rlm@19: int version = 0; rlm@1: rlm@19: if (fread(&version, 1, sizeof(version), f) != sizeof(version)) rlm@19: { rlm@19: fclose(f); rlm@19: return false; rlm@19: } rlm@1: rlm@19: if (version != 1) rlm@19: { rlm@19: systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_VERSION, rlm@19: N_("Unsupported cheat list version %d"), version); rlm@19: fclose(f); rlm@19: return false; rlm@19: } rlm@1: rlm@19: int type = 0; rlm@19: if (fread(&type, 1, sizeof(type), f) != sizeof(type)) rlm@19: { rlm@19: fclose(f); rlm@19: return false; rlm@19: } rlm@1: rlm@19: if (type != 1) rlm@19: { rlm@19: systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_TYPE, rlm@19: N_("Unsupported cheat list type %d"), type); rlm@19: fclose(f); rlm@19: return false; rlm@19: } rlm@1: rlm@19: if (fread(&count, 1, sizeof(count), f) != sizeof(count)) rlm@19: { rlm@19: fclose(f); rlm@19: return false; rlm@19: } rlm@1: rlm@19: if (fread(gbCheatList, 1, sizeof(gbCheatList), f) != sizeof(gbCheatList)) rlm@19: { rlm@19: fclose(f); rlm@19: return false; rlm@19: } rlm@1: rlm@19: fclose(f); rlm@19: gbCheatNumber = count; rlm@19: gbCheatUpdateMap(); rlm@1: rlm@19: return true; rlm@1: } rlm@1: rlm@1: bool gbVerifyGsCode(const char *code) rlm@1: { rlm@19: int len = strlen(code); rlm@1: rlm@19: if (len == 0) rlm@19: return true; rlm@1: rlm@19: if (len != 8) rlm@19: return false; rlm@1: rlm@19: for (int i = 0; i < 8; i++) rlm@19: if (!GBCHEAT_IS_HEX(code[i])) rlm@19: return false; rlm@1: rlm@19: int address = GBCHEAT_HEX_VALUE(code[6]) << 12 | rlm@19: GBCHEAT_HEX_VALUE(code[7]) << 8 | rlm@19: GBCHEAT_HEX_VALUE(code[4]) << 4 | rlm@19: GBCHEAT_HEX_VALUE(code[5]); rlm@1: rlm@19: if (address < 0xa000 || rlm@19: address > 0xdfff) rlm@19: return false; rlm@1: rlm@19: return true; rlm@1: } rlm@1: rlm@1: void gbAddGsCheat(const char *code, const char *desc) rlm@1: { rlm@19: if (gbCheatNumber > 99) rlm@19: { rlm@19: systemMessage(MSG_MAXIMUM_NUMBER_OF_CHEATS, rlm@19: N_("Maximum number of cheats reached.")); rlm@19: return; rlm@19: } rlm@1: rlm@19: if (!gbVerifyGsCode(code)) rlm@19: { rlm@19: systemMessage(MSG_INVALID_GAMESHARK_CODE, rlm@19: N_("Invalid GameShark code: %s"), code); rlm@19: return; rlm@19: } rlm@1: rlm@19: int i = gbCheatNumber; rlm@1: rlm@19: strcpy(gbCheatList[i].cheatCode, code); rlm@19: strcpy(gbCheatList[i].cheatDesc, desc); rlm@1: rlm@19: gbCheatList[i].code = GBCHEAT_HEX_VALUE(code[0]) << 4 | rlm@19: GBCHEAT_HEX_VALUE(code[1]); rlm@1: rlm@19: gbCheatList[i].value = GBCHEAT_HEX_VALUE(code[2]) << 4 | rlm@19: GBCHEAT_HEX_VALUE(code[3]); rlm@1: rlm@19: gbCheatList[i].address = GBCHEAT_HEX_VALUE(code[6]) << 12 | rlm@19: GBCHEAT_HEX_VALUE(code[7]) << 8 | rlm@19: GBCHEAT_HEX_VALUE(code[4]) << 4 | rlm@19: GBCHEAT_HEX_VALUE(code[5]); rlm@1: rlm@19: gbCheatList[i].compare = 0; rlm@1: rlm@19: gbCheatList[i].enabled = true; rlm@1: rlm@19: gbCheatMap[gbCheatList[i].address] = true; rlm@1: rlm@19: gbCheatNumber++; rlm@1: } rlm@1: rlm@1: bool gbVerifyGgCode(const char *code) rlm@1: { rlm@19: int len = strlen(code); rlm@1: rlm@19: if (len != 11 && rlm@19: len != 7 && rlm@19: len != 6 && rlm@19: len != 0) rlm@19: return false; rlm@1: rlm@19: if (len == 0) rlm@19: return true; rlm@1: rlm@19: if (!GBCHEAT_IS_HEX(code[0])) rlm@19: return false; rlm@19: if (!GBCHEAT_IS_HEX(code[1])) rlm@19: return false; rlm@19: if (!GBCHEAT_IS_HEX(code[2])) rlm@19: return false; rlm@19: if (code[3] != '-') rlm@19: return false; rlm@19: if (!GBCHEAT_IS_HEX(code[4])) rlm@19: return false; rlm@19: if (!GBCHEAT_IS_HEX(code[5])) rlm@19: return false; rlm@19: if (!GBCHEAT_IS_HEX(code[6])) rlm@19: return false; rlm@19: if (code[7] != 0) rlm@19: { rlm@19: if (code[7] != '-') rlm@19: return false; rlm@19: if (code[8] != 0) rlm@1: { rlm@19: if (!GBCHEAT_IS_HEX(code[8])) rlm@19: return false; rlm@19: if (!GBCHEAT_IS_HEX(code[9])) rlm@19: return false; rlm@19: if (!GBCHEAT_IS_HEX(code[10])) rlm@19: return false; rlm@1: } rlm@19: } rlm@1: rlm@19: // int replace = (GBCHEAT_HEX_VALUE(code[0]) << 4) + rlm@19: // GBCHEAT_HEX_VALUE(code[1]); rlm@1: rlm@19: int address = (GBCHEAT_HEX_VALUE(code[2]) << 8) + rlm@19: (GBCHEAT_HEX_VALUE(code[4]) << 4) + rlm@19: (GBCHEAT_HEX_VALUE(code[5])) + rlm@19: ((GBCHEAT_HEX_VALUE(code[6]) ^ 0x0f) << 12); rlm@1: rlm@19: if (address >= 0x8000 && address <= 0x9fff) rlm@19: return false; rlm@1: rlm@19: if (address >= 0xc000) rlm@19: return false; rlm@1: rlm@19: if (code[7] == 0 || code[8] == '0') rlm@19: return true; rlm@1: rlm@19: int compare = (GBCHEAT_HEX_VALUE(code[8]) << 4) + rlm@19: (GBCHEAT_HEX_VALUE(code[10])); rlm@19: compare = compare ^ 0xff; rlm@19: compare = (compare >> 2) | ((compare << 6) & 0xc0); rlm@19: compare ^= 0x45; rlm@1: rlm@19: int cloak = (GBCHEAT_HEX_VALUE(code[8])) ^ (GBCHEAT_HEX_VALUE(code[9])); rlm@1: rlm@19: if (cloak >= 1 && cloak <= 7) rlm@19: return false; rlm@1: rlm@19: return true; rlm@1: } rlm@1: rlm@1: void gbAddGgCheat(const char *code, const char *desc) rlm@1: { rlm@19: if (gbCheatNumber > 99) rlm@19: { rlm@19: systemMessage(MSG_MAXIMUM_NUMBER_OF_CHEATS, rlm@19: N_("Maximum number of cheats reached.")); rlm@19: return; rlm@19: } rlm@1: rlm@19: if (!gbVerifyGgCode(code)) rlm@19: { rlm@19: systemMessage(MSG_INVALID_GAMEGENIE_CODE, rlm@19: N_("Invalid GameGenie code: %s"), code); rlm@19: return; rlm@19: } rlm@1: rlm@19: int i = gbCheatNumber; rlm@1: rlm@19: int len = strlen(code); rlm@1: rlm@19: strcpy(gbCheatList[i].cheatCode, code); rlm@19: strcpy(gbCheatList[i].cheatDesc, desc); rlm@1: rlm@19: gbCheatList[i].code = 1; rlm@19: gbCheatList[i].value = (GBCHEAT_HEX_VALUE(code[0]) << 4) + rlm@19: GBCHEAT_HEX_VALUE(code[1]); rlm@1: rlm@19: gbCheatList[i].address = (GBCHEAT_HEX_VALUE(code[2]) << 8) + rlm@19: (GBCHEAT_HEX_VALUE(code[4]) << 4) + rlm@19: (GBCHEAT_HEX_VALUE(code[5])) + rlm@19: ((GBCHEAT_HEX_VALUE(code[6]) ^ 0x0f) << 12); rlm@1: rlm@19: gbCheatList[i].compare = 0; rlm@1: rlm@19: if (len != 7 && len != 8) rlm@19: { rlm@19: int compare = (GBCHEAT_HEX_VALUE(code[8]) << 4) + rlm@19: (GBCHEAT_HEX_VALUE(code[10])); rlm@19: compare = compare ^ 0xff; rlm@19: compare = (compare >> 2) | ((compare << 6) & 0xc0); rlm@19: compare ^= 0x45; rlm@1: rlm@19: gbCheatList[i].compare = compare; rlm@19: gbCheatList[i].code = 0; rlm@19: } rlm@1: rlm@19: gbCheatList[i].enabled = true; rlm@1: rlm@19: gbCheatMap[gbCheatList[i].address] = true; rlm@1: rlm@19: gbCheatNumber++; rlm@1: } rlm@1: rlm@1: void gbCheatRemove(int i) rlm@1: { rlm@19: if (i < 0 || i >= gbCheatNumber) rlm@19: { rlm@19: systemMessage(MSG_INVALID_CHEAT_TO_REMOVE, rlm@19: N_("Invalid cheat to remove %d"), i); rlm@19: return; rlm@19: } rlm@1: rlm@19: if ((i+1) < gbCheatNumber) rlm@19: { rlm@19: memcpy(&gbCheatList[i], &gbCheatList[i+1], sizeof(gbCheat)* rlm@19: (gbCheatNumber-i-1)); rlm@19: } rlm@1: rlm@19: gbCheatNumber--; rlm@1: rlm@19: gbCheatUpdateMap(); rlm@1: } rlm@1: rlm@1: void gbCheatRemoveAll() rlm@1: { rlm@19: gbCheatNumber = 0; rlm@19: gbCheatUpdateMap(); rlm@1: } rlm@1: rlm@1: void gbCheatEnable(int i) rlm@1: { rlm@19: if (i >= 0 && i < gbCheatNumber) rlm@19: { rlm@19: if (!gbCheatList[i].enabled) rlm@1: { rlm@19: gbCheatList[i].enabled = true; rlm@19: gbCheatUpdateMap(); rlm@1: } rlm@19: } rlm@1: } rlm@1: rlm@1: void gbCheatDisable(int i) rlm@1: { rlm@19: if (i >= 0 && i < gbCheatNumber) rlm@19: { rlm@19: if (gbCheatList[i].enabled) rlm@1: { rlm@19: gbCheatList[i].enabled = false; rlm@19: gbCheatUpdateMap(); rlm@1: } rlm@19: } rlm@1: } rlm@1: rlm@1: bool gbCheatReadGSCodeFile(const char *fileName) rlm@1: { rlm@19: FILE *file = fopen(fileName, "rb"); rlm@1: rlm@19: if (!file) rlm@19: return false; rlm@1: rlm@19: fseek(file, 0x18, SEEK_SET); rlm@19: int count = 0; rlm@19: fread(&count, 1, 2, file); rlm@19: int dummy = 0; rlm@19: gbCheatRemoveAll(); rlm@19: char desc[13]; rlm@19: char code[9]; rlm@19: int i; rlm@19: for (i = 0; i < count; i++) rlm@19: { rlm@19: fread(&dummy, 1, 2, file); rlm@19: fread(desc, 1, 12, file); rlm@19: desc[12] = 0; rlm@19: fread(code, 1, 8, file); rlm@19: code[8] = 0; rlm@19: gbAddGsCheat(code, desc); rlm@19: } rlm@1: rlm@19: for (i = 0; i < gbCheatNumber; i++) rlm@19: gbCheatDisable(i); rlm@1: rlm@19: fclose(file); rlm@19: return true; rlm@1: } rlm@1: rlm@1: u8 gbCheatRead(u16 address) rlm@1: { rlm@19: if (!cheatsEnabled) rlm@19: return gbReadMemoryQuick(address); rlm@1: rlm@19: for (int i = 0; i < gbCheatNumber; i++) rlm@19: { rlm@19: if (gbCheatList[i].enabled && gbCheatList[i].address == address) rlm@1: { rlm@19: switch (gbCheatList[i].code) rlm@19: { rlm@19: case 0x100: // GameGenie support rlm@19: if (gbReadMemoryQuick(address) == gbCheatList[i].compare) rlm@19: return gbCheatList[i].value; rlm@19: break; rlm@19: case 0x00: rlm@19: case 0x01: rlm@19: case 0x80: rlm@19: return gbCheatList[i].value; rlm@19: case 0x90: rlm@19: case 0x91: rlm@19: case 0x92: rlm@19: case 0x93: rlm@19: case 0x94: rlm@19: case 0x95: rlm@19: case 0x96: rlm@19: case 0x97: rlm@19: if (address >= 0xd000 && address < 0xe000) rlm@1: { rlm@19: if (((gbMemoryMap[0x0d] - gbWram)/0x1000) == rlm@19: (gbCheatList[i].code - 0x90)) rlm@19: return gbCheatList[i].value; rlm@1: } rlm@19: else rlm@19: return gbCheatList[i].value; rlm@19: } rlm@1: } rlm@19: } rlm@19: return gbReadMemoryQuick(address); rlm@1: } rlm@1: