From a3e31556787c87fd708dfd4a6a194a5d62a60c59 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Fri, 23 Jul 2010 05:22:12 +0000 Subject: [PATCH] Added a button to the "Gecko Codes" panel to download/parse codes from geckocodes.org. Codes that require modifiers (the XXXX business) will still not work properly, though they should load/save fine. A few more code types should work now. (All non-ASM type codes should at least attempt to run :p) Hacked a param into IniFile::GetLines to disable removal of text after # chars, so codes with # in the name/notes should load fine. git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@5949 8ced0084-cf51-0410-be5f-012b33b47a6e --- Source/Core/Common/Src/IniFile.cpp | 20 +- Source/Core/Common/Src/IniFile.h | 2 +- Source/Core/Core/Src/GeckoCode.cpp | 346 ++++++++++++-------- Source/Core/Core/Src/GeckoCode.h | 7 +- Source/Core/Core/Src/GeckoCodeConfig.cpp | 34 +- Source/Core/DolphinWX/Src/CheatsWindow.cpp | 6 +- Source/Core/DolphinWX/Src/GeckoCodeDiag.cpp | 195 +++++++++-- Source/Core/DolphinWX/Src/GeckoCodeDiag.h | 13 +- Source/Core/DolphinWX/Src/ISOProperties.cpp | 2 +- 9 files changed, 436 insertions(+), 189 deletions(-) diff --git a/Source/Core/Common/Src/IniFile.cpp b/Source/Core/Common/Src/IniFile.cpp index 325b87278a..85a88bc7fc 100644 --- a/Source/Core/Common/Src/IniFile.cpp +++ b/Source/Core/Common/Src/IniFile.cpp @@ -350,7 +350,7 @@ bool IniFile::GetKeys(const char* sectionName, std::vector& keys) c } // Return a list of all lines in a section -bool IniFile::GetLines(const char* sectionName, std::vector& lines) const +bool IniFile::GetLines(const char* sectionName, std::vector& lines, const bool remove_comments) const { const Section* section = GetSection(sectionName); if (!section) @@ -360,15 +360,19 @@ bool IniFile::GetLines(const char* sectionName, std::vector& lines) for (std::vector::const_iterator iter = section->lines.begin(); iter != section->lines.end(); ++iter) { std::string line = StripSpaces(*iter); - int commentPos = (int)line.find('#'); - if (commentPos == 0) - { - continue; - } - if (commentPos != (int)std::string::npos) + if (remove_comments) { - line = StripSpaces(line.substr(0, commentPos)); + int commentPos = (int)line.find('#'); + if (commentPos == 0) + { + continue; + } + + if (commentPos != (int)std::string::npos) + { + line = StripSpaces(line.substr(0, commentPos)); + } } lines.push_back(line); diff --git a/Source/Core/Common/Src/IniFile.h b/Source/Core/Common/Src/IniFile.h index 05eff76cdd..3c4959f04b 100644 --- a/Source/Core/Common/Src/IniFile.h +++ b/Source/Core/Common/Src/IniFile.h @@ -123,7 +123,7 @@ public: bool GetKeys(const char* sectionName, std::vector& keys) const; void SetLines(const char* sectionName, const std::vector &lines); - bool GetLines(const char* sectionName, std::vector& lines) const; + bool GetLines(const char* sectionName, std::vector& lines, const bool remove_comments = true) const; bool DeleteKey(const char* sectionName, const char* key); bool DeleteSection(const char* sectionName); diff --git a/Source/Core/Core/Src/GeckoCode.cpp b/Source/Core/Core/Src/GeckoCode.cpp index 3a06bf9177..2d283def58 100644 --- a/Source/Core/Core/Src/GeckoCode.cpp +++ b/Source/Core/Core/Src/GeckoCode.cpp @@ -57,7 +57,8 @@ u32 GeckoCode::Code::GetAddress() const static Common::CriticalSection active_codes_lock; // currently running code -static GeckoCode::Code current_code; +static GeckoCode::Code *codes_start = NULL, *current_code = NULL; +static const GeckoCode::Code *codes_end = NULL; // Functions for each code type bool RamWriteAndFill(); @@ -91,7 +92,7 @@ void SetActiveCodes(const std::vector& gcodes) active_codes_lock.Leave(); } -bool RunGeckoCode(const GeckoCode& gecko_code) +bool RunGeckoCode(GeckoCode& gecko_code) { static bool (*code_type_funcs[])(void) = { RamWriteAndFill, RegularIf, BaPoOps, FlowControl, RegisterOps, SpecialIf, AsmSwitchRange, EndCodes }; @@ -100,14 +101,11 @@ bool RunGeckoCode(const GeckoCode& gecko_code) pointer_address = 0x80000000; code_execution_counter = 0; - std::vector::const_iterator - codes_iter = gecko_code.codes.begin(), - codes_end = gecko_code.codes.end(); - for (; codes_iter != codes_end; ++codes_iter) + current_code = codes_start = &*gecko_code.codes.begin(); + codes_end = &*gecko_code.codes.end(); + for (; current_code < codes_end; ++current_code) { - const GeckoCode::Code& code = *codes_iter; - - current_code = code; + const GeckoCode::Code& code = *current_code; bool result = true; @@ -133,16 +131,12 @@ bool RunGeckoCode(const GeckoCode& gecko_code) if (false == result) { PanicAlert("GeckoCode failed to run (CT%i CST%i) (%s)" - "(Either a bad code or the code type is not yet supported.)" + "\n(either a bad code or the code type is not yet supported.)" , code.type, code.subtype, gecko_code.name.c_str()); return false; } - - - } - return true; } @@ -151,7 +145,7 @@ bool RunActiveCodes() if (false == active_codes_lock.TryEnter()) return true; - std::vector::const_iterator + std::vector::iterator gcodes_iter = active_codes.begin(), gcodes_end = active_codes.end(); for (; gcodes_iter!=gcodes_end; ++gcodes_iter) @@ -167,19 +161,19 @@ bool RunActiveCodes() } // CT0: Direct ram write/fill -// NOT COMPLETE, last 2 subtypes not started +// COMPLETE, maybe bool RamWriteAndFill() { - const GeckoCode::Code& code = current_code; + const GeckoCode::Code& code = *current_code; u32 new_addr = code.GetAddress(); const u32& data = code.data; - u16 count = (data >> 16) + 1; + u16 count = (data >> 16) + 1; // note: +1 switch (code.subtype) { // CST0: 8bits Write & Fill - case 0x0 : + case DATATYPE_8BIT : while (count--) { Memory::Write_U16((u16)data, new_addr); @@ -188,7 +182,7 @@ bool RamWriteAndFill() break; // CST1: 16bits Write & Fill - case 0x1 : + case DATATYPE_16BIT : while (count--) { Memory::Write_U16((u16)data, new_addr); @@ -197,23 +191,76 @@ bool RamWriteAndFill() break; // CST2: 32bits Write - case 0x2 : + case DATATYPE_32BIT : Memory::Write_U32((u32)data, new_addr); break; // CST3: String Code case 0x3 : - // TODO: - return false; + count = code.data; // count is different from the other subtypes + while (count) + { + if (codes_end == ++current_code) + return false; + + // write bytes from address + int byte_num = 4; + while (byte_num-- && count) + { + Memory::Write_U8((u8)(current_code->address >> byte_num * 8), new_addr); + ++new_addr; + --count; + } + + // write bytes from data + byte_num = 4; + while (byte_num-- && count) + { + Memory::Write_U8((u8)(current_code->data >> byte_num * 8), new_addr); + ++new_addr; + --count; + } + } break; // CST4: Serial Code case 0x4 : { - // TODO: complete - // u32 new_data = data; - - return false; + if (codes_end == ++current_code) + return false; + u32 new_data = data; // starting value of data + const u8 data_type = current_code->address >> 28; + const u32 data_inc = current_code->data; // amount to increment the data + const u16 addr_inc = (u16)current_code->address; // amount to increment the address + count = (current_code->address >> 16) & 0xFFF + 1; // count is different from the other subtypes, note: +1 + while (count--) + { + // switch inside the loop, :/ o well + switch (data_type) + { + case DATATYPE_8BIT : + Memory::Write_U8((u8)new_data, new_addr); + new_data = (u8)new_data + (u8)data_inc; + break; + + case DATATYPE_16BIT : + Memory::Write_U16((u16)new_data, new_addr); + new_data = (u16)new_data + (u16)data_inc; + break; + + case DATATYPE_32BIT : + Memory::Write_U32((u32)new_data, new_addr); + new_data += data_inc; + break; + + // INVALID DATATYPE + default : + return false; + break; + } + + new_addr += addr_inc; + } } break; @@ -230,7 +277,7 @@ bool RamWriteAndFill() // COMPLETE bool RegularIf() { - const GeckoCode::Code& code = current_code; + const GeckoCode::Code& code = *current_code; const bool is_endif = !!(code.address & 0x1); @@ -306,7 +353,7 @@ bool RegularIf() // NOT COMPLETE, last 2 subtypes aren't done bool BaPoOps() { - const GeckoCode::Code& code = current_code; + const GeckoCode::Code& code = *current_code; // base_address vs pointer (ba vs po) u32& change_address = (code.subtype & 0x4) ? pointer_address : base_address; @@ -360,10 +407,10 @@ bool BaPoOps() } // CT3 Repeat/Goto/Gosub/Return -// NOT COMPLETE +// COMPLETE, maybe bool FlowControl() { - const GeckoCode::Code& code = current_code; + const GeckoCode::Code& code = *current_code; // only the return subtype runs when code execution is off if (false == CodeExecution() && code.subtype != 0x2) @@ -376,10 +423,8 @@ bool FlowControl() { // CST0 : Set Repeat case 0x0 : - // TODO: store address of next code as well - block[block_num].address = code.address & 0xFFFF; - // block[block_num].number = ; - return false; // + block[block_num].number = code.address & 0xFFFF; + block[block_num].address = (u32)(current_code - codes_start + 1); break; // CST1 : Execute Repeat @@ -387,7 +432,8 @@ bool FlowControl() if (block[block_num].number) { --block[block_num].number; - // TODO: jump to code block + // needs -1 cause iterator gets ++ after code runs + current_code = codes_start + block[block_num].address - 1; } return false; // break; @@ -395,20 +441,43 @@ bool FlowControl() // CST2 : Return case 0x2 : if (((code.address >> 20) & 0xF) ^ (u32)CodeExecution()) - // TODO: jump to block[block_num].number - return false; // + { + // needs -1 cause iterator gets ++ after code runs + current_code = codes_start + block[block_num].address - 1; + } break; // CST3 : Goto case 0x3 : - // TODO: - return false; // + if (((code.address >> 20) & 0xF) ^ (u32)CodeExecution()) + { + GeckoCode::Code* const target_code = current_code + (s16)(code.address & 0xFFFF); + + if (target_code >= codes_start && target_code < codes_end) + { + // needs -1 cause iterator gets ++ after code runs + current_code = target_code - 1; + } + else + return false; // trying to GOTO to bad address + } break; // CST4 : Gosub case 0x4 : - // TODO: - return false; // + if (((code.address >> 20) & 0xF) ^ (u32)CodeExecution()) + { + GeckoCode::Code* const target_code = current_code + (s16)(code.address & 0xFFFF); + + if (target_code >= codes_start && target_code < codes_end) + { + block[block_num].address = u32(current_code - codes_start + 1); + // needs -1 cause iterator gets ++ after code runs + current_code = target_code - 1; + } + else + return false; // trying to GOSUB to bad address + } break; // INVALID SUBTYPE @@ -421,10 +490,10 @@ bool FlowControl() } // CT4 Gecko Register Operations -// NOT COMPLETE, need to do memory copy 1,2 +// COMPLETE, maybe bool RegisterOps() { - const GeckoCode::Code& code = current_code; + const GeckoCode::Code& code = *current_code; // 80TYZZZN XXXXXXXX @@ -519,15 +588,27 @@ bool RegisterOps() break; // CST5 : Memory Copy 1 - case 0x5 : - // TODO: - return false; // - break; - // CST6 : Memory Copy 2 + case 0x5 : case 0x6 : - // TODO: - return false; // + { + const u8 src_gr = (code.z & 0xF); + const u8 dst_gr = (code.n); + + u16 count = (u16)(code.address >> 8); + + // docs don't specify allowing 0xF for source and dest, but it can't hurt + u32 src_addr = ((0xF == src_gr) ? (code.use_po ? pointer_address : base_address) : gecko_register[src_gr]); + u32 dst_addr = ((0xF == dst_gr) ? (code.use_po ? pointer_address : base_address) : gecko_register[dst_gr]); + + if (0x5 == code.subtype) + dst_addr += new_data; + else + src_addr += new_data; + + while (count--) + Memory::Write_U8(Memory::Read_U8(src_addr++), dst_addr++); + } break; // INVALID SUBTYPE @@ -589,8 +670,7 @@ bool MathOperation(u32& ret, const u32 left, const u32 right, const u8 type) // 8 : asr (arithmetic shift right) case 0x8 : - // TODO: wuts this - return false; + ret = (left >> right) | (left & 0x80000000); break; // TODO: these float ops good? @@ -614,10 +694,11 @@ bool MathOperation(u32& ret, const u32 left, const u32 right, const u8 type) } // CT5: Special If codes (16bits) -// NOT COMPLETE, part 2 (counter stuff) not started +// COMPLETE, maybe (ugly) bool SpecialIf() { - const GeckoCode::Code& code = current_code; + // counter can modify the code :/ + GeckoCode::Code& code = *current_code; const bool is_endif = !!(code.address & 0x1); @@ -630,99 +711,87 @@ bool SpecialIf() bool result = false; + // TODO: should these be signed? probably? + s16 left_val = 0, right_val = 0; + // if code_execution is on, execute the conditional if (CodeExecution()) { const u32 addr = code.GetAddress() & ~0x1; const u32& data = code.data; - // A-______ NM00YYYY - if (code.subtype ^ 0x4) { // CT5 Part1 : Unknown values comparison + // A-______ NM00YYYY const u8 n = (u8)(data >> 28); const u8 m = (u8)((data >> 24) & 0xF); const u16 y = (u16)data; - // TODO: should these be signed? probably? - const s16 left_val = Memory::Read_U16(((0xF == n) ? addr : gecko_register[n]) & ~y); - const s16 right_val = Memory::Read_U16(((0xF == m) ? addr : gecko_register[m]) & ~y); - - switch (code.subtype) - { - // CST0 : 16bits (endif, then) If equal - case 0x0 : - result = (left_val == right_val); - break; - - // CST1 : 16bits (endif, then) If not equal - case 0x1 : - result = (left_val != right_val); - break; - - // CST2 : 16bits (endif, then) If greater - case 0x2 : - result = (left_val > right_val); - break; - - // CST3 : 16bits (endif, then) If lower - case 0x3 : - result = (left_val < right_val); - break; - } + left_val = Memory::Read_U16(((0xF == n) ? addr : gecko_register[n]) & ~y); + right_val = Memory::Read_U16(((0xF == m) ? addr : gecko_register[m]) & ~y); } else { // CT5 Part2 : 16bits Counter check - // TODO: + // A-0ZZZZT MMMMXXXX - switch (code.subtype) - { - // CST4 : 16bits (endif, then) If counter value equal - case 0x4 : - // TODO: - return false; // - break; - - // CST5 : 16bits (endif, then) If counter value not equal - case 0x5 : - // TODO: - return false; // - break; - - // CST6 : 16bits (endif, then) If counter value greater - case 0x6 : - // TODO: - return false; // - break; - - // CST7 : 16bits (endif, then) If counter value lower - case 0x7 : - // TODO: - return false; // - break; - } + left_val = (u16)(data) & ~(u16)(data >> 16); + right_val = (u16)(addr >> 4); } + + switch (code.subtype & 0x3) + { + // CST0 : 16bits (endif, then) If equal + case 0x0 : + result = (left_val == right_val); + break; + + // CST1 : 16bits (endif, then) If not equal + case 0x1 : + result = (left_val != right_val); + break; + + // CST2 : 16bits (endif, then) If greater + case 0x2 : + result = (left_val > right_val); + break; + + // CST3 : 16bits (endif, then) If lower + case 0x3 : + result = (left_val < right_val); + break; + } + } + else if (code.subtype & 0x4) + { + // counters get reset if code execution is off + code.address &= ~0x000FFFF0; } // if the conditional returned false, or it never ran because execution is off, increase the code execution counter if (false == result) ++code_execution_counter; + else if (code.subtype & 0x4) + { + // counters gets advanced if condition was true + // right_val is the value of the counter + + code.address &= ~0x000FFFF0; + code.address |= ((right_val+1) << 4); + } return true; } // CT6 ASM Codes, On/Off switch and Address Range Check -// NOT COMPLETE, hardly started -// fix the logic flow in this one +// NOT COMPLETE, asm stuff not started +// fix the uglyness bool AsmSwitchRange() { - const GeckoCode::Code& code = current_code; - - // only used for the last 2 subtypes - const bool is_endif = !!(code.address & 0x1); + // the switch subtype modifies the code :/ + GeckoCode::Code& code = *current_code; // only run if code_execution is set or this code is a switch or rangecheck subtype // the switch and rangecheck run if exectution_counter is 1 (directly inside the failed if) if they are an endif @@ -730,11 +799,11 @@ bool AsmSwitchRange() { if (code.subtype < 0x6) return true; - else if (false == (1 == code_execution_counter && is_endif)) + else if (1 != code_execution_counter) return true; } - const u32& data = code.data; + u32& data = code.data; switch (code.subtype) { @@ -752,24 +821,47 @@ bool AsmSwitchRange() // CST3 : Create a branch case 0x3 : - // TODO: + // watever + //if (code.data) return false; // break; // CST6 : On/Off switch case 0x6 : - // TODO: - return false; // + // in the 1st bit of code.data, i store if code execution was previously off + // in the 2nd bit of code.data, i store the switch's on/off state + if (CodeExecution()) + { + if (data & 0x1) + data ^= 0x2; // if code exec was previously off, flip the switch + + // mark code execution as previously on + data &= ~0x1; + } + else + { + // mark code execution as previously off + data |= 0x1; + } + + // set code execution to the state of the switch + code_execution_counter = !(data & 0x2); break; // CST7 : Address range check (If... code) case 0x7 : - if (code_execution_counter) + { + const bool is_endif = !!(code.address & 0x1); + if (is_endif) + --code_execution_counter; + + if (CodeExecution()) { const u32 addr = (code.use_po ? pointer_address : base_address); - if (addr >= (data & 0xFFFF0000) && addr <= (data << 16)) - --code_execution_counter; + if (addr < (data & 0xFFFF0000) || addr > (data << 16)) + ++code_execution_counter; } + } break; // INVALID SUBTYPE @@ -785,7 +877,7 @@ bool AsmSwitchRange() // COMPLETE, maybe bool EndCodes() { - const GeckoCode::Code& code = current_code; + const GeckoCode::Code& code = *current_code; const u32& data = code.data; const u32 x = (data & 0xFFFF0000); @@ -802,9 +894,7 @@ bool EndCodes() // CST0 : Full Terminator case 0x0 : // clears the code execution status - // TODO: should this always stop all codes, even if execution is off? - if (CodeExecution()) - code_execution_counter = -1; // silly maybe + code_execution_counter = 0; break; // CST1 : Endif (+else) diff --git a/Source/Core/Core/Src/GeckoCode.h b/Source/Core/Core/Src/GeckoCode.h index 8073b18096..af8c17e6a2 100644 --- a/Source/Core/Core/Src/GeckoCode.h +++ b/Source/Core/Core/Src/GeckoCode.h @@ -18,6 +18,8 @@ namespace Gecko struct Code { + Code() : address(0), data(0) {} + union { u32 address; @@ -50,11 +52,14 @@ namespace Gecko //}; }; + std::string original_line; + u32 GetAddress() const; }; std::vector codes; - std::string name, description, creator; + std::string name, creator; + std::vector notes; bool enabled; }; diff --git a/Source/Core/Core/Src/GeckoCodeConfig.cpp b/Source/Core/Core/Src/GeckoCodeConfig.cpp index 1827ca434c..f0e5a0585b 100644 --- a/Source/Core/Core/Src/GeckoCodeConfig.cpp +++ b/Source/Core/Core/Src/GeckoCodeConfig.cpp @@ -15,7 +15,7 @@ namespace Gecko void LoadCodes(const IniFile& inifile, std::vector& gcodes) { std::vector lines; - inifile.GetLines(GECKO_CODE_INI_SECTION, lines); + inifile.GetLines(GECKO_CODE_INI_SECTION, lines, false); GeckoCode gcode; @@ -29,6 +29,8 @@ void LoadCodes(const IniFile& inifile, std::vector& gcodes) std::istringstream ss(*lines_iter); + int read_state = 0; + switch ((*lines_iter)[0]) { @@ -46,30 +48,24 @@ void LoadCodes(const IniFile& inifile, std::vector& gcodes) gcode.name = StripSpaces(gcode.name); // read the code creator name std::getline(ss, gcode.creator, ']'); + read_state = 0; break; - // description + // notes case '*': - ss.seekg(1); - std::getline(ss, gcode.description); - break; - - // start of code option list - case ':': - // TODO: + gcode.notes.push_back(std::string(++lines_iter->begin(), lines_iter->end())); break; // either part of the code, or an option choice default : - // TODO: check if we are reading an option list { GeckoCode::Code new_code; // TODO: support options + new_code.original_line = *lines_iter; ss >> std::hex >> new_code.address >> new_code.data; gcode.codes.push_back(new_code); } break; - } } @@ -100,10 +96,6 @@ void SaveGeckoCode(std::vector& lines, const GeckoCode& gcode) } lines.push_back(name); - - // save the description - if (gcode.description.size()) - lines.push_back(std::string("*") + gcode.description); // save all the code lines std::vector::const_iterator @@ -112,13 +104,17 @@ void SaveGeckoCode(std::vector& lines, const GeckoCode& gcode) for (; codes_iter!=codes_end; ++codes_iter) { //ss << std::hex << codes_iter->address << ' ' << codes_iter->data; - lines.push_back(StringFromFormat("%08X %08X", codes_iter->address, codes_iter->data)); + //lines.push_back(StringFromFormat("%08X %08X", codes_iter->address, codes_iter->data)); + lines.push_back(codes_iter->original_line); //ss.clear(); } - //lines.push_back("BLAH"); - - // TODO: save the options + // save the notes + std::vector::const_iterator + notes_iter = gcode.notes.begin(), + notes_end = gcode.notes.end(); + for (; notes_iter!=notes_end; ++notes_iter) + lines.push_back(std::string("*") + *notes_iter); } void SaveCodes(IniFile& inifile, const std::vector& gcodes) diff --git a/Source/Core/DolphinWX/Src/CheatsWindow.cpp b/Source/Core/DolphinWX/Src/CheatsWindow.cpp index 1001d36a5d..83e8fef1fc 100644 --- a/Source/Core/DolphinWX/Src/CheatsWindow.cpp +++ b/Source/Core/DolphinWX/Src/CheatsWindow.cpp @@ -34,7 +34,7 @@ extern std::vector arCodes; static wxCheatsWindow *g_cheat_window; wxCheatsWindow::wxCheatsWindow(wxWindow* const parent) - : wxFrame(parent, wxID_ANY, wxT("Action Replay"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE) + : wxFrame(parent, wxID_ANY, wxT("Cheats Manager"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE) { ::g_cheat_window = this; @@ -201,8 +201,8 @@ CheatSearchTab::CheatSearchTab(wxWindow* const parent) // filter types in the compare dropdown static const wxString searches[] = { wxT("Unknown"), - wxT("Not Equals"), - wxT("Equals"), + wxT("Not Equal"), + wxT("Equal"), wxT("Greater Than"), wxT("Less Than"), // TODO: Implement between search. diff --git a/Source/Core/DolphinWX/Src/GeckoCodeDiag.cpp b/Source/Core/DolphinWX/Src/GeckoCodeDiag.cpp index 23b6d38985..1b19d27436 100644 --- a/Source/Core/DolphinWX/Src/GeckoCodeDiag.cpp +++ b/Source/Core/DolphinWX/Src/GeckoCodeDiag.cpp @@ -1,13 +1,20 @@ #include "GeckoCodeDiag.h" +#ifdef _WIN32 +#define _WINSOCK2API_ +#endif +#include + +#include + #define _connect_macro_(b, f, c, s) (b)->Connect(wxID_ANY, (c), wxCommandEventHandler(f), (wxObject*)0, (wxEvtHandler*)s) namespace Gecko { static const wxString wxstr_name(wxT("Name: ")), - wxstr_description(wxT("Description: ")), + wxstr_notes(wxT("Notes: ")), wxstr_creator(wxT("Creator: ")); CodeConfigPanel::CodeConfigPanel(wxWindow* const parent) @@ -19,41 +26,40 @@ CodeConfigPanel::CodeConfigPanel(wxWindow* const parent) m_infobox.label_name = new wxStaticText(this, -1, wxstr_name); m_infobox.label_creator = new wxStaticText(this, -1, wxstr_creator); - m_infobox.label_description = new wxStaticText(this, -1, wxstr_description); + m_infobox.label_notes = new wxStaticText(this, -1, wxstr_notes); + m_infobox.textctrl_notes = new wxTextCtrl(this, -1, wxEmptyString, wxDefaultPosition, wxSize(64, -1), wxTE_MULTILINE | wxTE_READONLY); m_infobox.listbox_codes = new wxListBox(this, -1, wxDefaultPosition, wxSize(-1, 64)); // TODO: buttons to add/edit codes // sizers wxBoxSizer* const sizer_infobox = new wxBoxSizer(wxVERTICAL); - sizer_infobox->Add(m_infobox.label_name, 0, wxLEFT | wxBOTTOM, 5); - sizer_infobox->Add(m_infobox.label_creator, 0, wxLEFT | wxBOTTOM, 5); - sizer_infobox->Add(m_infobox.label_description, 0, wxLEFT | wxBOTTOM, 5); - sizer_infobox->Add(m_infobox.listbox_codes, 0, wxLEFT | wxBOTTOM, 5); + sizer_infobox->Add(m_infobox.label_name, 0, wxBOTTOM, 5); + sizer_infobox->Add(m_infobox.label_creator, 0, wxBOTTOM, 5); + sizer_infobox->Add(m_infobox.label_notes, 0, wxBOTTOM, 5); + sizer_infobox->Add(m_infobox.textctrl_notes, 0, wxBOTTOM | wxEXPAND, 5); + sizer_infobox->Add(m_infobox.listbox_codes, 1, wxEXPAND, 5); - //wxBoxSizer* const sizer_horz = new wxBoxSizer(wxHORIZONTAL); - //sizer_horz->Add(sizer_infobox, 1, 0); + // button sizer + wxBoxSizer* const sizer_buttons = new wxBoxSizer(wxVERTICAL); + wxButton* const btn_download = new wxButton(this, -1, wxT("Download Codes (WiiRD Database)"), wxDefaultPosition, wxSize(128, -1)); + _connect_macro_(btn_download, CodeConfigPanel::DownloadCodes, wxEVT_COMMAND_BUTTON_CLICKED, this); + sizer_buttons->Add(btn_download, 0, wxEXPAND); - // silly - //if (show_apply_button) - //{ - // wxButton* const btn_apply = new wxButton(this, -1, wxT("Apply Changes"), wxDefaultPosition, wxSize(128, -1)); - // _connect_macro_(btn_apply, CodeConfigPanel::ApplyChanges, wxEVT_COMMAND_BUTTON_CLICKED, this); - // sizer_horz->Add(btn_apply, 0, wxALIGN_RIGHT | wxALIGN_BOTTOM); - //} + // horizontal sizer + wxBoxSizer* const sizer_horz = new wxBoxSizer(wxHORIZONTAL); + sizer_horz->Add(sizer_infobox, 1, wxEXPAND); + sizer_horz->Add(sizer_buttons, 1, wxLEFT | wxALIGN_BOTTOM, 5); wxBoxSizer* const sizer_main = new wxBoxSizer(wxVERTICAL); sizer_main->Add(m_listbox_gcodes, 1, wxALL | wxEXPAND, 5); - sizer_main->Add(sizer_infobox, 0, wxALL | wxEXPAND, 5); + sizer_main->Add(sizer_horz, 0, wxALL | wxEXPAND, 5); SetSizerAndFit(sizer_main); } -void CodeConfigPanel::LoadCodes(const IniFile& inifile) +void CodeConfigPanel::UpdateCodeList() { - m_gcodes.clear(); - Gecko::LoadCodes(inifile, m_gcodes); - m_listbox_gcodes->Clear(); // add the codes to the listbox std::vector::const_iterator @@ -70,6 +76,16 @@ void CodeConfigPanel::LoadCodes(const IniFile& inifile) UpdateInfoBox(evt); } +void CodeConfigPanel::LoadCodes(const IniFile& inifile, const std::string& gameid) +{ + m_gameid = gameid; + + m_gcodes.clear(); + Gecko::LoadCodes(inifile, m_gcodes); + + UpdateCodeList(); +} + void CodeConfigPanel::ToggleCode(wxCommandEvent& evt) { const int sel = evt.GetInt(); // this right? @@ -85,7 +101,16 @@ void CodeConfigPanel::UpdateInfoBox(wxCommandEvent&) if (sel > -1) { m_infobox.label_name->SetLabel(wxstr_name + wxString::FromAscii(m_gcodes[sel].name.c_str())); - m_infobox.label_description->SetLabel(wxstr_description + wxString::FromAscii(m_gcodes[sel].description.c_str())); + + // notes textctrl + m_infobox.textctrl_notes->Clear(); + std::vector::const_iterator + notes_iter = m_gcodes[sel].notes.begin(), + notes_end = m_gcodes[sel].notes.end(); + for (; notes_iter!=notes_end; ++notes_iter) + m_infobox.textctrl_notes->AppendText(wxString::FromAscii(notes_iter->c_str())); + m_infobox.textctrl_notes->ScrollLines(-99); // silly + m_infobox.label_creator->SetLabel(wxstr_creator + wxString::FromAscii(m_gcodes[sel].creator.c_str())); // add codes to info listbox @@ -98,15 +123,135 @@ void CodeConfigPanel::UpdateInfoBox(wxCommandEvent&) else { m_infobox.label_name->SetLabel(wxstr_name); - m_infobox.label_description->SetLabel(wxstr_description); + m_infobox.textctrl_notes->Clear(); m_infobox.label_creator->SetLabel(wxstr_creator); } } -void CodeConfigPanel::ApplyChanges(wxCommandEvent&) +//void CodeConfigPanel::ApplyChanges(wxCommandEvent&) +//{ +// Gecko::SetActiveCodes(m_gcodes); +//} + +void CodeConfigPanel::DownloadCodes(wxCommandEvent&) { - Gecko::SetActiveCodes(m_gcodes); + if (m_gameid.empty()) + return; + + sf::Http::Request req; + req.SetURI("/txt.php?txt=" + m_gameid); + + sf::Http http; + http.SetHost("geckocodes.org"); + + const sf::Http::Response resp = http.SendRequest(req, 5.0f); + + if (sf::Http::Response::Ok == resp.GetStatus()) + { + // temp vector containing parsed codes + std::vector gcodes; + + // parse the codes + std::istringstream ss(resp.GetBody()); + + // debug + //PanicAlert("File size is %i bytes.", ss.str().size()); + + std::string line; + + // make sure the txt file is for this game + // eh w/e + //std::getline(ss, line); + //if (line != m_gameid) + // PanicAlert("Bad code file."); + + // seek past the header, get to the first code + std::getline(ss, line); + std::getline(ss, line); + std::getline(ss, line); + + int read_state = 0; + GeckoCode gcode; + + while ((std::getline(ss, line).good())) + { + // empty line + if (0 == line.size() || line == "\r" || line == "\n") // \r\n checks might not be needed + { + // add the code + if (gcode.codes.size()) + gcodes.push_back(gcode); + gcode = GeckoCode(); + read_state = 0; + continue; + } + + switch (read_state) + { + // read new code + case 0 : + gcode.name = line; // TODO: parse creator name in []s + read_state = 1; + break; + + // read code lines + case 1 : + { + std::istringstream ssline(line); + std::string addr, data; + ssline >> addr >> data; + ssline.seekg(0); + + // check if this line a code, silly, but the dumb txt file comment lines can start with valid hex chars :/ + if (8 == addr.length() && 8 == data.length()) + { + GeckoCode::Code new_code; + new_code.original_line = line; + ssline >> std::hex >> new_code.address >> new_code.data; + gcode.codes.push_back(new_code); + } + else + { + gcode.notes.push_back(line); + read_state = 2; // start reading comments + } + + } + break; + + // read comment lines + case 2 : + // append comment line + gcode.notes.push_back(line); + break; + + } + } + + // add the last code + if (gcode.codes.size()) + gcodes.push_back(gcode); + + if (gcodes.size()) + { + PanicAlert("Downloaded %i codes.", gcodes.size()); + + // append the codes to the code list + std::vector::const_iterator + gcodes_iter = gcodes.begin(), + gcodes_end = gcodes.end(); + for (; gcodes_iter!= gcodes_end; ++gcodes_iter) + m_gcodes.push_back(*gcodes_iter); + + // refresh the list + UpdateCodeList(); + } + else + PanicAlert("File contained no codes."); + } + else + PanicAlert("Failed to download codes."); } - } + diff --git a/Source/Core/DolphinWX/Src/GeckoCodeDiag.h b/Source/Core/DolphinWX/Src/GeckoCodeDiag.h index f0374f7efd..92328d5f43 100644 --- a/Source/Core/DolphinWX/Src/GeckoCodeDiag.h +++ b/Source/Core/DolphinWX/Src/GeckoCodeDiag.h @@ -17,22 +17,28 @@ public: CodeConfigPanel(wxWindow* const parent); - void LoadCodes(const IniFile& inifile); + void LoadCodes(const IniFile& inifile, const std::string& gameid = ""); const std::vector& GetCodes() const { return m_gcodes; } protected: void UpdateInfoBox(wxCommandEvent&); void ToggleCode(wxCommandEvent& evt); - void ApplyChanges(wxCommandEvent&); + void DownloadCodes(wxCommandEvent&); + //void ApplyChanges(wxCommandEvent&); + + void UpdateCodeList(); private: std::vector m_gcodes; + std::string m_gameid; + // wxwidgets stuff wxCheckListBox *m_listbox_gcodes; struct { - wxStaticText *label_name, *label_description, *label_creator; + wxStaticText *label_name, *label_notes, *label_creator; + wxTextCtrl *textctrl_notes; wxListBox *listbox_codes; } m_infobox; @@ -43,3 +49,4 @@ private: } #endif + diff --git a/Source/Core/DolphinWX/Src/ISOProperties.cpp b/Source/Core/DolphinWX/Src/ISOProperties.cpp index 999cabe7f7..6211c9e6b4 100644 --- a/Source/Core/DolphinWX/Src/ISOProperties.cpp +++ b/Source/Core/DolphinWX/Src/ISOProperties.cpp @@ -882,7 +882,7 @@ void CISOProperties::LoadGameConfig() PatchList_Load(); ActionReplayList_Load(); - m_geckocode_panel->LoadCodes(GameIni); + m_geckocode_panel->LoadCodes(GameIni, OpenISO->GetUniqueID()); } bool CISOProperties::SaveGameConfig()