diff --git a/Source/Core/Core/Core.vcproj b/Source/Core/Core/Core.vcproj
index c932f669c9..164cac1ec5 100644
--- a/Source/Core/Core/Core.vcproj
+++ b/Source/Core/Core/Core.vcproj
@@ -1359,6 +1359,26 @@
>
+
+
+
+
+
+
+
+
+
+
diff --git a/Source/Core/Core/Src/GeckoCode.cpp b/Source/Core/Core/Src/GeckoCode.cpp
new file mode 100644
index 0000000000..f265331db2
--- /dev/null
+++ b/Source/Core/Core/Src/GeckoCode.cpp
@@ -0,0 +1,852 @@
+
+#include "GeckoCode.h"
+
+#include "Thread.h"
+#include "HW/Memmap.h"
+
+#include "vector"
+
+namespace Gecko
+{
+
+enum
+{
+ // Code Types
+ CODETYPE_WRITE_FILL = 0x0,
+ CODETYPE_IF = 0x1,
+ CODETYPE_BA_PO_OPS = 0x2,
+ CODETYPE_FLOW_CONTROL = 0x3,
+ CODETYPE_REGISTER_OPS = 0x4,
+ CODETYPE_SPECIAL_IF = 0x5,
+ CODETYPE_ASM_SWITCH_RANGE = 0x6,
+ CODETYPE_END_CODES = 0x7,
+
+ // Data Types
+ DATATYPE_8BIT = 0x0,
+ DATATYPE_16BIT = 0x1,
+ DATATYPE_32BIT = 0x2,
+};
+
+// globals
+static u32 base_address = 0;
+static u32 pointer_address = 0;
+static u32 gecko_register[0x10] = {0};
+static struct
+{
+ u32 address;
+ u32 number;
+} block[0x10];
+
+// codes execute when counter is 0
+static int code_execution_counter = 0;
+
+// the currently active codes
+std::vector active_codes;
+
+// return true if code execution is on
+inline bool CodeExecution()
+{
+ return (0 == code_execution_counter);
+}
+
+u32 GeckoCode::Code::GetAddress() const
+{
+ return gcaddress + (use_po ? pointer_address : (base_address & 0xFE000000));
+}
+
+static Common::CriticalSection active_codes_lock;
+
+// currently running code
+static GeckoCode::Code current_code;
+
+// Functions for each code type
+bool RamWriteAndFill();
+bool RegularIf();
+bool BaPoOps();
+bool FlowControl();
+bool RegisterOps();
+bool SpecialIf();
+bool AsmSwitchRange();
+bool EndCodes();
+
+bool MathOperation(u32& ret, const u32 left, const u32 right, const u8 type);
+
+void SetActiveCodes(const std::vector& gcodes)
+{
+ active_codes_lock.Enter();
+
+ active_codes.clear();
+ // add enabled codes
+ std::vector::const_iterator
+ gcodes_iter = gcodes.begin(),
+ gcodes_end = gcodes.end();
+ for (; gcodes_iter!=gcodes_end; ++gcodes_iter)
+ if (gcodes_iter->enabled)
+ {
+ // TODO: apply modifiers
+ // TODO: don't need description or creator string, just takin up memory
+ active_codes.push_back(*gcodes_iter);
+ }
+
+ active_codes_lock.Leave();
+}
+
+bool RunGeckoCode(const GeckoCode& gecko_code)
+{
+ static bool (*code_type_funcs[])(void) =
+ { RamWriteAndFill, RegularIf, BaPoOps, FlowControl, RegisterOps, SpecialIf, AsmSwitchRange, EndCodes };
+
+ base_address = 0x80000000;
+ 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)
+ {
+ const GeckoCode::Code& code = *codes_iter;
+
+ current_code = code;
+
+ bool result = true;
+
+ switch (code.type)
+ {
+ // These codetypes run even if code_execution is off
+ case CODETYPE_IF :
+ case CODETYPE_FLOW_CONTROL :
+ case CODETYPE_SPECIAL_IF :
+ case CODETYPE_ASM_SWITCH_RANGE :
+ case CODETYPE_END_CODES :
+ result = code_type_funcs[code.type]();
+ break;
+
+ // The rest of the codetypes only run if code_execution is on
+ default :
+ if (CodeExecution())
+ result = code_type_funcs[code.type]();
+ break;
+ }
+
+ // code failed
+ 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.)"
+ , code.type, code.subtype, gecko_code.name.c_str());
+ return false;
+ }
+
+
+
+ }
+
+
+ return true;
+}
+
+bool RunActiveCodes()
+{
+ if (false == active_codes_lock.TryEnter())
+ return true;
+
+ std::vector::const_iterator
+ gcodes_iter = active_codes.begin(),
+ gcodes_end = active_codes.end();
+ for (; gcodes_iter!=gcodes_end; ++gcodes_iter)
+ {
+ RunGeckoCode(*gcodes_iter);
+ // we don't need to stop all codes if one fails, maybe
+ //if (false == RunGeckoCode(*gcodes_iter))
+ //return false;
+ }
+
+ active_codes_lock.Leave();
+ return true;
+}
+
+// CT0: Direct ram write/fill
+// NOT COMPLETE, last 2 subtypes not started
+bool RamWriteAndFill()
+{
+ const GeckoCode::Code& code = current_code;
+ u32 new_addr = code.GetAddress();
+ const u32& data = code.data;
+
+ u16 count = (data >> 16) + 1;
+
+ switch (code.subtype)
+ {
+ // CST0: 8bits Write & Fill
+ case 0x0 :
+ while (count--)
+ {
+ Memory::Write_U16((u16)data, new_addr);
+ ++new_addr;
+ }
+ break;
+
+ // CST1: 16bits Write & Fill
+ case 0x1 :
+ while (count--)
+ {
+ Memory::Write_U16((u16)data, new_addr);
+ new_addr += 2;
+ }
+ break;
+
+ // CST2: 32bits Write
+ case 0x2 :
+ Memory::Write_U32((u32)data, new_addr);
+ break;
+
+ // CST3: String Code
+ case 0x3 :
+ // TODO:
+ return false;
+ break;
+
+ // CST4: Serial Code
+ case 0x4 :
+ {
+ // TODO: complete
+ u32 new_data = data;
+
+
+ return false;
+ }
+ break;
+
+ // INVALID SUBTYPE
+ default :
+ return false;
+ break;
+ }
+
+ return true;
+}
+
+// CT1 Regular If codes (16/32 bits)
+// COMPLETE
+bool RegularIf()
+{
+ const GeckoCode::Code& code = current_code;
+
+ const bool is_endif = !!(code.address & 0x1);
+
+ // if code_execution is off and this is an endif, decrease the execution counter
+ if (false == CodeExecution())
+ {
+ if (is_endif)
+ --code_execution_counter;
+ }
+
+ bool result = false;
+
+ // if code_execution is on, execute the conditional
+ if (CodeExecution())
+ {
+ const u32 new_addr = code.GetAddress() & ~0x1;
+ const u32& data = code.data;
+
+ s32 read_value = 0;
+ s32 data_value = 0;
+
+ // 16bit vs 32bit
+ if (code.subtype & 0x4)
+ {
+ // 16bits
+ read_value = Memory::Read_U16(new_addr) & ~(data >> 16);
+ data_value = (data & 0xFFFF);
+ }
+ else
+ {
+ // 32bits
+ read_value = Memory::Read_U32(new_addr);
+ data_value = data;
+ }
+
+ switch (code.subtype & 0x3)
+ {
+ // CST0 : 32bits (endif, then) If equal
+ // CST4 : 16bits (endif, then) If equal
+ case 0x0 :
+ result = (read_value == data_value);
+ break;
+
+ // CST1 : 32bits (endif, then) If not equal
+ // CST5 : 16bits (endif, then) If not equal
+ case 0x1 :
+ result = (read_value != data_value);
+ break;
+
+ // CST2 : 32bits (endif, then) If greater
+ // CST6 : 16bits (endif, then) If greater
+ case 0x2 :
+ result = (read_value > data_value);
+ break;
+
+ // CST3 : 32bits (endif, then) If lower
+ // CST7 : 16bits (endif, then) If lower
+ case 0x3 :
+ result = (read_value < data_value);
+ break;
+ }
+ }
+
+ // if the conditional returned false, or it never ran because execution is off,
+ // increase the code execution counter
+ if (false == result)
+ ++code_execution_counter;
+
+ return true;
+}
+
+// CT2 Base Address/Pointer Operations
+// NOT COMPLETE, last 2 subtypes aren't done
+bool BaPoOps()
+{
+ const GeckoCode::Code& code = current_code;
+
+ // base_address vs pointer (ba vs po)
+ u32& change_address = (code.subtype & 0x4) ? pointer_address : base_address;
+
+ // 4STYZ00N XXXXXXXX
+ u32 new_data = code.data;
+
+ // append grN
+ if (code.z)
+ new_data += gecko_register[code.n];
+
+ // append ba or po (depending on last nibble's first bit)
+ if (code.y)
+ new_data += (code.use_po ? pointer_address : base_address);
+
+ // append to current value (not used in all subtypes, T will be 0 in those)
+ if (code.t)
+ new_data += change_address;
+
+ switch (code.subtype & 0x3)
+ {
+ // CST0 : Load into Base Address
+ // CST4 : Load into Pointer
+ case 0x0 :
+ change_address = Memory::Read_U32(new_data);
+ break;
+
+ // CST1 : Set Base Address to
+ // CST5 : Set Pointer to
+ case 0x1 :
+ change_address = new_data;
+ break;
+
+ // CST2 : Save Base Address to
+ // CST6 : Save Pointer to
+ case 0x2 :
+ Memory::Write_U32(change_address, new_data);
+ break;
+
+ // CST3 : Put next line of code location into the Base Address
+ // CST7 : Put grN's location into the Pointer
+
+ // conflicting documentation, one doc says CST7 is the same as CST3 but with po
+ case 0x3 :
+ // TODO:
+ return false; //
+ break;
+ }
+
+ return true;
+}
+
+// CT3 Repeat/Goto/Gosub/Return
+// NOT COMPLETE
+bool FlowControl()
+{
+ const GeckoCode::Code& code = current_code;
+
+ // only the return subtype runs when code execution is off
+ if (false == CodeExecution() && code.subtype != 0x2)
+ return true;
+
+ // not all of these are used in all subtypes
+ const u8 block_num = (u8)(code.data & 0xF);
+ const s16 code_offset = (s16)code.address;
+
+ switch (code.subtype)
+ {
+ // 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; //
+ break;
+
+ // CST1 : Execute Repeat
+ case 0x1 :
+ if (block[block_num].number)
+ {
+ --block[block_num].number;
+ // TODO: jump to code block
+ }
+ return false; //
+ break;
+
+ // CST2 : Return
+ case 0x2 :
+ if (((code.address >> 20) & 0xF) ^ (u32)CodeExecution())
+ // TODO: jump to block[block_num].number
+ return false; //
+ break;
+
+ // CST3 : Goto
+ case 0x3 :
+ // TODO:
+ return false; //
+ break;
+
+ // CST4 : Gosub
+ case 0x4 :
+ // TODO:
+ return false; //
+ break;
+
+ // INVALID SUBTYPE
+ default :
+ return false;
+ break;
+ }
+
+ return true;
+}
+
+// CT4 Gecko Register Operations
+// NOT COMPLETE, need to do memory copy 1,2
+bool RegisterOps()
+{
+ const GeckoCode::Code& code = current_code;
+
+ // 80TYZZZN XXXXXXXX
+
+ u32 new_data = code.data;
+
+ // append ba or po (depending on last nibble's first bit)
+ if (code.y)
+ new_data += (code.use_po ? pointer_address : base_address);
+
+ u32& geckreg = gecko_register[code.n];
+
+ switch (code.subtype)
+ {
+ // CST0 : Set Gecko Register to
+ case 0x0 :
+ // append to or set register
+ geckreg = new_data + (code.t ? geckreg : 0);
+ break;
+
+ // CST1 : Load into Gecko Register
+ case 0x1 :
+ switch (code.t)
+ {
+ // 8bit
+ case DATATYPE_8BIT :
+ geckreg = Memory::Read_U8(new_data);
+ break;
+
+ // 16bit
+ case DATATYPE_16BIT :
+ geckreg = Memory::Read_U16(new_data);
+ break;
+
+ // 32bit
+ case DATATYPE_32BIT :
+ geckreg = Memory::Read_U32(new_data);
+ break;
+
+ // INVALID DATATYPE
+ default :
+ return false;
+ break;
+ }
+ break;
+
+ // CST2 : Save Gecko Register to
+ case 0x2 :
+ switch (code.t)
+ {
+ // 8bit
+ case DATATYPE_8BIT :
+ for (u16 i = 0; i <= code.z; ++i)
+ Memory::Write_U8((u8)geckreg, new_data + i);
+ break;
+
+ // 16bit
+ case DATATYPE_16BIT :
+ for (u16 i = 0; i <= code.z; ++i)
+ Memory::Write_U16((u16)geckreg, new_data + (i << 1));
+ break;
+
+ // 32bit
+ case DATATYPE_32BIT :
+ for (u16 i = 0; i <= code.z; ++i)
+ Memory::Write_U32((u32)geckreg, new_data + (i << 2));
+ break;
+
+ // INVALID DATATYPE
+ default :
+ return false;
+ break;
+ }
+ break;
+
+ // CST3 : Gecko Register / Direct Value operations
+ // CST4 : Gecko Registers operations
+ case 0x3 :
+ case 0x4 :
+ {
+ // subtype 3 uses value in .data subtype 4 uses register
+ u32 right_val = (code.subtype & 0x1 ? code.data : gecko_register[code.data & 0xF]);
+ if (code.y & 0x2)
+ right_val = Memory::Read_U32(right_val);
+
+ u32 left_val = geckreg;
+ if (code.y & 0x1)
+ left_val = Memory::Read_U32(left_val);
+
+ if (false == MathOperation(geckreg, left_val, right_val, code.t))
+ return false;
+ }
+ break;
+
+ // CST5 : Memory Copy 1
+ case 0x5 :
+ // TODO:
+ return false; //
+ break;
+
+ // CST6 : Memory Copy 2
+ case 0x6 :
+ // TODO:
+ return false; //
+ break;
+
+ // INVALID SUBTYPE
+ default :
+ return false;
+ break;
+
+ }
+
+ return true;
+}
+
+// NOT COMPLETE, at least one op needs to be coded/fixed
+bool MathOperation(u32& ret, const u32 left, const u32 right, const u8 type)
+{
+ // ? = T :
+ switch (type)
+ {
+ // 0 : add (+)
+ case 0x0 :
+ ret = left + right;
+ break;
+
+ // 1 : mul (*)
+ case 0x1 :
+ ret = left * right;
+ break;
+
+ // 2 : or (|)
+ case 0x2 :
+ ret = left | right;
+ break;
+
+ // 3 : and (&)
+ case 0x3 :
+ ret = left & right;
+ break;
+
+ // 4 : xor (^)
+ case 0x4 :
+ ret = left ^ right;
+ break;
+
+ // 5 : slw (<<)
+ case 0x5 :
+ ret = left << right;
+ break;
+
+ // 6 : srw (>>)
+ case 0x6 :
+ ret = left >> right;
+ break;
+
+ // TODO: this one good?
+ // 7 : rol (rotate left)
+ case 0x7 :
+ ret = (left << right) | (left >> (8 - right));
+ break;
+
+ // 8 : asr (arithmetic shift right)
+ case 0x8 :
+ // TODO: wuts this
+ return false;
+ break;
+
+ // TODO: these float ops good?
+ // A : fadds (single float add)
+ case 0xA :
+ *(float*)&ret = *(float*)&left + *(float*)&right;
+ break;
+
+ // B : fmuls (single float mul)
+ case 0xB :
+ *(float*)&ret = *(float*)&left * *(float*)&right;
+ break;
+
+ // INVALID OPERATION TYPE
+ default :
+ return false;
+ break;
+ }
+
+ return true;
+}
+
+// CT5: Special If codes (16bits)
+// NOT COMPLETE, part 2 (counter stuff) not started
+bool SpecialIf()
+{
+ const GeckoCode::Code& code = current_code;
+
+ const bool is_endif = !!(code.address & 0x1);
+
+ // if code_execution is off and this is an endif, decrease the execution counter
+ if (false == CodeExecution())
+ {
+ if (is_endif)
+ --code_execution_counter;
+ }
+
+ bool result = false;
+
+ // 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
+
+ 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;
+ }
+ }
+ else
+ {
+ // CT5 Part2 : 16bits Counter check
+ // TODO:
+
+ const u16 z = (u16)(data >> 16);
+
+ 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;
+ }
+ }
+ }
+
+ // if the conditional returned false, or it never ran because execution is off, increase the code execution counter
+ if (false == result)
+ ++code_execution_counter;
+
+ return true;
+}
+
+// CT6 ASM Codes, On/Off switch and Address Range Check
+// NOT COMPLETE, hardly started
+// fix the logic flow in this one
+bool AsmSwitchRange()
+{
+ const GeckoCode::Code& code = current_code;
+
+ // only used for the last 2 subtypes
+ const bool is_endif = !!(code.address & 0x1);
+
+ // 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
+ if (false == CodeExecution())
+ {
+ if (code.subtype < 0x6)
+ return true;
+ else if (false == (1 == code_execution_counter && is_endif))
+ return true;
+ }
+
+ const u32& data = code.data;
+
+ switch (code.subtype)
+ {
+ // CST0 : Execute following ASM Code
+ case 0x0 :
+ // TODO:
+ return false; //
+ break;
+
+ // CST1 : Insert ASM code in the game
+ case 0x1 :
+ // TODO:
+ return false;
+ break;
+
+ // CST3 : Create a branch
+ case 0x3 :
+ // TODO:
+ return false; //
+ break;
+
+ // CST6 : On/Off switch
+ case 0x6 :
+ // TODO:
+ return false; //
+ break;
+
+ // CST7 : Address range check (If... code)
+ case 0x7 :
+ if (code_execution_counter)
+ {
+ const u32 addr = (code.use_po ? pointer_address : base_address);
+ if (addr >= (data & 0xFFFF0000) && addr <= (data << 16))
+ --code_execution_counter;
+ }
+ break;
+
+ // INVALID SUBTYPE
+ default :
+ return false;
+ break;
+ }
+
+ return true;
+}
+
+// CT7 End of codes, Endif (Else)
+// COMPLETE, maybe
+bool EndCodes()
+{
+ const GeckoCode::Code& code = current_code;
+ const u32& data = code.data;
+
+ const u32 x = (data & 0xFFFF0000);
+ const u32 y = (data << 16);
+
+ // these 2 do not happen in the "CST7 : End of Code", but in that subtype they will be 0
+ if (x)
+ base_address = x;
+ if (y)
+ pointer_address = y;
+
+ switch (code.subtype)
+ {
+ // 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
+ break;
+
+ // CST1 : Endif (+else)
+ case 0x1 :
+ {
+ // apply endifs
+ const u8 v = (u8)code.address;
+ if (code_execution_counter >= v)
+ code_execution_counter -= v;
+ else
+ {
+ // too many endifs
+ return false;
+ }
+
+ const bool is_else = !!(code.address & 0x00100000);
+ // apply else
+ if (is_else)
+ if (code_execution_counter <= 1)
+ code_execution_counter ^= 1;
+ }
+ break;
+
+ // CST7 : End of Code
+ case 0x7 :
+ // tell the code handler that there are no more codes in the code list
+ // TODO: should this always stop all codes, even if execution is off?
+ if (CodeExecution())
+ code_execution_counter = -1; // silly maybe
+ break;
+
+ // INVALID SUBTYPE
+ default :
+ return false;
+ break;
+ }
+
+ return true;
+}
+
+} // namespace Gecko
\ No newline at end of file
diff --git a/Source/Core/Core/Src/GeckoCode.h b/Source/Core/Core/Src/GeckoCode.h
new file mode 100644
index 0000000000..305560318b
--- /dev/null
+++ b/Source/Core/Core/Src/GeckoCode.h
@@ -0,0 +1,66 @@
+
+#ifndef __GECKOCODE_h__
+#define __GECKOCODE_h__
+
+#include "Common.h"
+
+#include
+
+namespace Gecko
+{
+
+ class GeckoCode
+ {
+ public:
+
+ GeckoCode() : enabled(false) {}
+
+ struct Code
+ {
+ union
+ {
+ u32 address;
+
+ struct
+ {
+ u32 gcaddress : 25;
+ u32 subtype: 3;
+ u32 use_po : 1;
+ u32 type: 3;
+ };
+
+ struct
+ {
+ u32 n : 4;
+ u32 z : 12;
+ u32 y : 4;
+ u32 t : 4;
+ //u32 s : 4;
+ //u32 : 4;
+ };// subsubtype;
+ };
+
+ union
+ {
+ u32 data;
+ //struct
+ //{
+ //
+ //};
+ };
+
+ u32 GetAddress() const;
+ };
+
+ std::vector codes;
+ std::string name, description, creator;
+
+ bool enabled;
+ };
+
+ void SetActiveCodes(const std::vector& gcodes);
+ bool RunActiveCodes();
+
+} // namespace Gecko
+
+#endif
\ No newline at end of file
diff --git a/Source/Core/Core/Src/GeckoCodeConfig.cpp b/Source/Core/Core/Src/GeckoCodeConfig.cpp
new file mode 100644
index 0000000000..a9c0822eac
--- /dev/null
+++ b/Source/Core/Core/Src/GeckoCodeConfig.cpp
@@ -0,0 +1,141 @@
+
+#include "GeckoCodeConfig.h"
+
+#include "StringUtil.h"
+
+#include
+#include
+#include
+
+#define GECKO_CODE_INI_SECTION "Gecko"
+
+namespace Gecko
+{
+
+void LoadCodes(const IniFile& inifile, std::vector& gcodes)
+{
+ std::vector lines;
+ inifile.GetLines(GECKO_CODE_INI_SECTION, lines);
+
+ GeckoCode gcode;
+
+ std::vector::const_iterator
+ lines_iter = lines.begin(),
+ lines_end = lines.end();
+ for (; lines_iter!=lines_end; ++lines_iter)
+ {
+ if (lines_iter->empty())
+ continue;
+
+ std::istringstream ss(*lines_iter);
+
+ switch ((*lines_iter)[0])
+ {
+
+ // enabled or disabled code
+ case '+' :
+ ss.seekg(1);
+ case '$' :
+ if (gcode.name.size())
+ gcodes.push_back(gcode);
+ gcode = GeckoCode();
+ gcode.enabled = (1 == ss.tellg()); // silly
+ ss.seekg(1, std::ios_base::cur);
+ // read the code name
+ std::getline(ss, gcode.name, '['); // stop at [ character (begining of contributer name)
+ gcode.name = StripSpaces(gcode.name);
+ // read the code creator name
+ std::getline(ss, gcode.creator, ']');
+ break;
+
+ // description
+ case '*':
+ ss.seekg(1);
+ std::getline(ss, gcode.description);
+ break;
+
+ // start of code option list
+ case ':':
+ // TODO:
+ 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
+ ss >> std::hex >> new_code.address >> new_code.data;
+ gcode.codes.push_back(new_code);
+ }
+ break;
+
+ }
+
+ }
+
+ // add the last code
+ if (gcode.name.size())
+ gcodes.push_back(gcode);
+}
+
+// used by the SaveGeckoCodes function
+void SaveGeckoCode(std::vector& lines, const GeckoCode& gcode)
+{
+ std::string name;
+
+ if (gcode.enabled)
+ name += '+';
+
+ // save the name
+ name += '$';
+ name += gcode.name;
+
+ // save the creator name
+ if (gcode.creator.size())
+ {
+ name += " [";
+ name += gcode.creator;
+ name += ']';
+ }
+
+ 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
+ codes_iter = gcode.codes.begin(),
+ codes_end = gcode.codes.end();
+ 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));
+ //ss.clear();
+ }
+
+ //lines.push_back("BLAH");
+
+ // TODO: save the options
+}
+
+void SaveCodes(IniFile& inifile, const std::vector& gcodes)
+{
+ std::vector lines;
+
+ std::vector::const_iterator
+ gcodes_iter = gcodes.begin(),
+ gcodes_end = gcodes.end();
+ for (; gcodes_iter!=gcodes_end; ++gcodes_iter)
+ {
+ SaveGeckoCode(lines, *gcodes_iter);
+ }
+
+ inifile.SetLines(GECKO_CODE_INI_SECTION, lines);
+}
+
+
+
+};
\ No newline at end of file
diff --git a/Source/Core/Core/Src/GeckoCodeConfig.h b/Source/Core/Core/Src/GeckoCodeConfig.h
new file mode 100644
index 0000000000..ab04fab8ba
--- /dev/null
+++ b/Source/Core/Core/Src/GeckoCodeConfig.h
@@ -0,0 +1,20 @@
+
+#ifndef __GECKOCODECONFIG_h__
+#define __GECKOCODECONFIG_h__
+
+#include "GeckoCode.h"
+
+#include "IniFile.h"
+
+namespace Gecko
+{
+
+void LoadCodes(const IniFile& inifile, std::vector& gcodes);
+void SaveCodes(IniFile& inifile, const std::vector& gcodes);
+
+
+
+
+};
+
+#endif
\ No newline at end of file
diff --git a/Source/Core/Core/Src/PatchEngine.cpp b/Source/Core/Core/Src/PatchEngine.cpp
index ca90c4491a..fb5f2056fa 100644
--- a/Source/Core/Core/Src/PatchEngine.cpp
+++ b/Source/Core/Core/Src/PatchEngine.cpp
@@ -34,6 +34,8 @@
#include "PatchEngine.h"
#include "HW/Memmap.h"
#include "ActionReplay.h"
+#include "GeckoCode.h";
+#include "GeckoCodeConfig.h";
#include "FileUtil.h"
using namespace Common;
@@ -154,6 +156,12 @@ void LoadPatches(const char *gameID)
if (ini.Load(filename.c_str())) {
LoadPatchSection("OnFrame", onFrame, ini);
ActionReplay::LoadCodes(ini, false);
+
+ // lil silly
+ std::vector gcodes;
+ Gecko::LoadCodes(ini, gcodes);
+ Gecko::SetActiveCodes(gcodes);
+
LoadSpeedhacks("Speedhacks", speedHacks, ini);
LoadDiscList("DiscList", discList, ini);
}
@@ -197,6 +205,8 @@ void ApplyFramePatches()
void ApplyARPatches()
{
ActionReplay::RunAllActive();
+ // w/e this can be changed later
+ Gecko::RunActiveCodes();
}
} // namespace
diff --git a/Source/Core/Core/Src/SConscript b/Source/Core/Core/Src/SConscript
index 00e8224a20..493f3c5cbc 100644
--- a/Source/Core/Core/Src/SConscript
+++ b/Source/Core/Core/Src/SConscript
@@ -6,6 +6,8 @@ import sys
files = [
"ActionReplay.cpp",
"ARDecrypt.cpp",
+ "GeckoCode.cpp",
+ "GeckoCodeConfig.cpp",
"ConfigManager.cpp",
"Console.cpp",
"Core.cpp",
diff --git a/Source/Core/DolphinWX/DolphinWX.vcproj b/Source/Core/DolphinWX/DolphinWX.vcproj
index 4880b09376..dad6f0f115 100644
--- a/Source/Core/DolphinWX/DolphinWX.vcproj
+++ b/Source/Core/DolphinWX/DolphinWX.vcproj
@@ -864,6 +864,14 @@
RelativePath=".\src\GameListCtrl.h"
>
+
+
+
+
diff --git a/Source/Core/DolphinWX/Src/CheatsWindow.cpp b/Source/Core/DolphinWX/Src/CheatsWindow.cpp
index 1fd0925654..759a42e71c 100644
--- a/Source/Core/DolphinWX/Src/CheatsWindow.cpp
+++ b/Source/Core/DolphinWX/Src/CheatsWindow.cpp
@@ -20,6 +20,7 @@
#include "ActionReplay.h"
#include "Core.h"
#include "ConfigManager.h"
+#include "VolumeHandler.h"
#include "ISOProperties.h"
#include "HW/Memmap.h"
@@ -43,6 +44,17 @@ wxCheatsWindow::wxCheatsWindow(wxWindow* const parent)
// Load Data
Load_ARCodes();
+ // Load Gecko Codes :/
+ {
+ const DiscIO::IVolume* const vol = VolumeHandler::GetVolume();
+ if (vol)
+ {
+ m_gameini_path = std::string(File::GetUserPath(D_GAMECONFIG_IDX)) + vol->GetUniqueID() + ".ini";
+ m_gameini.Load(m_gameini_path);
+ m_geckocode_panel->LoadCodes(m_gameini);
+ }
+ }
+
Center();
Show();
}
@@ -64,9 +76,6 @@ void wxCheatsWindow::Init_ChildControls()
m_Label_Codename = new wxStaticText(m_Tab_Cheats, wxID_ANY, _T("Name: "), wxDefaultPosition, wxDefaultSize);
m_GroupBox_Info = new wxStaticBox(m_Tab_Cheats, wxID_ANY, _T("Code Info"), wxDefaultPosition, wxDefaultSize);
-
- wxButton* const button_applycodes = new wxButton(m_Tab_Cheats, wxID_ANY, _T("Apply Changes"), wxDefaultPosition, wxDefaultSize);
- _connect_macro_(button_applycodes, wxCheatsWindow::OnEvent_ButtonUpdateCodes_Press, wxEVT_COMMAND_BUTTON_CLICKED, this);
m_Label_NumCodes = new wxStaticText(m_Tab_Cheats, wxID_ANY, _T("Number Of Codes: "), wxDefaultPosition, wxDefaultSize);
m_ListBox_CodesList = new wxListBox(m_Tab_Cheats, wxID_ANY, wxDefaultPosition, wxSize(120, 150), 0, 0, wxLB_HSCROLL);
@@ -76,13 +85,9 @@ void wxCheatsWindow::Init_ChildControls()
sGroupBoxInfo->Add(m_Label_NumCodes, 0, wxALL, 5);
sGroupBoxInfo->Add(m_ListBox_CodesList, 1, wxALL, 5);
- wxBoxSizer* sB1 = new wxBoxSizer(wxVERTICAL);
- sB1->Add(button_applycodes, 0, wxALL | wxEXPAND, 5);
- sB1->Add(sGroupBoxInfo, 1, wxALL, 5);
-
wxBoxSizer* sizer_tab_cheats = new wxBoxSizer(wxHORIZONTAL);
sizer_tab_cheats->Add(m_CheckListBox_CheatsList, 1, wxEXPAND | wxTOP | wxBOTTOM | wxLEFT, 10);
- sizer_tab_cheats->Add(sB1, 0, wxALIGN_LEFT | wxEXPAND | wxALL, 5);
+ sizer_tab_cheats->Add(sGroupBoxInfo, 0, wxALIGN_LEFT | wxEXPAND | wxALL, 5);
m_Tab_Cheats->SetSizerAndFit(sizer_tab_cheats);
@@ -112,14 +117,19 @@ void wxCheatsWindow::Init_ChildControls()
m_Tab_Log->SetSizerAndFit(sTabLog);
// Add Tabs to Notebook
- m_Notebook_Main->AddPage(m_Tab_Cheats, _T("Codes List"));
+ m_Notebook_Main->AddPage(m_Tab_Cheats, _T("AR Codes"));
+ m_geckocode_panel = new Gecko::CodeConfigPanel(m_Notebook_Main);
+ m_Notebook_Main->AddPage(m_geckocode_panel, wxT("Gecko Codes"));
m_Notebook_Main->AddPage(tab_cheat_search, _T("Cheat Search"));
m_Notebook_Main->AddPage(m_Tab_Log, _T("Logging"));
// Button Strip
+ wxButton* const button_apply = new wxButton(panel, wxID_ANY, _T("Apply"), wxDefaultPosition, wxDefaultSize);
+ _connect_macro_(button_apply, wxCheatsWindow::OnEvent_ApplyChanges_Press, wxEVT_COMMAND_BUTTON_CLICKED, this);
wxButton* const button_close = new wxButton(panel, wxID_ANY, _T("Close"), wxDefaultPosition, wxDefaultSize);
_connect_macro_(button_close, wxCheatsWindow::OnEvent_ButtonClose_Press, wxEVT_COMMAND_BUTTON_CLICKED, this);
wxBoxSizer* sButtons = new wxBoxSizer(wxHORIZONTAL);
+ sButtons->Add(button_apply, 1, wxRIGHT, 5);
sButtons->Add(button_close, 1, 0, 0);
wxBoxSizer* const sMain = new wxBoxSizer(wxVERTICAL);
@@ -280,12 +290,23 @@ void wxCheatsWindow::OnEvent_CheatsList_ItemToggled(wxCommandEvent& WXUNUSED (ev
}
}
-void wxCheatsWindow::OnEvent_ButtonUpdateCodes_Press(wxCommandEvent& WXUNUSED (event))
+void wxCheatsWindow::OnEvent_ApplyChanges_Press(wxCommandEvent& WXUNUSED (event))
{
+ // Appply AR Code changes
for (size_t i = 0; i < indexList.size(); i++)
{
ActionReplay::SetARCode_IsActive(m_CheckListBox_CheatsList->IsChecked(indexList[i].uiIndex), indexList[i].index);
}
+
+ // Apply Gecko Code changes
+ Gecko::SetActiveCodes(m_geckocode_panel->GetCodes());
+
+ // save gameini, with changed gecko codes
+ if (m_gameini_path.size())
+ {
+ Gecko::SaveCodes(m_gameini, m_geckocode_panel->GetCodes());
+ m_gameini.Save(m_gameini_path);
+ }
}
void wxCheatsWindow::OnEvent_ButtonUpdateLog_Press(wxCommandEvent& WXUNUSED (event))
diff --git a/Source/Core/DolphinWX/Src/CheatsWindow.h b/Source/Core/DolphinWX/Src/CheatsWindow.h
index 73b04e6dcb..a128d9edbe 100644
--- a/Source/Core/DolphinWX/Src/CheatsWindow.h
+++ b/Source/Core/DolphinWX/Src/CheatsWindow.h
@@ -36,6 +36,8 @@
#include "ActionReplay.h"
+#include "GeckoCodeDiag.h"
+
#include "Filesystem.h"
#include "IniFile.h"
@@ -143,6 +145,10 @@ class wxCheatsWindow : public wxFrame
std::vector indexList;
+ Gecko::CodeConfigPanel *m_geckocode_panel;
+ IniFile m_gameini;
+ std::string m_gameini_path;
+
void Init_ChildControls();
void Load_ARCodes();
@@ -156,8 +162,8 @@ class wxCheatsWindow : public wxFrame
void OnEvent_CheatsList_ItemSelected(wxCommandEvent& event);
void OnEvent_CheatsList_ItemToggled(wxCommandEvent& event);
- // $ Update Active Codes Button
- void OnEvent_ButtonUpdateCodes_Press(wxCommandEvent& event);
+ // $ Apply Changes Button
+ void OnEvent_ApplyChanges_Press(wxCommandEvent& event);
// $ Update Log Button
void OnEvent_ButtonUpdateLog_Press(wxCommandEvent& event);
diff --git a/Source/Core/DolphinWX/Src/FrameTools.cpp b/Source/Core/DolphinWX/Src/FrameTools.cpp
index de9efe9199..082e94ad59 100644
--- a/Source/Core/DolphinWX/Src/FrameTools.cpp
+++ b/Source/Core/DolphinWX/Src/FrameTools.cpp
@@ -186,7 +186,7 @@ void CFrame::CreateMenu()
toolsMenu->Append(IDM_LUA, _T("New &Lua Console"));
toolsMenu->Append(IDM_MEMCARD, _T("&Memcard Manager (GC)"));
toolsMenu->Append(IDM_IMPORTSAVE, _T("Wii Save Import"));
- toolsMenu->Append(IDM_CHEATS, _T("Action &Replay Manager"));
+ toolsMenu->Append(IDM_CHEATS, _T("&Cheats Manager"));
toolsMenu->Append(IDM_NETPLAY, _T("Start &NetPlay"));
diff --git a/Source/Core/DolphinWX/Src/GeckoCodeDiag.cpp b/Source/Core/DolphinWX/Src/GeckoCodeDiag.cpp
new file mode 100644
index 0000000000..888dc503b1
--- /dev/null
+++ b/Source/Core/DolphinWX/Src/GeckoCodeDiag.cpp
@@ -0,0 +1,112 @@
+
+#include "GeckoCodeDiag.h"
+
+#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_creator(wxT("Creator: "));
+
+CodeConfigPanel::CodeConfigPanel(wxWindow* const parent)
+ : wxPanel(parent, -1, wxDefaultPosition, wxDefaultSize)
+{
+ m_listbox_gcodes = new wxCheckListBox(this, -1, wxDefaultPosition, wxDefaultSize);
+ _connect_macro_(m_listbox_gcodes, CodeConfigPanel::UpdateInfoBox, wxEVT_COMMAND_LISTBOX_SELECTED, this);
+ _connect_macro_(m_listbox_gcodes, CodeConfigPanel::ToggleCode, wxEVT_COMMAND_CHECKLISTBOX_TOGGLED, this);
+
+ 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.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);
+
+ //wxBoxSizer* const sizer_horz = new wxBoxSizer(wxHORIZONTAL);
+ //sizer_horz->Add(sizer_infobox, 1, 0);
+
+ // 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);
+ //}
+
+ 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);
+
+ SetSizerAndFit(sizer_main);
+}
+
+void CodeConfigPanel::LoadCodes(const IniFile& inifile)
+{
+ m_gcodes.clear();
+ Gecko::LoadCodes(inifile, m_gcodes);
+
+ m_listbox_gcodes->Clear();
+ // add the codes to the listbox
+ std::vector::const_iterator
+ gcodes_iter = m_gcodes.begin(),
+ gcodes_end = m_gcodes.end();
+ for (; gcodes_iter!=gcodes_end; ++gcodes_iter)
+ {
+ m_listbox_gcodes->Append(wxString::FromAscii(gcodes_iter->name.c_str()));
+ if (gcodes_iter->enabled)
+ m_listbox_gcodes->Check(m_listbox_gcodes->GetCount()-1, true);
+ }
+
+ wxCommandEvent evt;
+ UpdateInfoBox(evt);
+}
+
+void CodeConfigPanel::ToggleCode(wxCommandEvent& evt)
+{
+ const int sel = evt.GetInt(); // this right?
+ if (sel > -1)
+ m_gcodes[sel].enabled = m_listbox_gcodes->IsChecked(sel);
+}
+
+void CodeConfigPanel::UpdateInfoBox(wxCommandEvent&)
+{
+ m_infobox.listbox_codes->Clear();
+ const int sel = m_listbox_gcodes->GetSelection();
+
+ 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()));
+ m_infobox.label_creator->SetLabel(wxstr_creator + wxString::FromAscii(m_gcodes[sel].creator.c_str()));
+
+ // add codes to info listbox
+ std::vector::const_iterator
+ codes_iter = m_gcodes[sel].codes.begin(),
+ codes_end = m_gcodes[sel].codes.end();
+ for (; codes_iter!=codes_end; ++codes_iter)
+ m_infobox.listbox_codes->Append(wxString::Format(wxT("%08X %08X"), codes_iter->address, codes_iter->data));
+ }
+ else
+ {
+ m_infobox.label_name->SetLabel(wxstr_name);
+ m_infobox.label_description->SetLabel(wxstr_description);
+ m_infobox.label_creator->SetLabel(wxstr_creator);
+ }
+}
+
+void CodeConfigPanel::ApplyChanges(wxCommandEvent&)
+{
+ Gecko::SetActiveCodes(m_gcodes);
+}
+
+
+}
\ No newline at end of file
diff --git a/Source/Core/DolphinWX/Src/GeckoCodeDiag.h b/Source/Core/DolphinWX/Src/GeckoCodeDiag.h
new file mode 100644
index 0000000000..44e4836a62
--- /dev/null
+++ b/Source/Core/DolphinWX/Src/GeckoCodeDiag.h
@@ -0,0 +1,45 @@
+
+#ifndef __GECKOCODEDIAG_h__
+#define __GECKOCODEDIAG_h__
+
+#include "GeckoCode.h"
+#include "GeckoCodeConfig.h"
+
+#include "wx/wx.h"
+
+namespace Gecko
+{
+
+
+class CodeConfigPanel : public wxPanel
+{
+public:
+ CodeConfigPanel(wxWindow* const parent);
+
+
+ void LoadCodes(const IniFile& inifile);
+ const std::vector& GetCodes() const { return m_gcodes; }
+
+protected:
+ void UpdateInfoBox(wxCommandEvent&);
+ void ToggleCode(wxCommandEvent& evt);
+ void ApplyChanges(wxCommandEvent&);
+
+private:
+ std::vector m_gcodes;
+
+ // wxwidgets stuff
+ wxCheckListBox *m_listbox_gcodes;
+ struct
+ {
+ wxStaticText *label_name, *label_description, *label_creator;
+ wxListBox *listbox_codes;
+ } m_infobox;
+
+};
+
+
+
+}
+
+#endif
\ No newline at end of file
diff --git a/Source/Core/DolphinWX/Src/ISOProperties.cpp b/Source/Core/DolphinWX/Src/ISOProperties.cpp
index b0ac8ea0e9..999cabe7f7 100644
--- a/Source/Core/DolphinWX/Src/ISOProperties.cpp
+++ b/Source/Core/DolphinWX/Src/ISOProperties.cpp
@@ -24,6 +24,7 @@
#include "ISOProperties.h"
#include "PatchAddEdit.h"
#include "ARCodeAddEdit.h"
+#include "GeckoCodeDiag.h"
#include "ConfigManager.h"
#include "StringUtil.h"
@@ -268,6 +269,8 @@ void CISOProperties::CreateGUIControls(bool IsWad)
m_Notebook->AddPage(m_PatchPage, _("Patches"));
m_CheatPage = new wxPanel(m_Notebook, ID_ARCODE_PAGE, wxDefaultPosition, wxDefaultSize);
m_Notebook->AddPage(m_CheatPage, _("AR Codes"));
+ m_geckocode_panel = new Gecko::CodeConfigPanel(m_Notebook);
+ m_Notebook->AddPage(m_geckocode_panel, wxT("Gecko Codes"));
m_Information = new wxPanel(m_Notebook, ID_INFORMATION, wxDefaultPosition, wxDefaultSize);
m_Notebook->AddPage(m_Information, _("Info"));
m_Filesystem = new wxPanel(m_Notebook, ID_FILESYSTEM, wxDefaultPosition, wxDefaultSize);
@@ -879,6 +882,7 @@ void CISOProperties::LoadGameConfig()
PatchList_Load();
ActionReplayList_Load();
+ m_geckocode_panel->LoadCodes(GameIni);
}
bool CISOProperties::SaveGameConfig()
@@ -966,6 +970,7 @@ bool CISOProperties::SaveGameConfig()
PatchList_Save();
ActionReplayList_Save();
+ Gecko::SaveCodes(GameIni, m_geckocode_panel->GetCodes());
return GameIni.Save(GameIniFile.c_str());
}
diff --git a/Source/Core/DolphinWX/Src/ISOProperties.h b/Source/Core/DolphinWX/Src/ISOProperties.h
index ca8680905c..18759ee443 100644
--- a/Source/Core/DolphinWX/Src/ISOProperties.h
+++ b/Source/Core/DolphinWX/Src/ISOProperties.h
@@ -35,6 +35,7 @@
#include "IniFile.h"
#include "PatchEngine.h"
#include "ActionReplay.h"
+#include "GeckoCodeDiag.h"
class CISOProperties : public wxDialog
{
@@ -145,6 +146,8 @@ class CISOProperties : public wxDialog
wxTreeItemId RootId;
wxImageList *m_iconList;
+ Gecko::CodeConfigPanel *m_geckocode_panel;
+
enum
{
ID_CLOSE = 1000,
diff --git a/Source/Core/DolphinWX/Src/SConscript b/Source/Core/DolphinWX/Src/SConscript
index 5cafa52c17..34f496f743 100644
--- a/Source/Core/DolphinWX/Src/SConscript
+++ b/Source/Core/DolphinWX/Src/SConscript
@@ -23,6 +23,7 @@ if env['HAVE_WX']:
files += [
'AboutDolphin.cpp',
'ARCodeAddEdit.cpp',
+ 'GeckoCodeDiag.cpp',
'ConfigMain.cpp',
'Frame.cpp',
'FrameAui.cpp',