diff --git a/cheats.cpp b/cheats.cpp index 72ee6a5e..ec60180d 100644 --- a/cheats.cpp +++ b/cheats.cpp @@ -223,21 +223,38 @@ static bool8 S9xAllHex (const char *code, int len) const char * S9xProActionReplayToRaw (const char *code, uint32 &address, uint8 &byte) { - uint32 data = 0; + bool valid = false; + int len = strlen(code); + if (len == 9 && S9xAllHex(code, 6) && *(code + 6) == ':' && S9xAllHex(code + 7, 2)) { + uint32 address_, byte_; + *(char*)(code + 6) = '\0'; + if (sscanf(code, "%x", &address_) == 1 && sscanf(code + 7, "%x", &byte_) == 1) { + address = address_; + byte = (uint8) byte_; + valid = true; + } + *(char*)(code + 6) = ':'; + } + else if (len == 8 && S9xAllHex(code, 8)) { + uint32 data; + if (sscanf(code, "%x", &data) == 1) { + address = data >> 8; + byte = (uint8) data; + valid = true; + } + } - if (strlen(code) != 8 || !S9xAllHex(code, 8) || sscanf(code, "%x", &data) != 1) + if (valid) + return (NULL); + else return ("Invalid Pro Action Replay code - should be 8 hex digits in length."); - - address = data >> 8; - byte = (uint8) data; - - return (NULL); } -const char * S9xGoldFingerToRaw (const char *code, uint32 &address, bool8 &sram, uint8 &num_bytes, uint8 bytes[3]) +const char * S9xGoldFingerToRaw (const char *code, uint32 &address, uint8 &num_bytes, uint8 bytes[3]) { char tmp[15]; int i; + bool8 sram; if (strlen(code) != 14) return ("Invalid Gold Finger code - should be 14 hex digits in length."); @@ -247,6 +264,10 @@ const char * S9xGoldFingerToRaw (const char *code, uint32 &address, bool8 &sram, if (sscanf(tmp, "%x", &address) != 1) return ("Invalid Gold Finger code."); + sram = code[13] == '1'; + if (sram) + return ("Unsupported Gold Finger code - writing to SRAM is not supported."); + for (i = 0; i < 3; i++) { unsigned int byte; @@ -259,7 +280,6 @@ const char * S9xGoldFingerToRaw (const char *code, uint32 &address, bool8 &sram, } num_bytes = i; - sram = code[13] == '1'; return (NULL); } diff --git a/cheats.h b/cheats.h index fa9a118c..3c0dabe8 100644 --- a/cheats.h +++ b/cheats.h @@ -179,22 +179,28 @@ #ifndef _CHEATS_H_ #define _CHEATS_H_ -#define MAX_CHEATS 150 +#include +#include struct SCheat { uint32 address; uint8 byte; uint8 saved_byte; - bool8 enabled; bool8 saved; - char name[22]; +}; + +struct SCheatItem +{ + std::vector c; + std::string code; + std::string name; + bool8 enabled; }; struct SCheatData { - struct SCheat c[MAX_CHEATS]; - uint32 num_cheats; + std::vector c; uint8 CWRAM[0x20000]; uint8 CSRAM[0x10000]; uint8 CIRAM[0x2000]; @@ -236,18 +242,22 @@ typedef enum extern SCheatData Cheat; extern Watch watches[16]; -void S9xApplyCheat (uint32); -void S9xApplyCheats (void); +void S9xApplyCheat (uint32, bool8 = TRUE); +void S9xApplyCheats (bool8 = TRUE); void S9xRemoveCheat (uint32); void S9xRemoveCheats (void); void S9xDeleteCheat (uint32); void S9xDeleteCheats (void); void S9xEnableCheat (uint32); void S9xDisableCheat (uint32); -void S9xAddCheat (bool8, bool8, uint32, uint8); +bool S9xIsValidCheatCode(const char *); +void S9xAddCheat (bool8, bool8, const char *, const char *); +void S9xAddCheat (bool8, bool8, uint32, uint8, const char * = ""); void S9xInitCheatData (void); void S9xInitWatchedAddress (void); +bool8 IsOldCheatFile (const char *); bool8 S9xLoadCheatFile (const char *); +bool8 S9xLoadCheatFileOld (const char *); bool8 S9xSaveCheatFile (const char *); void S9xStartCheatSearch (SCheatData *); @@ -258,6 +268,6 @@ void S9xOutputCheatSearchResults (SCheatData *); const char * S9xGameGenieToRaw (const char *, uint32 &, uint8 &); const char * S9xProActionReplayToRaw (const char *, uint32 &, uint8 &); -const char * S9xGoldFingerToRaw (const char *, uint32 &, bool8 &, uint8 &, uint8 bytes[3]); +const char * S9xGoldFingerToRaw (const char *, uint32 &, uint8 &, uint8 bytes[3]); #endif diff --git a/cheats2.cpp b/cheats2.cpp index a4154b46..22f5080a 100644 --- a/cheats2.cpp +++ b/cheats2.cpp @@ -180,6 +180,10 @@ #include "memmap.h" #include "cheats.h" +#include +#include +#include + uint8 S9xGetByteFree (uint32); void S9xSetByteFree (uint8, uint32); @@ -223,69 +227,151 @@ void S9xInitCheatData (void) Cheat.FillRAM = Memory.FillRAM; } -void S9xAddCheat (bool8 enable, bool8 save_current_value, uint32 address, uint8 byte) +bool S9xIsValidCheatCode(const char *codestring) { - if (Cheat.num_cheats < sizeof(Cheat.c) / sizeof(Cheat.c[0])) + std::string codes(codestring); + + if (codes.empty() || codes[codes.length() - 1] == '+') + return false; // std::getline cannot handle this case + + std::istringstream iss(codes); + std::string code; + while(std::getline(iss, code, '+')) { - Cheat.c[Cheat.num_cheats].address = address; - Cheat.c[Cheat.num_cheats].byte = byte; - Cheat.c[Cheat.num_cheats].enabled = enable; - - if (save_current_value) + uint32 address; + uint8 byte; + if (S9xProActionReplayToRaw(code.c_str(), address, byte) != NULL && + S9xGameGenieToRaw(code.c_str(), address, byte) != NULL) { - Cheat.c[Cheat.num_cheats].saved_byte = S9xGetByteFree(address); - Cheat.c[Cheat.num_cheats].saved = TRUE; - } + uint8 bytes[3]; + uint8 num_bytes; - Cheat.num_cheats++; + if (S9xGoldFingerToRaw(code.c_str(), address, num_bytes, bytes) != NULL) + return false; // invalid format + } + } + return true; +} + +static inline bool S9xCompileCheat(struct SCheatItem& cheat, bool8 save_current_value) +{ + std::string codes(cheat.code); + std::vector cheatVector; + + if (codes.empty() || codes[codes.length() - 1] == '+') + return false; // std::getline cannot handle this case + + std::istringstream iss(codes); + std::string code; + while(std::getline(iss, code, '+')) + { + SCheat c; + if (S9xProActionReplayToRaw(code.c_str(), c.address, c.byte) == NULL || + S9xGameGenieToRaw(code.c_str(), c.address, c.byte) == NULL) + { + c.saved = save_current_value; + if (c.saved) { + c.saved_byte = S9xGetByteFree(c.address); + } + cheatVector.push_back(c); + } + else + { + uint32 address; + uint8 bytes[3]; + uint8 num_bytes; + + if (S9xGoldFingerToRaw(code.c_str(), address, num_bytes, bytes) == NULL) + { + for (int i = 0; i < num_bytes; i++) + { + c.address = address + i; + c.byte = bytes[i]; + c.saved = save_current_value; + if (c.saved) { + c.saved_byte = S9xGetByteFree(c.address); + } + cheatVector.push_back(c); + } + } + else + return false; // invalid format + } + } + cheat.c.clear(); + copy(cheatVector.begin(), cheatVector.end(), std::back_inserter(cheat.c)); + return true; +} + +void S9xAddCheat (bool8 enable, bool8 save_current_value, const char *code, const char *name) +{ + struct SCheatItem cheat; + cheat.code = code; + cheat.name = name; + cheat.enabled = enable; + if (S9xCompileCheat(cheat, save_current_value)) + Cheat.c.push_back(cheat); +} + +void S9xAddCheat (bool8 enable, bool8 save_current_value, uint32 address, uint8 byte, const char *name) +{ + if (address >= 0x000000 && address <= 0xFFFFFF) { + char code[10]; + sprintf(code, "%06X:%02X", address, byte); + S9xAddCheat(enable, save_current_value, code, name); } } void S9xDeleteCheat (uint32 which1) { - if (which1 < Cheat.num_cheats) + std::vector::iterator iter = Cheat.c.begin(); + std::advance(iter, which1); + if (which1 < Cheat.c.size()) { if (Cheat.c[which1].enabled) S9xRemoveCheat(which1); - - memmove(&Cheat.c[which1], &Cheat.c[which1 + 1], sizeof(Cheat.c[0]) * (Cheat.num_cheats - which1 - 1)); - - Cheat.num_cheats--; + Cheat.c.erase(iter); } } void S9xDeleteCheats (void) { S9xRemoveCheats(); - Cheat.num_cheats = 0; + Cheat.c.clear(); } void S9xRemoveCheat (uint32 which1) { - if (Cheat.c[which1].saved) + std::vector::iterator iter = Cheat.c[which1].c.begin(); + while (iter != Cheat.c[which1].c.end()) { - uint32 address = Cheat.c[which1].address; + SCheat& c = *iter; + if (c.saved) + { + uint32 address = c.address; - int block = (address & 0xffffff) >> MEMMAP_SHIFT; - uint8 *ptr = Memory.Map[block]; + int block = (address & 0xffffff) >> MEMMAP_SHIFT; + uint8 *ptr = Memory.Map[block]; - if (ptr >= (uint8 *) CMemory::MAP_LAST) - *(ptr + (address & 0xffff)) = Cheat.c[which1].saved_byte; - else - S9xSetByteFree(Cheat.c[which1].saved_byte, address); + if (ptr >= (uint8 *) CMemory::MAP_LAST) + *(ptr + (address & 0xffff)) = c.saved_byte; + else + S9xSetByteFree(c.saved_byte, address); + } + iter++; } } void S9xRemoveCheats (void) { - for (uint32 i = 0; i < Cheat.num_cheats; i++) + for (uint32 i = 0; i < Cheat.c.size(); i++) if (Cheat.c[i].enabled) S9xRemoveCheat(i); } void S9xEnableCheat (uint32 which1) { - if (which1 < Cheat.num_cheats && !Cheat.c[which1].enabled) + if (which1 < Cheat.c.size() && !Cheat.c[which1].enabled) { Cheat.c[which1].enabled = TRUE; S9xApplyCheat(which1); @@ -294,62 +380,222 @@ void S9xEnableCheat (uint32 which1) void S9xDisableCheat (uint32 which1) { - if (which1 < Cheat.num_cheats && Cheat.c[which1].enabled) + if (which1 < Cheat.c.size() && Cheat.c[which1].enabled) { S9xRemoveCheat(which1); Cheat.c[which1].enabled = FALSE; } } -void S9xApplyCheat (uint32 which1) +void S9xApplyCheat (uint32 which1, bool8 force_save_current_value) { - uint32 address = Cheat.c[which1].address; - - if (!Cheat.c[which1].saved) + std::vector::iterator iter = Cheat.c[which1].c.begin(); + while (iter != Cheat.c[which1].c.end()) { - Cheat.c[which1].saved_byte = S9xGetByteFree(address); - Cheat.c[which1].saved = TRUE; + SCheat& c = *iter; + uint32 address = c.address; + + if (force_save_current_value) + { + c.saved_byte = S9xGetByteFree(address); + c.saved = TRUE; + } + + int block = (address & 0xffffff) >> MEMMAP_SHIFT; + uint8 *ptr = Memory.Map[block]; + + if (ptr >= (uint8 *) CMemory::MAP_LAST) + *(ptr + (address & 0xffff)) = c.byte; + else + S9xSetByteFree(c.byte, address); + + iter++; } - - int block = (address & 0xffffff) >> MEMMAP_SHIFT; - uint8 *ptr = Memory.Map[block]; - - if (ptr >= (uint8 *) CMemory::MAP_LAST) - *(ptr + (address & 0xffff)) = Cheat.c[which1].byte; - else - S9xSetByteFree(Cheat.c[which1].byte, address); } -void S9xApplyCheats (void) +void S9xApplyCheats (bool8 force_save_current_value) { if (Settings.ApplyCheats) { - for (uint32 i = 0; i < Cheat.num_cheats; i++) + for (uint32 i = 0; i < Cheat.c.size(); i++) if (Cheat.c[i].enabled) - S9xApplyCheat(i); + S9xApplyCheat(i, force_save_current_value); } } -bool8 S9xLoadCheatFile (const char *filename) +std::string& string_replace (std::string& str, const std::string& from, const std::string& to) +{ + std::string::size_type pos = 0; + while (pos = str.find(from, pos), pos != std::string::npos) { + str.replace(pos, from.length(), to); + pos += to.length(); + } + return str; +} + +std::string csv_escape(const std::string &token, bool quoteAlways = false) +{ + std::string res(token); + bool needsQuote = false; + if (res.find("\"", 0) != std::string::npos) { + needsQuote = true; + string_replace(res, "\"", "\"\""); + } + if (res.find(",", 0) != std::string::npos || + res.find("\n", 0) != std::string::npos) { + needsQuote = true; + } + if (needsQuote || quoteAlways) { + res.insert(0, "\""); + res.append("\""); + } + return res; +} + + +std::string csv_unescape(const std::string &token) +{ + std::string res(token); + if (res.length() >= 2 && res[0] == '\"' && res[res.length() - 1] == '\"') { + res.erase(res.length() - 1, 1); + res.erase(0, 1); + } + string_replace(res, "\"\"", "\""); + return res; +} + +std::vector csv_split(const std::string &str, bool *valid = NULL) +{ + std::vector res; + + if (valid != NULL) { + *valid = true; + } + + std::string::size_type start = 0; + std::string::size_type current = 0; + std::string::size_type found; + bool insideQuote = false; + while ((found = str.find_first_of(",\"", current)) != std::string::npos) { + char c = str[found]; + if (c == '\"') { + if (insideQuote) { + if (str[found + 1] == '\"') { + found++; // skip the next quote + } + else { + if (valid != NULL && str[found + 1] != ',' && str[found + 1] != '\0' && str[found + 1] != '\r' && str[found + 1] != '\n') { + *valid = false; + } + insideQuote = false; + } + } + else { + if (valid != NULL && found > 0 && str[found - 1] != ',' && str[found - 1] != '\r' && str[found - 1] != '\n') { + *valid = false; + } + insideQuote = true; + } + } + else if (c == ',') { + if (!insideQuote) { + res.push_back(std::string(str, start, found - start)); + start = found + 1; + } + } + current = found + 1; + } + res.push_back(std::string(str, start, str.size() - start)); + + if (valid != NULL && insideQuote) { + *valid = false; + } + + for (int i = 0; i < res.size(); i++) { + res[i] = csv_unescape(res[i]); + } + return res; +} + +std::string csv_join(std::vector &v, bool quoteAlways = false) +{ + std::string res; + + std::vector::iterator iter = v.begin(); + bool first = true; + while(iter != v.end()) + { + if (first) { + first = false; + } + else { + res.append(","); + } + + res.append(csv_escape(*iter, quoteAlways)); + iter++; + } + return res; +} + +bool8 IsOldCheatFile (const char *filename) { FILE *fs; - uint8 data[28]; - - Cheat.num_cheats = 0; fs = fopen(filename, "rb"); if (!fs) return (FALSE); - while (fread((void *) data, 1, 28, fs) == 28) + int c = fgetc(fs); + fclose(fs); + + return ((c & ~(4 | 8)) == 0); +} + +bool8 S9xLoadCheatFile (const char *filename) +{ + FILE *fs; + char data[1024]; + + if (IsOldCheatFile(filename)) + return S9xLoadCheatFileOld(filename); + + Cheat.c.clear(); + + fs = fopen(filename, "r"); + if (!fs) + return (FALSE); + + while (fgets(data, 1024, fs) != NULL) { - Cheat.c[Cheat.num_cheats].enabled = (data[0] & 4) == 0; - Cheat.c[Cheat.num_cheats].byte = data[1]; - Cheat.c[Cheat.num_cheats].address = data[2] | (data[3] << 8) | (data[4] << 16); - Cheat.c[Cheat.num_cheats].saved_byte = data[5]; - Cheat.c[Cheat.num_cheats].saved = (data[0] & 8) != 0; - memmove(Cheat.c[Cheat.num_cheats].name, &data[8], 20); - Cheat.c[Cheat.num_cheats++].name[20] = 0; + data[strlen(data) - 1] = '\0'; // erase newline + + std::vector v = csv_split(std::string(data)); + if (v.size() != 3) { + fclose(fs); + return (FALSE); + } + + struct SCheatItem c; + if (v[0] == "enabled") { + c.enabled = TRUE; + } + else if (v[0] == "disabled") { + c.enabled = FALSE; + } + else { + fclose(fs); + return (FALSE); + } + c.code = v[1]; + c.name = v[2]; + + if (!S9xCompileCheat(c, FALSE)) + { + fclose(fs); + return (FALSE); + } + Cheat.c.push_back(c); } fclose(fs); @@ -357,46 +603,83 @@ bool8 S9xLoadCheatFile (const char *filename) return (TRUE); } +bool8 S9xLoadCheatFileOld (const char *filename) +{ + FILE *fs; + uint8 data[28]; + + Cheat.c.clear(); + + fs = fopen(filename, "rb"); + if (!fs) + return (FALSE); + + while (fread((void *) data, 1, 28, fs) == 28) + { + struct SCheatItem c; + uint32 address; + uint8 byte; + uint8 saved_byte; + bool8 saved; + c.enabled = (data[0] & 4) == 0; + byte = data[1]; + address = data[2] | (data[3] << 8) | (data[4] << 16); + saved_byte = data[5]; + saved = (data[0] & 8) != 0; + + char code[10]; + char name[22]; + sprintf(code, "%06X%02X", address, byte); + memmove(name, &data[8], 20); + name[20] = '\0'; + c.code = code; + c.name = name; + + if (!S9xCompileCheat(c, FALSE)) + { + fclose(fs); + return (FALSE); + } + + std::vector::iterator iter = c.c.begin(); + while (iter != c.c.end()) + { + iter->saved_byte = saved_byte; + iter->saved = saved; + iter++; + } + + Cheat.c.push_back(c); + } + + fclose(fs); + + S9xSaveCheatFile(filename); + return (TRUE); +} + bool8 S9xSaveCheatFile (const char *filename) { - if (Cheat.num_cheats == 0) + if (Cheat.c.size() == 0) { remove(filename); return (TRUE); } FILE *fs; - uint8 data[28]; - fs = fopen(filename, "wb"); + fs = fopen(filename, "w"); if (!fs) return (FALSE); - for (uint32 i = 0; i < Cheat.num_cheats; i++) + for (uint32 i = 0; i < Cheat.c.size(); i++) { - memset(data, 0, 28); - - if (i == 0) - { - data[6] = 254; - data[7] = 252; - } - - if (!Cheat.c[i].enabled) - data[0] |= 4; - - if (Cheat.c[i].saved) - data[0] |= 8; - - data[1] = Cheat.c[i].byte; - data[2] = (uint8) (Cheat.c[i].address >> 0); - data[3] = (uint8) (Cheat.c[i].address >> 8); - data[4] = (uint8) (Cheat.c[i].address >> 16); - data[5] = Cheat.c[i].saved_byte; - - memmove(&data[8], Cheat.c[i].name, 19); - - if (fwrite(data, 28, 1, fs) != 1) + struct SCheatItem& c = Cheat.c[i]; + std::vector v; + v.push_back(c.enabled ? "enabled" : "disabled"); + v.push_back(c.code); + v.push_back(c.name); + if (fprintf(fs, "%s\n", csv_join(v, true).c_str()) < 0) { fclose(fs); return (FALSE); diff --git a/gfx.cpp b/gfx.cpp index fccff339..68341067 100644 --- a/gfx.cpp +++ b/gfx.cpp @@ -448,7 +448,7 @@ void S9xEndScreenRefresh (void) else S9xControlEOF(); - S9xApplyCheats(); + S9xApplyCheats(FALSE); #ifdef DEBUGGER if (CPU.Flags & FRAME_ADVANCE_FLAG) diff --git a/memmap.cpp b/memmap.cpp index 053f8ae2..9ada288e 100644 --- a/memmap.cpp +++ b/memmap.cpp @@ -1759,7 +1759,7 @@ bool8 CMemory::LoadROMInt (int32 ROMfillSize) InitROM(); S9xInitCheatData(); - S9xApplyCheats(); + S9xApplyCheats(FALSE); S9xReset(); @@ -1930,7 +1930,7 @@ bool8 CMemory::LoadMultiCartInt () InitROM(); S9xInitCheatData(); - S9xApplyCheats(); + S9xApplyCheats(FALSE); S9xReset(); diff --git a/snes9x.cpp b/snes9x.cpp index d36ab95e..08a463ea 100644 --- a/snes9x.cpp +++ b/snes9x.cpp @@ -788,11 +788,10 @@ char * S9xParseArgs (char **argv, int argc) { uint32 address; uint8 bytes[3]; - bool8 sram; uint8 num_bytes; const char *error; - if ((error = S9xGoldFingerToRaw(argv[++i], address, sram, num_bytes, bytes)) == NULL) + if ((error = S9xGoldFingerToRaw(argv[++i], address, num_bytes, bytes)) == NULL) { for (int c = 0; c < num_bytes; c++) S9xAddCheat(TRUE, FALSE, address + c, bytes[c]); diff --git a/win32/ramwatch.cpp b/win32/ramwatch.cpp index ed5ec923..e2ef281d 100644 --- a/win32/ramwatch.cpp +++ b/win32/ramwatch.cpp @@ -1203,16 +1203,7 @@ LRESULT CALLBACK RamWatchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam cht.new_val = rswatches[watchIndex].CurValue; cht.saved_val = rswatches[watchIndex].CurValue; extern INT_PTR CALLBACK DlgCheatSearchAdd(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); - if(DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_CHEAT_FROM_SEARCH), hDlg, DlgCheatSearchAdd, (LPARAM)&cht)) - { - int p; - for(p=0; p>(8*p))&0xFF)); - //add cheat - strcpy(Cheat.c[Cheat.num_cheats-1].name, cht.name); - } - } + DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_CHEAT_FROM_SEARCH), hDlg, DlgCheatSearchAdd, (LPARAM)&cht); } } break; diff --git a/win32/rsrc/resource.h b/win32/rsrc/resource.h index e53a16a5..92271564 100644 --- a/win32/rsrc/resource.h +++ b/win32/rsrc/resource.h @@ -160,11 +160,11 @@ #define IDC_JPTOGGLE 1126 #define IDC_LOCALVIDMEM 1126 #define IDC_VSYNC 1126 -#define IDC_CHEAT_DESCRIPTION 1127 +#define IDC_CHEAT_NAME 1127 #define IDC_KEYBOARD 1127 #define IDC_ALLOWLEFTRIGHT 1127 -#define IDC_CHEAT_ADDRESS 1128 -#define IDC_CHEAT_BYTE 1129 +#define IDC_CHEAT_DEC 1128 +#define IDC_CHEAT_HEX 1129 #define IDC_ADD_CHEAT 1130 #define IDC_CHEAT_LIST 1131 #define IDC_PICTURE 1132 @@ -253,13 +253,9 @@ #define IDC_LABEL_UP10 1189 #define IDC_LABEL_MAXSKIP 1190 #define IDC_LABEL_UP11 1190 -#define IDC_LABEL_CHEAT_CODE 1191 #define IDC_LABEL_UP12 1191 -#define IDC_LABEL_CHEAT_DESCRIPTION 1192 #define IDC_LABEL_UP13 1192 -#define IDC_LABEL_CHEAT_ADDRESS 1193 #define IDC_LABEL_UP14 1193 -#define IDC_LABEL_CHEAT_BYTE 1194 #define IDC_LABEL_UP15 1194 #define IDC_LABEL_SERVERADDY 1195 #define IDC_LABEL_UP16 1195 @@ -368,6 +364,7 @@ #define IDC_SHADER_HLSL_BROWSE 3016 #define IDC_SHADER_GROUP 3017 #define IDC_SHADER_GLSL_BROWSE 3018 +#define IDC_CHEAT_SYNTAX 3018 #define IDC_RAMLIST 5000 //#define IDC_C_SEARCH 5001 #define IDC_C_ADDCHEAT 5002 @@ -578,7 +575,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 151 #define _APS_NEXT_COMMAND_VALUE 40175 -#define _APS_NEXT_CONTROL_VALUE 3018 +#define _APS_NEXT_CONTROL_VALUE 3019 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/win32/rsrc/snes9x.rc b/win32/rsrc/snes9x.rc index 6162ce41..f38d2122 100644 --- a/win32/rsrc/snes9x.rc +++ b/win32/rsrc/snes9x.rc @@ -50,7 +50,7 @@ BEGIN VK_BACK, ID_WINDOW_STRETCH, VIRTKEY, ALT, NOINVERT END -IDR_RWACCELERATOR ACCELERATORS +IDR_RWACCELERATOR ACCELERATORS BEGIN "N", RAMMENU_FILE_NEW, VIRTKEY, CONTROL "O", RAMMENU_FILE_OPEN, VIRTKEY, CONTROL @@ -248,26 +248,28 @@ BEGIN CONTROL "VSync",IDC_VSYNC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,102,37,10 END -IDD_CHEATER DIALOGEX 0, 0, 262, 218 -STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_3DLOOK | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU +IDD_CHEATER DIALOGEX 0, 0, 320, 240 +STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU CAPTION "Cheat Entry and Editor" -FONT 8, "MS Sans Serif", 0, 0, 0x1 +FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - CONTROL "List1",IDC_CHEAT_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,5,5,200,125,WS_EX_CLIENTEDGE - PUSHBUTTON "&Add",IDC_ADD_CHEAT,215,5,40,15,WS_DISABLED - PUSHBUTTON "&Delete",IDC_DELETE_CHEAT,215,25,40,15,WS_DISABLED - PUSHBUTTON "&Update",IDC_UPDATE_CHEAT,215,45,40,15,WS_DISABLED - PUSHBUTTON "C&lear",IDC_CLEAR_CHEATS,215,65,40,15 - EDITTEXT IDC_CHEAT_CODE,86,134,118,15,ES_UPPERCASE | ES_AUTOHSCROLL - EDITTEXT IDC_CHEAT_DESCRIPTION,85,154,119,15,ES_AUTOHSCROLL - EDITTEXT IDC_CHEAT_ADDRESS,85,175,44,15,ES_UPPERCASE | ES_AUTOHSCROLL - EDITTEXT IDC_CHEAT_BYTE,215,175,26,15,ES_UPPERCASE | ES_AUTOHSCROLL - PUSHBUTTON "&OK",IDOK,93,196,50,15 - PUSHBUTTON "&Cancel",IDCANCEL,151,196,50,15 - RTEXT "Enter Cheat Code:",IDC_LABEL_CHEAT_CODE,23,134,59,15,SS_CENTERIMAGE - RTEXT "Cheat Description",IDC_LABEL_CHEAT_DESCRIPTION,19,154,61,15,SS_CENTERIMAGE - RTEXT "Cheat Address (hex)",IDC_LABEL_CHEAT_ADDRESS,18,175,64,15,SS_CENTERIMAGE - RTEXT "New Value (dec or hex)",IDC_LABEL_CHEAT_BYTE,134,175,74,15,SS_CENTERIMAGE + CONTROL "List1",IDC_CHEAT_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,5,5,260,125,WS_EX_CLIENTEDGE + PUSHBUTTON "&Add",IDC_ADD_CHEAT,273,5,40,15,WS_DISABLED + PUSHBUTTON "&Delete",IDC_DELETE_CHEAT,273,25,40,15,WS_DISABLED + PUSHBUTTON "&Update",IDC_UPDATE_CHEAT,273,45,40,15,WS_DISABLED + EDITTEXT IDC_CHEAT_CODE,85,134,179,15,ES_UPPERCASE | ES_AUTOHSCROLL + EDITTEXT IDC_CHEAT_NAME,85,154,179,15,ES_AUTOHSCROLL + PUSHBUTTON "C&lear",IDC_CLEAR_CHEATS,268,154,40,15 + PUSHBUTTON "Syntax",IDC_CHEAT_SYNTAX,268,133,40,15 + EDITTEXT IDC_CHEAT_DEC,85,189,53,15,ES_UPPERCASE | ES_AUTOHSCROLL + EDITTEXT IDC_CHEAT_HEX,195,189,45,15,ES_UPPERCASE | ES_AUTOHSCROLL + PUSHBUTTON "&OK",IDOK,106,218,50,15 + PUSHBUTTON "&Cancel",IDCANCEL,164,218,50,15 + RTEXT "Cheat Code:",IDC_STATIC,22,134,59,15,SS_CENTERIMAGE + RTEXT "Cheat Description:",IDC_STATIC,20,154,61,15,SS_CENTERIMAGE + RTEXT "decimal",IDC_STATIC,52,189,30,15,SS_CENTERIMAGE + RTEXT "hexadecimal",IDC_STATIC,143,189,45,15,SS_CENTERIMAGE + GROUPBOX "Radix Converter",IDC_STATIC,29,177,235,36,0,WS_EX_TRANSPARENT END IDD_NETPLAYPROGRESS DIALOG 0, 0, 186, 61 @@ -375,8 +377,8 @@ BEGIN PUSHBUTTON "&OK",IDOK,85,123,45,13 PUSHBUTTON "&Cancel",IDCANCEL,135,123,45,13 EDITTEXT IDC_NC_ADDRESS,79,7,101,12,ES_AUTOHSCROLL - EDITTEXT IDC_NC_CURRVAL,79,31,101,12,ES_AUTOHSCROLL - EDITTEXT IDC_NC_PREVVAL,79,55,101,12,ES_AUTOHSCROLL + EDITTEXT IDC_NC_CURRVAL,79,31,101,12,ES_AUTOHSCROLL | ES_READONLY + EDITTEXT IDC_NC_PREVVAL,79,55,101,12,ES_AUTOHSCROLL | ES_READONLY EDITTEXT IDC_NC_NEWVAL,79,79,101,12,ES_AUTOHSCROLL EDITTEXT IDC_NC_DESC,79,103,101,12,ES_AUTOHSCROLL RTEXT "Address",IDC_STATIC,7,7,70,12,SS_CENTERIMAGE @@ -687,7 +689,7 @@ BEGIN PUSHBUTTON "&Cancel",IDCANCEL,120,80,50,14 END -IDD_PROMPT DIALOG 0, 0, 186, 68 +IDD_PROMPT DIALOG 0, 0, 186, 68 STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Input Prompt" FONT 8, "Ms Shell Dlg 2" @@ -790,9 +792,9 @@ BEGIN IDD_CHEATER, DIALOG BEGIN LEFTMARGIN, 7 - RIGHTMARGIN, 255 + RIGHTMARGIN, 313 TOPMARGIN, 7 - BOTTOMMARGIN, 211 + BOTTOMMARGIN, 233 END IDD_NETPLAYPROGRESS, DIALOG @@ -1139,7 +1141,7 @@ BEGIN END END -RAMWATCH_MENU MENU +RAMWATCH_MENU MENU BEGIN POPUP "File" BEGIN diff --git a/win32/wlanguage.h b/win32/wlanguage.h index 27fe4def..27eb7b0c 100644 --- a/win32/wlanguage.h +++ b/win32/wlanguage.h @@ -534,6 +534,8 @@ Nintendo is a trade mark.") #define SEARCH_COLUMN_ADDRESS TEXT("Address") #define SEARCH_COLUMN_VALUE TEXT("Value") #define SEARCH_COLUMN_DESCRIPTION TEXT("Description") +#define CHEAT_COLUMN_CODE TEXT("Code") +#define CHEAT_COLUMN_DESCRIPTION TEXT("Description") // ROM dialog diff --git a/win32/wsnes9x.cpp b/win32/wsnes9x.cpp index dff2e886..728a4aa9 100644 --- a/win32/wsnes9x.cpp +++ b/win32/wsnes9x.cpp @@ -2419,10 +2419,7 @@ LRESULT CALLBACK WinProc( break; case ID_CHEAT_ENTER: RestoreGUIDisplay (); - S9xRemoveCheats (); DialogBox(g_hInst, MAKEINTRESOURCE(IDD_CHEATER), hWnd, DlgCheater); - S9xSaveCheatFile (S9xGetFilename (".cht", CHEAT_DIR)); - S9xApplyCheats (); RestoreSNESDisplay (); break; case ID_CHEAT_SEARCH: @@ -2476,7 +2473,7 @@ LRESULT CALLBACK WinProc( S9xApplyCheats (); bool on = false; extern struct SCheatData Cheat; - for (uint32 i = 0; i < Cheat.num_cheats && !on; i++) + for (uint32 i = 0; i < Cheat.c.size() && !on; i++) if (Cheat.c [i].enabled) on = true; S9xMessage (S9X_INFO, S9X_GAME_GENIE_CODE_ERROR, on ? CHEATS_INFO_ENABLED : CHEATS_INFO_ENABLED_NONE); @@ -8360,811 +8357,414 @@ int ScanAddress(const TCHAR* str, IntType& value) return ret; } -enum CheatStatus{ - Untouched, - Deleted, - Modified -}; -typedef struct{ -int* index; -DWORD* state; -}CheatTracker; +#define CHEAT_LVITEM_CODE 0 +#define CHEAT_LVITEM_NAME 1 -#define ITEM_QUERY(a, b, c, d, e) memset(&a, 0, sizeof(LV_ITEM)); \ - a.iItem= ListView_GetSelectionMark(GetDlgItem(hDlg, b)); \ - a.iSubItem=c; \ - a.mask=LVIF_TEXT; \ - a.pszText=d; \ - a.cchTextMax=e; \ - ListView_GetItem(GetDlgItem(hDlg, b), &a); +std::tstring tstring_GetWindowText(HWND hWnd) +{ + std::tstring text; + TCHAR *textBuf = NULL; + + int textLength = GetWindowTextLength(hWnd); + if (textLength == 0) + return text; + + textBuf = new TCHAR[textLength + 1]; + textBuf[0] = TEXT('\0'); + GetWindowText(hWnd, textBuf, textLength + 1); + text = textBuf; + delete [] textBuf; + + return text; +} + +std::tstring tstring_ListView_GetItemText(HWND hwnd, int iItem, int iSubItem) +{ + std::tstring text; + TCHAR *textBuf = NULL; + DWORD bufferSize = 256; + + LVITEM lvi; + memset(&lvi, 0, sizeof(LVITEM)); + lvi.iSubItem = iSubItem; + + while(true) + { + textBuf = new TCHAR[bufferSize]; + lvi.pszText = textBuf; + lvi.cchTextMax = bufferSize; + int recvLength = SendMessage(hwnd, LVM_GETITEMTEXT, (WPARAM)iItem, (LPARAM)&lvi); + if (recvLength < bufferSize - 1) + break; + + delete [] textBuf; + bufferSize *= 2; + }; + + text = textBuf; + delete [] textBuf; + return text; +} + +BOOL WinAddCheatToDialog(HWND hDlg, LPCTSTR code, LPCTSTR name); +BOOL WinUpdateCheatInDialog(HWND hDlg, int iItem, LPCTSTR code, LPCTSTR name); +void WinSetCheatsToDialog(HWND hDlg, std::vector cheats); +std::vector WinGetCheatsFromDialog(HWND hDlg); + +BOOL WinAddCheatToDialog(HWND hDlg, LPCTSTR code, LPCTSTR name, BOOL enabled) +{ + int iItem; + + LVFINDINFO lvfi; + memset(&lvfi, 0, sizeof(LVFINDINFO)); + lvfi.flags = LVFI_STRING; + lvfi.psz = code; + iItem = ListView_FindItem(GetDlgItem(hDlg,IDC_CHEAT_LIST), -1, &lvfi); + if (iItem >= 0) + { + return WinUpdateCheatInDialog(hDlg, iItem, code, name); + } + + LVITEM lvi; + + memset(&lvi, 0, sizeof(LVITEM)); + lvi.mask = LVIF_TEXT; + lvi.pszText = (LPTSTR)code; + lvi.iItem = ListView_GetItemCount(GetDlgItem(hDlg, IDC_CHEAT_LIST)); + iItem = ListView_InsertItem(GetDlgItem(hDlg,IDC_CHEAT_LIST), &lvi); + if (iItem == -1) + { + return FALSE; + } + + ListView_SetItemText(GetDlgItem(hDlg, IDC_CHEAT_LIST), iItem, CHEAT_LVITEM_NAME, (LPTSTR)name); + ListView_SetCheckState(GetDlgItem(hDlg, IDC_CHEAT_LIST), iItem, enabled); + return TRUE; +} + +BOOL WinUpdateCheatInDialog(HWND hDlg, int iItem, LPCTSTR code, LPCTSTR name) +{ + if (iItem < 0) + return TRUE; + + ListView_SetItemText(GetDlgItem(hDlg, IDC_CHEAT_LIST), iItem, CHEAT_LVITEM_CODE, (LPTSTR)code); + ListView_SetItemText(GetDlgItem(hDlg, IDC_CHEAT_LIST), iItem, CHEAT_LVITEM_NAME, (LPTSTR)name); + return TRUE; +} + +void WinSetCheatsToDialog(HWND hDlg, std::vector cheats) +{ + ListView_DeleteAllItems(GetDlgItem(hDlg, IDC_CHEAT_LIST)); + std::vector::iterator iter = cheats.begin(); + while (iter != cheats.end()) + { + WinAddCheatToDialog(hDlg, _tFromChar(iter->code.c_str()), _tFromChar(iter->name.c_str()), iter->enabled); + iter++; + } +} + +std::vector WinGetCheatsFromDialog(HWND hDlg) +{ + std::vector cheats; + + int iCount = ListView_GetItemCount(GetDlgItem(hDlg, IDC_CHEAT_LIST)); + for (int iItem = 0; iItem < iCount; iItem++) + { + std::tstring code = tstring_ListView_GetItemText(GetDlgItem(hDlg, IDC_CHEAT_LIST), iItem, CHEAT_LVITEM_CODE); + std::tstring name = tstring_ListView_GetItemText(GetDlgItem(hDlg, IDC_CHEAT_LIST), iItem, CHEAT_LVITEM_NAME); + bool enabled = ListView_GetCheckState(GetDlgItem(hDlg, IDC_CHEAT_LIST), iItem); + struct SCheatItem c; + c.code = _tToChar(code.c_str()); + c.name = _tToChar(name.c_str()); + c.enabled = enabled; + cheats.push_back(c); + } + + return cheats; +} + +void WinCheatUpdateButtons(HWND hDlg) +{ + std::tstring code = tstring_GetWindowText(GetDlgItem(hDlg, IDC_CHEAT_CODE)); + bool validCode = S9xIsValidCheatCode(_tToChar(code.c_str())); + EnableWindow(GetDlgItem(hDlg, IDC_ADD_CHEAT), validCode); + + static int prevSelCount = -1; + int selCount = ListView_GetSelectedCount(GetDlgItem(hDlg, IDC_CHEAT_LIST)); + if (selCount != prevSelCount) + { + if(selCount < 2 || prevSelCount < 2) + { + EnableWindow(GetDlgItem(hDlg, IDC_UPDATE_CHEAT), selCount == 1 && validCode); + EnableWindow(GetDlgItem(hDlg, IDC_DELETE_CHEAT), selCount >= 1); + } + prevSelCount = selCount; + } +} + +// limits inputable characters of textbox +// supports only single-line ASCII string, multi-byte characters are not supported. +void FilterTextBox(HWND hWnd, LPCTSTR filterChars) +{ + std::tstring text = tstring_GetWindowText(hWnd); + std::tstring filter(filterChars); + if (text.empty()) + return; + + POINT point; + GetCaretPos(&point); + int caretIndex = SendMessage(hWnd, EM_CHARFROMPOS, 0, MAKELPARAM(point.x, point.y)); + + std::tstring newText; + if (newText.capacity() < text.size()) + newText.reserve(text.size()); + + bool modified = false; + size_t current = 0, found; + int newCaretIndex = caretIndex; + while ((found = text.find_first_not_of(filter, current)) != string::npos) { + if (found - current > 0) { + newText.append(text.substr(current, found - current)); + } + if (current < caretIndex) { + newCaretIndex--; + } + current = found + 1; + modified = true; + } + newText.append(text.substr(current, text.size() - current)); + + if (modified) { + Edit_SetText(hWnd, newText.c_str()); + Edit_SetSel(hWnd, newCaretIndex, newCaretIndex); + MessageBeep(MB_OK); + } +} INT_PTR CALLBACK DlgCheater(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) { - static bool internal_change; - static bool has_sel; - static int sel_idx; - static uint8 new_sel; - static CheatTracker ct; + static BOOL disableRadixConverter = FALSE; switch(msg) { case WM_INITDIALOG: - WinRefreshDisplay(); + { + WinRefreshDisplay(); - ListView_SetExtendedListViewStyle(GetDlgItem(hDlg, IDC_CHEAT_LIST), LVS_EX_FULLROWSELECT|LVS_EX_CHECKBOXES); + S9xRemoveCheats(); - SendDlgItemMessage(hDlg, IDC_CHEAT_CODE, EM_LIMITTEXT, 14, 0); - SendDlgItemMessage(hDlg, IDC_CHEAT_DESCRIPTION, EM_LIMITTEXT, 22, 0); - SendDlgItemMessage(hDlg, IDC_CHEAT_ADDRESS, EM_LIMITTEXT, 6, 0); - SendDlgItemMessage(hDlg, IDC_CHEAT_BYTE, EM_LIMITTEXT, 3, 0); + ListView_SetExtendedListViewStyle(GetDlgItem(hDlg, IDC_CHEAT_LIST), LVS_EX_FULLROWSELECT|LVS_EX_CHECKBOXES); - LVCOLUMN col; - TCHAR temp[32]; - lstrcpy(temp,SEARCH_COLUMN_ADDRESS); - memset(&col, 0, sizeof(LVCOLUMN)); - col.mask=LVCF_FMT|LVCF_ORDER|LVCF_TEXT|LVCF_WIDTH; - col.fmt=LVCFMT_LEFT; - col.iOrder=0; - col.cx=70; - col.cchTextMax=7; - col.pszText=temp; + Edit_LimitText(GetDlgItem(hDlg, IDC_CHEAT_DEC), 11); + Edit_LimitText(GetDlgItem(hDlg, IDC_CHEAT_HEX), 8); - ListView_InsertColumn(GetDlgItem(hDlg,IDC_CHEAT_LIST), 0, &col); + LVCOLUMN col; - lstrcpy(temp,SEARCH_COLUMN_VALUE); - memset(&col, 0, sizeof(LVCOLUMN)); - col.mask=LVCF_FMT|LVCF_ORDER|LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM; - col.fmt=LVCFMT_LEFT; - col.iOrder=1; - col.cx=43; - col.cchTextMax=3; - col.pszText=temp; - col.iSubItem=1; + memset(&col, 0, sizeof(LVCOLUMN)); + col.mask = LVCF_FMT|LVCF_ORDER|LVCF_TEXT|LVCF_WIDTH; + col.fmt = LVCFMT_LEFT; + col.iOrder = CHEAT_LVITEM_CODE; + col.cx = 140; + col.pszText = CHEAT_COLUMN_CODE; + ListView_InsertColumn(GetDlgItem(hDlg, IDC_CHEAT_LIST), CHEAT_LVITEM_CODE, &col); - ListView_InsertColumn(GetDlgItem(hDlg,IDC_CHEAT_LIST), 1, &col); + memset(&col, 0, sizeof(LVCOLUMN)); + col.mask = LVCF_FMT|LVCF_ORDER|LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM; + col.fmt = LVCFMT_LEFT; + col.iOrder = CHEAT_LVITEM_NAME; + col.cx = 220; + col.pszText = CHEAT_COLUMN_DESCRIPTION; + col.iSubItem = CHEAT_LVITEM_NAME; + ListView_InsertColumn(GetDlgItem(hDlg, IDC_CHEAT_LIST), CHEAT_LVITEM_NAME, &col); - lstrcpy(temp,SEARCH_COLUMN_DESCRIPTION); - memset(&col, 0, sizeof(LVCOLUMN)); - col.mask=LVCF_FMT|LVCF_ORDER|LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM; - col.fmt=LVCFMT_LEFT; - col.iOrder=2; - col.cx=165; - col.cchTextMax=32; - col.pszText=temp; - col.iSubItem=2; + WinSetCheatsToDialog(hDlg, Cheat.c); + WinCheatUpdateButtons(hDlg); + return TRUE; + } - ListView_InsertColumn(GetDlgItem(hDlg,IDC_CHEAT_LIST), 2, &col); - - ct.index=new int[Cheat.num_cheats]; - ct.state=new DWORD[Cheat.num_cheats]; - - uint32 counter; - for(counter=0; counter=curr_idx) - ct.index[k]++; - } - ct.index[counter]=curr_idx; - ct.state[counter]=Untouched; - - _stprintf(buffer, TEXT("%02X"), Cheat.c[counter].byte); - memset(&lvi, 0, sizeof(LVITEM)); - lvi.iItem=curr_idx; - lvi.iSubItem=1; - lvi.mask=LVIF_TEXT; - lvi.pszText=buffer; - lvi.cchTextMax=3; - SendDlgItemMessage(hDlg,IDC_CHEAT_LIST, LVM_SETITEM, 0, (LPARAM)&lvi); - - lstrcpy(buffer,_tFromChar(Cheat.c[counter].name)); - memset(&lvi, 0, sizeof(LVITEM)); - lvi.iItem=curr_idx; - lvi.iSubItem=2; - lvi.mask=LVIF_TEXT; - lvi.pszText=buffer; - lvi.cchTextMax=23; - SendDlgItemMessage(hDlg,IDC_CHEAT_LIST, LVM_SETITEM, 0, (LPARAM)&lvi); - - ListView_SetCheckState(GetDlgItem(hDlg,IDC_CHEAT_LIST), curr_idx, Cheat.c[counter].enabled); - - } - return true; case WM_PAINT: - { + { PAINTSTRUCT ps; BeginPaint (hDlg, &ps); EndPaint (hDlg, &ps); - } - return true; + return TRUE; + } + case WM_NOTIFY: + { + switch(wParam) { - switch(LOWORD(wParam)) - { case IDC_CHEAT_LIST: - if(0==ListView_GetSelectedCount(GetDlgItem(hDlg, IDC_CHEAT_LIST))) + { + LPNMHDR lP = (LPNMHDR) lParam; + switch (lP->code) { - EnableWindow(GetDlgItem(hDlg, IDC_DELETE_CHEAT), false); - EnableWindow(GetDlgItem(hDlg, IDC_UPDATE_CHEAT), false); - has_sel=false; - sel_idx=-1; - } - else - { - EnableWindow(GetDlgItem(hDlg, IDC_DELETE_CHEAT), true); - if(!has_sel||sel_idx!=ListView_GetSelectionMark(GetDlgItem(hDlg, IDC_CHEAT_LIST))) + case LVN_ITEMCHANGED: // selection changed event { - new_sel=3; - //change - TCHAR buf[25]; - LV_ITEM lvi; + NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)lP; + if(pNMListView->uNewState & LVIS_FOCUSED || + (pNMListView->uNewState ^ pNMListView->uOldState) & LVIS_SELECTED) + { + // set the selection to the edit form before updating buttons + int iItem = pNMListView->iItem; + std::tstring code = tstring_ListView_GetItemText(GetDlgItem(hDlg, IDC_CHEAT_LIST), iItem, CHEAT_LVITEM_CODE); + std::tstring name = tstring_ListView_GetItemText(GetDlgItem(hDlg, IDC_CHEAT_LIST), iItem, CHEAT_LVITEM_NAME); + Edit_SetText(GetDlgItem(hDlg, IDC_CHEAT_CODE), code.c_str()); + Edit_SetText(GetDlgItem(hDlg, IDC_CHEAT_NAME), name.c_str()); - ITEM_QUERY (lvi, IDC_CHEAT_LIST, 0, buf, 7); - - SetDlgItemText(hDlg, IDC_CHEAT_ADDRESS, lvi.pszText); - - ITEM_QUERY (lvi, IDC_CHEAT_LIST, 1, &buf[lstrlen(buf)], 3); - - SetDlgItemText(hDlg, IDC_CHEAT_CODE, buf); - TCHAR temp[4]; - int q; - _stscanf(lvi.pszText, TEXT("%02X"), &q); - _stprintf(temp, TEXT("%d"), q); - SetDlgItemText(hDlg, IDC_CHEAT_BYTE, temp); - - ITEM_QUERY (lvi, IDC_CHEAT_LIST, 2, buf, 24); - - internal_change=true; - SetDlgItemText(hDlg, IDC_CHEAT_DESCRIPTION, lvi.pszText); + // disable buttons that we don't have the right number of selected items for + WinCheatUpdateButtons(hDlg); + } + return TRUE; } - sel_idx=ListView_GetSelectionMark(GetDlgItem(hDlg, IDC_CHEAT_LIST)); - has_sel=true; } - - return true; - default: return false; } } - case WM_COMMAND: - { - switch(LOWORD(wParam)) - { - case IDC_CHEAT_DESCRIPTION: - { - switch(HIWORD(wParam)) - { - case EN_CHANGE: - - if(internal_change) - { - internal_change=!internal_change; - return false; - } - if(!has_sel) - return true; - EnableWindow(GetDlgItem(hDlg, IDC_UPDATE_CHEAT), true); - return true; - } - break; - - } - case IDC_ADD_CHEAT: - { - char temp[24]; - TCHAR tempDesc[24]; - GetDlgItemTextA(hDlg, IDC_CHEAT_CODE, temp, 23); - if(strcmp(temp, "")) - { - int j; - bool skip=false; - int count=1; - uint32 addy; - uint8 byte[3]; - //test game genie - if (NULL==S9xGameGenieToRaw (temp, addy, byte[0])) - skip=true; - //test goldfinger - - // if(!skip - - //test PAR - - if(!skip) - { - if(NULL==S9xProActionReplayToRaw(temp, addy, byte[0])) - skip=true; - } - - if(!skip) - return 0; - - for(j=0; j=curr_idx) - ct.index[k]++; - } - - - _stprintf(buffer, TEXT("%02X"), byte[j]); - memset(&lvi, 0, sizeof(LVITEM)); - lvi.iItem=curr_idx; - lvi.iSubItem=1; - lvi.mask=LVIF_TEXT; - lvi.pszText=buffer; - lvi.cchTextMax=2; - SendDlgItemMessage(hDlg,IDC_CHEAT_LIST, LVM_SETITEM, 0, (LPARAM)&lvi); - - - GetDlgItemText(hDlg, IDC_CHEAT_DESCRIPTION, tempDesc, 23); - - memset(&lvi, 0, sizeof(LVITEM)); - lvi.iItem=curr_idx; - lvi.iSubItem=2; - lvi.mask=LVIF_TEXT; - lvi.pszText=tempDesc; - lvi.cchTextMax=23; - SendDlgItemMessage(hDlg,IDC_CHEAT_LIST, LVM_SETITEM, 0, (LPARAM)&lvi); - - addy++; - - - } - } - else - { - uint8 byte; - TCHAR buffer[7]; - TCHAR buffer2[7]; - - GetDlgItemText(hDlg, IDC_CHEAT_ADDRESS, buffer, 7); - GetDlgItemText(hDlg, IDC_CHEAT_BYTE, buffer2, 7); - - int curr_idx=-1; - LVITEM lvi; - memset(&lvi, 0, sizeof(LVITEM)); - lvi.mask=LVIF_TEXT; - lvi.pszText=buffer; - lvi.cchTextMax=6; - lvi.iItem=0; - curr_idx=ListView_InsertItem(GetDlgItem(hDlg,IDC_CHEAT_LIST), &lvi); - - int scanres; - if(buffer2[0]=='$') - _stscanf(buffer2,TEXT("$%2X"), (unsigned int*)&scanres); - else _stscanf(buffer2,TEXT("%d"), &scanres); - byte = (uint8)(scanres & 0xff); - - _stprintf(buffer2, TEXT("%02X"), byte); - - memset(&lvi, 0, sizeof(LVITEM)); - lvi.iItem=curr_idx; - lvi.iSubItem=1; - lvi.mask=LVIF_TEXT; - lvi.pszText=buffer2; - lvi.cchTextMax=2; - SendDlgItemMessage(hDlg,IDC_CHEAT_LIST, LVM_SETITEM, 0, (LPARAM)&lvi); - - GetDlgItemText(hDlg, IDC_CHEAT_DESCRIPTION, tempDesc, 23); - - memset(&lvi, 0, sizeof(LVITEM)); - lvi.iItem=curr_idx; - lvi.iSubItem=2; - lvi.mask=LVIF_TEXT; - lvi.pszText=tempDesc; - lvi.cchTextMax=23; - SendDlgItemMessage(hDlg,IDC_CHEAT_LIST, LVM_SETITEM, 0, (LPARAM)&lvi); - } - } - break; - case IDC_UPDATE_CHEAT: - { - TCHAR temp[24]; - char code[24]; - GetDlgItemText(hDlg, IDC_CHEAT_CODE, temp, 23); - strcpy(code,_tToChar(temp)); - if(strcmp(code, "")) - { - int j; - bool skip=false; - int count=1; - uint32 addy; - uint8 byte[3]; - //test game genie - if (NULL==S9xGameGenieToRaw (code, addy, byte[0])) - skip=true; - //test goldfinger - - // if(!skip - - //test PAR - - if(!skip) - { - if(NULL==S9xProActionReplayToRaw(code, addy, byte[0])) - skip=true; - } - - if(!skip) - return 0; - - for(j=0;j<(int)Cheat.num_cheats;j++) - { - if(ct.index[j]==sel_idx) - ct.state[j]=Modified; - } - - for(j=0; jsel_idx) - ct.index[j]--; - } - } - ListView_DeleteItem(GetDlgItem(hDlg, IDC_CHEAT_LIST), sel_idx); - - break; - case IDC_CLEAR_CHEATS: - internal_change = true; - SetDlgItemText(hDlg,IDC_CHEAT_CODE,TEXT("")); - SetDlgItemText(hDlg,IDC_CHEAT_ADDRESS,TEXT("")); - SetDlgItemText(hDlg,IDC_CHEAT_BYTE,TEXT("")); - SetDlgItemText(hDlg,IDC_CHEAT_DESCRIPTION,TEXT("")); - ListView_SetItemState(GetDlgItem(hDlg, IDC_CHEAT_LIST),sel_idx, 0, LVIS_SELECTED|LVIS_FOCUSED); - ListView_SetSelectionMark(GetDlgItem(hDlg, IDC_CHEAT_LIST), -1); - sel_idx=-1; - has_sel=false; - break; - case IDC_CHEAT_CODE: - { - uint32 j, k; - long index; - char buffer[15]; - char buffer2[15]; - POINT point; - switch(HIWORD(wParam)) - { - case EN_CHANGE: - if(internal_change) - { - internal_change=false; - return true; - } - SendMessageA((HWND)lParam, WM_GETTEXT, 15,(LPARAM)buffer); - GetCaretPos(&point); - - index = SendMessageA((HWND)lParam,(UINT) EM_CHARFROMPOS, 0, (LPARAM) ((point.x&0x0000FFFF) | (((point.y&0x0000FFFF))<<16))); - - k=0; - for(j=0; j='0' && buffer[j]<='9') || (buffer[j]>='A' && buffer[j]<='F') || buffer[j]=='-' || buffer[j]=='X') - { - buffer2[k]=buffer[j]; - k++; - } - else index --; - } - buffer2[k]='\0'; - - if(has_sel&&!new_sel&&strlen(buffer2)!=0) - { - SetDlgItemTextA(hDlg, IDC_CHEAT_ADDRESS, ""); - SetDlgItemTextA(hDlg, IDC_CHEAT_BYTE, ""); - - } - - if(new_sel!=0) - new_sel--; - - internal_change=true; - SendMessageA((HWND)lParam, WM_SETTEXT, 0,(LPARAM)buffer2); - SendMessageA((HWND)lParam, (UINT) EM_SETSEL, (WPARAM) (index), index); - - uint32 addy; - uint8 val; - bool8 sram; - uint8 bytes[3]; - if(NULL==S9xGameGenieToRaw(buffer2, addy, val)||NULL==S9xProActionReplayToRaw(buffer2, addy, val)||NULL==S9xGoldFingerToRaw(buffer2, addy, sram, val, bytes)) - { - if(has_sel) - EnableWindow(GetDlgItem(hDlg, IDC_UPDATE_CHEAT), true); - else EnableWindow(GetDlgItem(hDlg, IDC_UPDATE_CHEAT), false); - EnableWindow(GetDlgItem(hDlg, IDC_ADD_CHEAT), true); - } - else - { - EnableWindow(GetDlgItem(hDlg, IDC_ADD_CHEAT), false); - EnableWindow(GetDlgItem(hDlg, IDC_UPDATE_CHEAT), false); - } - - // SetDlgItemText(hDlg, IDC_CHEAT_ADDRESS, ""); - // SetDlgItemText(hDlg, IDC_CHEAT_BYTE, ""); - break; - } - break; - } - case IDC_CHEAT_ADDRESS: - { - uint32 j, k; - long index; - char buffer[7]; - char buffer2[7]; - POINT point; - switch(HIWORD(wParam)) - { - case EN_CHANGE: - if(internal_change) - { - internal_change=false; - return true; - } - SendMessageA((HWND)lParam, WM_GETTEXT, 7,(LPARAM)buffer); - GetCaretPos(&point); - - index = SendMessageA((HWND)lParam,(UINT) EM_CHARFROMPOS, 0, (LPARAM) ((point.x&0x0000FFFF) | (((point.y&0x0000FFFF))<<16))); - - k=0; - for(j=0; j='0' && buffer[j]<='9') || (buffer[j]>='A' && buffer[j]<='F')) - { - buffer2[k]=buffer[j]; - k++; - } - else index --; - } - buffer2[k]='\0'; - - - internal_change=true; - SendMessageA((HWND)lParam, WM_SETTEXT, 0,(LPARAM)buffer2); - SendMessageA((HWND)lParam, (UINT) EM_SETSEL, (WPARAM) (index), index); - - SendMessageA(GetDlgItem(hDlg, IDC_CHEAT_BYTE), WM_GETTEXT, 4,(LPARAM)buffer); - - if(has_sel&&!new_sel&&0!=strlen(buffer2)) - SetDlgItemTextA(hDlg, IDC_CHEAT_CODE, ""); - - if(new_sel!=0) - new_sel--; - - if(strlen(buffer2)!=0 && strlen(buffer) !=0) - { - if(has_sel) - EnableWindow(GetDlgItem(hDlg, IDC_UPDATE_CHEAT), true); - else EnableWindow(GetDlgItem(hDlg, IDC_UPDATE_CHEAT), false); - EnableWindow(GetDlgItem(hDlg, IDC_ADD_CHEAT), true); - } - else - { - EnableWindow(GetDlgItem(hDlg, IDC_ADD_CHEAT), false); - EnableWindow(GetDlgItem(hDlg, IDC_UPDATE_CHEAT), false); - } - - break; - } - break; - } - case IDC_CHEAT_BYTE: - { - uint32 j, k; - long index; - char buffer[4]; - char buffer2[4]; - POINT point; - switch(HIWORD(wParam)) - { - case EN_CHANGE: - if(internal_change) - { - internal_change=false; - return true; - } - SendMessageA((HWND)lParam, WM_GETTEXT, 4,(LPARAM)buffer); - GetCaretPos(&point); - - index = SendMessageA((HWND)lParam,(UINT) EM_CHARFROMPOS, 0, (LPARAM) ((point.x&0x0000FFFF) | (((point.y&0x0000FFFF))<<16))); - - k=0; - for(j=0; j='0' && buffer[j]<='9') || (buffer[j]>='A' && buffer[j]<='F') || buffer[j]=='$') - { - buffer2[k]=buffer[j]; - k++; - } - else index --; - } - buffer2[k]='\0'; - - if(has_sel&&!new_sel&&0!=strlen(buffer2)) - SetDlgItemTextA(hDlg, IDC_CHEAT_CODE, ""); - - if(new_sel!=0) - new_sel--; - - internal_change=true; - SendMessageA((HWND)lParam, WM_SETTEXT, 0,(LPARAM)buffer2); - SendMessageA((HWND)lParam, (UINT) EM_SETSEL, (WPARAM) (index), index); - - SendMessageA(GetDlgItem(hDlg, IDC_CHEAT_ADDRESS), WM_GETTEXT, 7,(LPARAM)buffer); - if(strlen(buffer2)!=0 && strlen(buffer) !=0) - { - if(has_sel) - EnableWindow(GetDlgItem(hDlg, IDC_UPDATE_CHEAT), true); - else EnableWindow(GetDlgItem(hDlg, IDC_UPDATE_CHEAT), false); - EnableWindow(GetDlgItem(hDlg, IDC_ADD_CHEAT), true); - } - else - { - EnableWindow(GetDlgItem(hDlg, IDC_ADD_CHEAT), false); - EnableWindow(GetDlgItem(hDlg, IDC_UPDATE_CHEAT), false); - } - //SetDlgItemText(hDlg, IDC_CHEAT_CODE, ""); - break; - } - break; - } - case IDOK: - { - int k,l; - bool hit; - unsigned int scanned; - for(k=0;k=0;l--) - { - if(ct.state[l]==Deleted) - { - S9xDeleteCheat(l); - } - } - } - case IDCANCEL: - delete [] ct.index; - delete [] ct.state; - EndDialog(hDlg, 0); - return true; - default:return false; - } - } - default: return false; + return FALSE; } + + case WM_COMMAND: + { + switch(LOWORD(wParam)) + { + case IDC_CHEAT_CODE: + { + switch(HIWORD(wParam)) + { + case EN_CHANGE: + FilterTextBox(GetDlgItem(hDlg, IDC_CHEAT_CODE), TEXT("01234566789ABCDEFX+-:")); + WinCheatUpdateButtons(hDlg); + return TRUE; + } + return FALSE; + } + + case IDC_CHEAT_SYNTAX: + { + MessageBox(hDlg, TEXT("Pro Action Replay\n7E0A2A:06 (colon ':' can be omitted)\n\nGame Genie\nC235-C768\n\nMultiple codes at once\n7E0A2A:06+C235-C768"), SNES9X_INFO, MB_OK|MB_ICONINFORMATION); + return TRUE; + } + + case IDC_CHEAT_DEC: + { + switch(HIWORD(wParam)) + { + case EN_CHANGE: + FilterTextBox(GetDlgItem(hDlg, IDC_CHEAT_DEC), TEXT("-01234566789")); + if (!disableRadixConverter) + { + TCHAR stmp[64]; + GetDlgItemText(hDlg, IDC_CHEAT_DEC, stmp, 64); + uint32 ivalue = _tcstoul(stmp, NULL, 10); + disableRadixConverter = true; + _stprintf(stmp, TEXT("%X"), ivalue); + SetDlgItemText(hDlg, IDC_CHEAT_HEX, stmp); + disableRadixConverter = false; + } + return TRUE; + } + return FALSE; + } + + case IDC_CHEAT_HEX: + { + switch(HIWORD(wParam)) + { + case EN_CHANGE: + FilterTextBox(GetDlgItem(hDlg, IDC_CHEAT_HEX), TEXT("01234566789ABCDEF")); + if (!disableRadixConverter) + { + TCHAR stmp[64]; + GetDlgItemText(hDlg, IDC_CHEAT_HEX, stmp, 64); + uint32 ivalue = _tcstoul(stmp, NULL, 16); + disableRadixConverter = true; + _stprintf(stmp, TEXT("%u"), ivalue); + SetDlgItemText(hDlg, IDC_CHEAT_DEC, stmp); + disableRadixConverter = false; + } + return TRUE; + } + return FALSE; + } + + case IDC_ADD_CHEAT: + { + std::tstring code = tstring_GetWindowText(GetDlgItem(hDlg, IDC_CHEAT_CODE)); + std::tstring name = tstring_GetWindowText(GetDlgItem(hDlg, IDC_CHEAT_NAME)); + + WinAddCheatToDialog(hDlg, code.c_str(), name.c_str(), TRUE); + Edit_SetText(GetDlgItem(hDlg, IDC_CHEAT_CODE), TEXT("")); + Edit_SetText(GetDlgItem(hDlg, IDC_CHEAT_NAME), TEXT("")); + return TRUE; + } + + case IDC_UPDATE_CHEAT: + { + std::tstring code = tstring_GetWindowText(GetDlgItem(hDlg, IDC_CHEAT_CODE)); + std::tstring name = tstring_GetWindowText(GetDlgItem(hDlg, IDC_CHEAT_NAME)); + + int selCount = ListView_GetSelectedCount(GetDlgItem(hDlg, IDC_CHEAT_LIST)); + if (selCount == 1) { + int iItem = ListView_GetSelectionMark(GetDlgItem(hDlg, IDC_CHEAT_LIST)); + WinUpdateCheatInDialog(hDlg, iItem, code.c_str(), name.c_str()); + } + return TRUE; + } + + case IDC_DELETE_CHEAT: + { + HWND hCheatList = GetDlgItem(hDlg, IDC_CHEAT_LIST); + int cheatIndex = ListView_GetNextItem(hCheatList, -1, LVNI_ALL | LVNI_SELECTED); + while (cheatIndex >= 0) + { + ListView_DeleteItem(hCheatList, cheatIndex); + cheatIndex = ListView_GetNextItem(hCheatList, -1, LVNI_ALL | LVNI_SELECTED); + } + Edit_SetText(GetDlgItem(hDlg, IDC_CHEAT_CODE), TEXT("")); + Edit_SetText(GetDlgItem(hDlg, IDC_CHEAT_NAME), TEXT("")); + return TRUE; + } + + case IDC_CLEAR_CHEATS: + { + Edit_SetText(GetDlgItem(hDlg, IDC_CHEAT_CODE), TEXT("")); + Edit_SetText(GetDlgItem(hDlg, IDC_CHEAT_NAME), TEXT("")); + return TRUE; + } + + case IDOK: + { + S9xDeleteCheats(); + std::vector cheats = WinGetCheatsFromDialog(hDlg); + std::vector::iterator iter = cheats.begin(); + while (iter != cheats.end()) + { + S9xAddCheat(iter->enabled, TRUE, iter->code.c_str(), iter->name.c_str()); + iter++; + } + S9xApplyCheats(); + S9xSaveCheatFile(S9xGetFilename(".cht", CHEAT_DIR)); + EndDialog(hDlg, 0); + return TRUE; + } + + case IDCANCEL: + { + S9xApplyCheats(); + EndDialog(hDlg, 0); + return TRUE; + } + + default: + return FALSE; + } + } + + default: + return FALSE; + } + } @@ -9235,6 +8835,14 @@ bool TestRange(int val_type, S9xCheatDataSize bytes, uint32 value) } +#define ITEM_QUERY(a, b, c, d, e) memset(&a, 0, sizeof(LV_ITEM)); \ + a.iItem= ListView_GetSelectionMark(GetDlgItem(hDlg, b)); \ + a.iSubItem=c; \ + a.mask=LVIF_TEXT; \ + a.pszText=d; \ + a.cchTextMax=e; \ + ListView_GetItem(GetDlgItem(hDlg, b), &a); + INT_PTR CALLBACK DlgCheatSearch(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) { static S9xCheatDataSize bytes; @@ -9745,9 +9353,9 @@ INT_PTR CALLBACK DlgCheatSearch(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lPara // int idx=-1; LVITEM lvi; static TCHAR buf[12]; // the following code assumes this variable is static, I think - memset(&cht, 0, sizeof(struct SCheat)); + memset(&cht, 0, sizeof(struct ICheat)); - //retrieve and convert to SCheat + //retrieve and convert to ICheat if(bytes==S9X_8_BITS) cht.size=1; @@ -10187,6 +9795,25 @@ INT_PTR CALLBACK DlgCheatSearchAdd(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP } break; //HEX case 2: + switch(new_cheat->size) + { + case 1: + if(new_cheat->new_val > 0x7f) new_cheat->new_val |= ~0xff; + if(new_cheat->saved_val > 0x7f) new_cheat->saved_val |= ~0xff; + break; + case 2: + if(new_cheat->new_val > 0x7fff) new_cheat->new_val |= ~0xffff; + if(new_cheat->saved_val > 0x7fff) new_cheat->saved_val |= ~0xffff; + break; + case 3: + if(new_cheat->new_val > 0x7fffff) new_cheat->new_val |= ~0xffffff; + if(new_cheat->saved_val > 0x7fffff) new_cheat->saved_val |= ~0xffffff; + break; + case 4: + if(new_cheat->new_val > 0x7fffffff) new_cheat->new_val |= ~0xffffffff; + if(new_cheat->saved_val > 0x7fffffff) new_cheat->saved_val |= ~0xffffffff; + break; + } memset(buf,0,sizeof(TCHAR) * 12); _stprintf(buf, TEXT("%d"), new_cheat->new_val); SetDlgItemText(hDlg, IDC_NC_CURRVAL, buf); @@ -10301,11 +9928,15 @@ INT_PTR CALLBACK DlgCheatSearchAdd(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP strncpy(new_cheat->name,_tToChar(tempBuf),22); new_cheat->enabled=TRUE; + + std::string code; for(int byteIndex = 0; byteIndex < new_cheat->size; byteIndex++) { - S9xAddCheat(new_cheat->enabled,new_cheat->saved_val,new_cheat->address+byteIndex,(new_cheat->new_val>>(8*byteIndex))&0xFF); - strcpy(Cheat.c[Cheat.num_cheats-1].name,new_cheat->name); + char temp[10]; + sprintf(temp, "%s%06X%02X", byteIndex > 0 ? "+" : "", new_cheat->address + byteIndex, (new_cheat->new_val >> (8 * byteIndex)) & 0xFF); + code.append(temp); } + S9xAddCheat(new_cheat->enabled, TRUE, code.c_str(), new_cheat->name); ret=0; } } @@ -10923,7 +10554,7 @@ void S9xPostRomInit() if (Settings.ApplyCheats) { extern struct SCheatData Cheat; - for (uint32 i = 0; i < Cheat.num_cheats; i++) + for (uint32 i = 0; i < Cheat.c.size(); i++) { if (Cheat.c [i].enabled) { diff --git a/win32/wsnes9x.h b/win32/wsnes9x.h index c43a5c85..18ce76b3 100644 --- a/win32/wsnes9x.h +++ b/win32/wsnes9x.h @@ -224,6 +224,22 @@ #define _tFromAnsi #endif +#include +#include +namespace std { +#ifndef tstring +#ifdef UNICODE + typedef wstring tstring; + typedef wstringbuf tstringbuf; + typedef wstringstream tstringstream; +#else + typedef string tstring; + typedef stringbuf tstringbuf; + typedef stringstream tstringstream; +#endif +#endif +} + /****************************************************************************/ inline static void Log (const char *str) {