diff --git a/CHANGES b/CHANGES index 3dc9719de..7e57ed1a8 100644 --- a/CHANGES +++ b/CHANGES @@ -92,6 +92,7 @@ Misc: - Core: Add savedataUpdated callback - Core: Add shutdown callback - Core: Rework thread state synchronization + - Core: Improve support for ROM patch cheats, supporting disabling overlapping patches - GB: Allow pausing event loop while CPU is blocked - GB: Add support for sleep and shutdown callbacks - GB I/O: Implement preliminary support for PCM12/PCM34 (closes mgba.io/i/1468) diff --git a/include/mgba/core/cheats.h b/include/mgba/core/cheats.h index d05ac8e5f..4d7bd909c 100644 --- a/include/mgba/core/cheats.h +++ b/include/mgba/core/cheats.h @@ -12,6 +12,7 @@ CXX_GUARD_START #include #include +#include #include enum mCheatType { @@ -44,9 +45,20 @@ struct mCheat { int32_t operandOffset; }; +struct mCheatPatch { + uint32_t address; + int segment; + uint32_t value; + int width; + bool applied; + uint32_t checkValue; + bool check; +}; + mLOG_DECLARE_CATEGORY(CHEATS); DECLARE_VECTOR(mCheatList, struct mCheat); +DECLARE_VECTOR(mCheatPatchList, struct mCheatPatch); struct mCheatDevice; struct mCheatSet { @@ -66,6 +78,7 @@ struct mCheatSet { char* name; bool enabled; + struct mCheatPatchList romPatches; struct StringList lines; }; @@ -78,6 +91,7 @@ struct mCheatDevice { struct mCheatSet* (*createSet)(struct mCheatDevice*, const char* name); struct mCheatSets cheats; + struct Table unpatchedMemory; bool autosave; bool buttonDown; }; diff --git a/include/mgba/internal/gb/cheats.h b/include/mgba/internal/gb/cheats.h index 0eb7d3b3c..7f74cb968 100644 --- a/include/mgba/internal/gb/cheats.h +++ b/include/mgba/internal/gb/cheats.h @@ -11,7 +11,6 @@ CXX_GUARD_START #include -#include enum GBCheatType { GB_CHEAT_AUTODETECT, @@ -20,22 +19,6 @@ enum GBCheatType { GB_CHEAT_VBA }; -struct GBCheatPatch { - uint16_t address; - int8_t newValue; - int8_t oldValue; - int segment; - bool applied; - bool checkByte; -}; - -DECLARE_VECTOR(GBCheatPatchList, struct GBCheatPatch); - -struct GBCheatSet { - struct mCheatSet d; - struct GBCheatPatchList romPatches; -}; - struct mCheatDevice* GBCheatDeviceCreate(void); CXX_GUARD_END diff --git a/include/mgba/internal/gba/cheats.h b/include/mgba/internal/gba/cheats.h index 575d103ca..69e589547 100644 --- a/include/mgba/internal/gba/cheats.h +++ b/include/mgba/internal/gba/cheats.h @@ -134,23 +134,14 @@ struct GBACheatHook { size_t reentries; }; -struct GBACheatPatch { - uint32_t address; - int16_t newValue; - int16_t oldValue; - bool applied; -}; - DECLARE_VECTOR(GBACheatPatchList, struct GBACheatPatch); struct GBACheatSet { struct mCheatSet d; struct GBACheatHook* hook; - struct GBACheatPatchList romPatches; - size_t incompleteCheat; - struct GBACheatPatch* incompletePatch; + struct mCheatPatch* incompletePatch; size_t currentBlock; int gsaVersion; diff --git a/src/core/cheats.c b/src/core/cheats.c index 015144022..62290fa00 100644 --- a/src/core/cheats.c +++ b/src/core/cheats.c @@ -18,6 +18,33 @@ mLOG_DEFINE_CATEGORY(CHEATS, "Cheats", "core.cheats"); DEFINE_VECTOR(mCheatList, struct mCheat); DEFINE_VECTOR(mCheatSets, struct mCheatSet*); +DEFINE_VECTOR(mCheatPatchList, struct mCheatPatch); + +struct mCheatPatchedMem { + uint32_t originalValue; + int refs; + bool dirty; +}; + +static uint32_t _patchMakeKey(struct mCheatPatch* patch) { + // NB: This assumes patches have only one valid size per platform + uint32_t patchKey = patch->address; + switch (patch->width) { + case 2: + patchKey >>= 1; + break; + case 4: + patchKey >>= 2; + break; + default: + break; + } + // TODO: More than one segment + if (patch->segment > 0) { + patchKey |= patch->segment << 16; + } + return patchKey; +} static int32_t _readMem(struct mCore* core, uint32_t address, int width) { switch (width) { @@ -31,6 +58,18 @@ static int32_t _readMem(struct mCore* core, uint32_t address, int width) { return 0; } +static int32_t _readMemSegment(struct mCore* core, uint32_t address, int segment, int width) { + switch (width) { + case 1: + return core->rawRead8(core, address, segment); + case 2: + return core->rawRead16(core, address, segment); + case 4: + return core->rawRead32(core, address, segment); + } + return 0; +} + static void _writeMem(struct mCore* core, uint32_t address, int width, int32_t value) { switch (width) { case 1: @@ -45,6 +84,83 @@ static void _writeMem(struct mCore* core, uint32_t address, int width, int32_t v } } +static void _patchMem(struct mCore* core, uint32_t address, int segment, int width, int32_t value) { + switch (width) { + case 1: + core->rawWrite8(core, address, segment, value); + break; + case 2: + core->rawWrite16(core, address, segment, value); + break; + case 4: + core->rawWrite32(core, address, segment, value); + break; + } +} + +static void _patchROM(struct mCheatDevice* device, struct mCheatSet* cheats) { + if (!device->p) { + return; + } + size_t i; + for (i = 0; i < mCheatPatchListSize(&cheats->romPatches); ++i) { + struct mCheatPatch* patch = mCheatPatchListGetPointer(&cheats->romPatches, i); + int segment = -1; + if (patch->check && patch->segment < 0) { + int maxSegment = 0; + for (segment = 0; segment < maxSegment; ++segment) { + uint32_t value = _readMemSegment(device->p, patch->address, segment, patch->width); + if (value == patch->checkValue) { + break; + } + } + if (segment == maxSegment) { + continue; + } + } + patch->segment = segment; + + uint32_t patchKey = _patchMakeKey(patch); + struct mCheatPatchedMem* patchData = TableLookup(&device->unpatchedMemory, patchKey); + if (!patchData) { + patchData = malloc(sizeof(*patchData)); + patchData->originalValue = _readMemSegment(device->p, patch->address, segment, patch->width); + patchData->refs = 1; + patchData->dirty = false; + TableInsert(&device->unpatchedMemory, patchKey, patchData); + } else if (!patch->applied) { + ++patchData->refs; + patchData->dirty = true; + } else if (!patchData->dirty) { + continue; + } + _patchMem(device->p, patch->address, segment, patch->width, patch->value); + patch->applied = true; + } +} + +static void _unpatchROM(struct mCheatDevice* device, struct mCheatSet* cheats) { + if (!device->p) { + return; + } + size_t i; + for (i = 0; i < mCheatPatchListSize(&cheats->romPatches); ++i) { + struct mCheatPatch* patch = mCheatPatchListGetPointer(&cheats->romPatches, i); + if (!patch->applied) { + continue; + } + uint32_t patchKey = _patchMakeKey(patch); + struct mCheatPatchedMem* patchData = TableLookup(&device->unpatchedMemory, patchKey); + --patchData->refs; + patchData->dirty = true; + if (patchData->refs <= 0) { + _patchMem(device->p, patch->address, patch->segment, patch->width, patchData->originalValue); + TableRemove(&device->unpatchedMemory, patchKey); + } + patch->applied = false; + } +} + static void mCheatDeviceInit(void*, struct mCPUComponent*); static void mCheatDeviceDeinit(struct mCPUComponent*); @@ -55,11 +171,13 @@ void mCheatDeviceCreate(struct mCheatDevice* device) { device->autosave = false; device->buttonDown = false; mCheatSetsInit(&device->cheats, 4); + TableInit(&device->unpatchedMemory, 4, free); } void mCheatDeviceDestroy(struct mCheatDevice* device) { mCheatDeviceClear(device); mCheatSetsDeinit(&device->cheats); + TableDeinit(&device->unpatchedMemory); free(device); } @@ -75,6 +193,7 @@ void mCheatDeviceClear(struct mCheatDevice* device) { void mCheatSetInit(struct mCheatSet* set, const char* name) { mCheatListInit(&set->list, 4); StringListInit(&set->lines, 4); + mCheatPatchListInit(&set->romPatches, 4); if (name) { set->name = strdup(name); } else { @@ -93,7 +212,10 @@ void mCheatSetDeinit(struct mCheatSet* set) { free(set->name); } StringListDeinit(&set->lines); - set->deinit(set); + mCheatPatchListDeinit(&set->romPatches); + if (set->deinit) { + set->deinit(set); + } free(set); } @@ -117,7 +239,9 @@ bool mCheatAddLine(struct mCheatSet* set, const char* line, int type) { void mCheatAddSet(struct mCheatDevice* device, struct mCheatSet* cheats) { *mCheatSetsAppend(&device->cheats) = cheats; - cheats->add(cheats, device); + if (cheats->add) { + cheats->add(cheats, device); + } } void mCheatRemoveSet(struct mCheatDevice* device, struct mCheatSet* cheats) { @@ -131,7 +255,9 @@ void mCheatRemoveSet(struct mCheatDevice* device, struct mCheatSet* cheats) { return; } mCheatSetsShift(&device->cheats, i, 1); - cheats->remove(cheats, device); + if (cheats->remove) { + cheats->remove(cheats, device); + } } bool mCheatParseFile(struct mCheatDevice* device, struct VFile* vf) { @@ -503,8 +629,14 @@ void mCheatAutosave(struct mCheatDevice* device) { #endif void mCheatRefresh(struct mCheatDevice* device, struct mCheatSet* cheats) { - cheats->refresh(cheats, device); + if (cheats->enabled) { + _patchROM(device, cheats); + } + if (cheats->refresh) { + cheats->refresh(cheats, device); + } if (!cheats->enabled) { + _unpatchROM(device, cheats); return; } diff --git a/src/gb/cheats.c b/src/gb/cheats.c index 5c202782c..fa6a5aa7f 100644 --- a/src/gb/cheats.c +++ b/src/gb/cheats.c @@ -10,58 +10,6 @@ #include #include -DEFINE_VECTOR(GBCheatPatchList, struct GBCheatPatch); - -static void _patchROM(struct mCheatDevice* device, struct GBCheatSet* cheats) { - if (!device->p) { - return; - } - size_t i; - for (i = 0; i < GBCheatPatchListSize(&cheats->romPatches); ++i) { - struct GBCheatPatch* patch = GBCheatPatchListGetPointer(&cheats->romPatches, i); - if (patch->applied) { - continue; - } - int segment = 0; - if (patch->checkByte) { - struct GB* gb = device->p->board; - int maxSegment = (gb->memory.romSize + GB_SIZE_CART_BANK0 - 1) / GB_SIZE_CART_BANK0; - for (; segment < maxSegment; ++segment) { - int8_t value = GBView8(device->p->cpu, patch->address, segment); - if (value == patch->oldValue) { - break; - } - } - if (segment == maxSegment) { - continue; - } - } - // TODO: More than one segment - GBPatch8(device->p->cpu, patch->address, patch->newValue, &patch->oldValue, segment); - patch->applied = true; - patch->segment = segment; - } -} - -static void _unpatchROM(struct mCheatDevice* device, struct GBCheatSet* cheats) { - if (!device->p) { - return; - } - size_t i; - for (i = 0; i < GBCheatPatchListSize(&cheats->romPatches); ++i) { - struct GBCheatPatch* patch = GBCheatPatchListGetPointer(&cheats->romPatches, i); - if (!patch->applied) { - continue; - } - GBPatch8(device->p->cpu, patch->address, patch->oldValue, &patch->newValue, patch->segment); - patch->applied = false; - } -} - -static void GBCheatSetDeinit(struct mCheatSet* set); -static void GBCheatAddSet(struct mCheatSet* cheats, struct mCheatDevice* device); -static void GBCheatRemoveSet(struct mCheatSet* cheats, struct mCheatDevice* device); -static void GBCheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device); static void GBCheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet); static void GBCheatParseDirectives(struct mCheatSet* set, const struct StringList* directives); static void GBCheatDumpDirectives(struct mCheatSet* set, struct StringList* directives); @@ -69,23 +17,21 @@ static bool GBCheatAddLine(struct mCheatSet*, const char* line, int type); static struct mCheatSet* GBCheatSetCreate(struct mCheatDevice* device, const char* name) { UNUSED(device); - struct GBCheatSet* set = malloc(sizeof(*set)); - mCheatSetInit(&set->d, name); + struct mCheatSet* set = malloc(sizeof(*set)); + mCheatSetInit(set, name); - GBCheatPatchListInit(&set->romPatches, 0); + set->deinit = NULL; + set->add = NULL; + set->remove = NULL; - set->d.deinit = GBCheatSetDeinit; - set->d.add = GBCheatAddSet; - set->d.remove = GBCheatRemoveSet; + set->addLine = GBCheatAddLine; + set->copyProperties = GBCheatSetCopyProperties; - set->d.addLine = GBCheatAddLine; - set->d.copyProperties = GBCheatSetCopyProperties; + set->parseDirectives = GBCheatParseDirectives; + set->dumpDirectives = GBCheatDumpDirectives; - set->d.parseDirectives = GBCheatParseDirectives; - set->d.dumpDirectives = GBCheatDumpDirectives; - - set->d.refresh = GBCheatRefresh; - return &set->d; + set->refresh = NULL; + return set; } struct mCheatDevice* GBCheatDeviceCreate(void) { @@ -95,23 +41,8 @@ struct mCheatDevice* GBCheatDeviceCreate(void) { return device; } -static void GBCheatSetDeinit(struct mCheatSet* set) { - struct GBCheatSet* gbset = (struct GBCheatSet*) set; - GBCheatPatchListDeinit(&gbset->romPatches); -} - -static void GBCheatAddSet(struct mCheatSet* cheats, struct mCheatDevice* device) { - struct GBCheatSet* gbset = (struct GBCheatSet*) cheats; - _patchROM(device, gbset); -} - -static void GBCheatRemoveSet(struct mCheatSet* cheats, struct mCheatDevice* device) { - struct GBCheatSet* gbset = (struct GBCheatSet*) cheats; - _unpatchROM(device, gbset); -} - -static bool GBCheatAddCodebreaker(struct GBCheatSet* cheats, uint16_t address, uint8_t data) { - struct mCheat* cheat = mCheatListAppend(&cheats->d.list); +static bool GBCheatAddCodebreaker(struct mCheatSet* cheats, uint16_t address, uint8_t data) { + struct mCheat* cheat = mCheatListAppend(&cheats->list); cheat->type = CHEAT_ASSIGN; cheat->width = 1; cheat->address = address; @@ -121,11 +52,11 @@ static bool GBCheatAddCodebreaker(struct GBCheatSet* cheats, uint16_t address, u return true; } -static bool GBCheatAddGameShark(struct GBCheatSet* cheats, uint32_t op) { +static bool GBCheatAddGameShark(struct mCheatSet* cheats, uint32_t op) { return GBCheatAddCodebreaker(cheats, ((op & 0xFF) << 8) | ((op >> 8) & 0xFF), (op >> 16) & 0xFF); } -static bool GBCheatAddGameSharkLine(struct GBCheatSet* cheats, const char* line) { +static bool GBCheatAddGameSharkLine(struct mCheatSet* cheats, const char* line) { uint32_t op; if (!hex32(line, &op)) { return false; @@ -133,7 +64,7 @@ static bool GBCheatAddGameSharkLine(struct GBCheatSet* cheats, const char* line) return GBCheatAddGameShark(cheats, op); } -static bool GBCheatAddGameGenieLine(struct GBCheatSet* cheats, const char* line) { +static bool GBCheatAddGameGenieLine(struct mCheatSet* cheats, const char* line) { uint16_t op1; uint16_t op2; uint16_t op3 = 0x1000; @@ -156,24 +87,26 @@ static bool GBCheatAddGameGenieLine(struct GBCheatSet* cheats, const char* line) uint16_t address = (op1 & 0xF) << 8; address |= (op2 >> 4) & 0xFF; address |= ((op2 & 0xF) ^ 0xF) << 12; - struct GBCheatPatch* patch = GBCheatPatchListAppend(&cheats->romPatches); + struct mCheatPatch* patch = mCheatPatchListAppend(&cheats->romPatches); patch->address = address; - patch->newValue = op1 >> 4; + patch->value = op1 >> 4; patch->applied = false; + patch->width = 1; + patch->segment = -1; if (op3 < 0x1000) { uint32_t value = ((op3 & 0xF00) << 20) | (op3 & 0xF); value = ROR(value, 2); value |= value >> 24; value ^= 0xBA; - patch->oldValue = value; - patch->checkByte = true; + patch->checkValue = value; + patch->check = true; } else { - patch->checkByte = false; + patch->check = false; } return true; } -static bool GBCheatAddVBALine(struct GBCheatSet* cheats, const char* line) { +static bool GBCheatAddVBALine(struct mCheatSet* cheats, const char* line) { uint16_t address; uint8_t value; const char* lineNext = hex16(line, &address); @@ -183,7 +116,7 @@ static bool GBCheatAddVBALine(struct GBCheatSet* cheats, const char* line) { if (!hex8(line, &value)) { return false; } - struct mCheat* cheat = mCheatListAppend(&cheats->d.list); + struct mCheat* cheat = mCheatListAppend(&cheats->list); cheat->type = CHEAT_ASSIGN; cheat->width = 1; cheat->address = address; @@ -193,8 +126,7 @@ static bool GBCheatAddVBALine(struct GBCheatSet* cheats, const char* line) { return true; } -bool GBCheatAddLine(struct mCheatSet* set, const char* line, int type) { - struct GBCheatSet* cheats = (struct GBCheatSet*) set; +bool GBCheatAddLine(struct mCheatSet* cheats, const char* line, int type) { switch (type) { case GB_CHEAT_AUTODETECT: break; @@ -242,15 +174,6 @@ bool GBCheatAddLine(struct mCheatSet* set, const char* line, int type) { } } -static void GBCheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device) { - struct GBCheatSet* gbset = (struct GBCheatSet*) cheats; - if (cheats->enabled) { - _patchROM(device, gbset); - } else { - _unpatchROM(device, gbset); - } -} - static void GBCheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet) { UNUSED(set); UNUSED(oldSet); diff --git a/src/gba/cheats.c b/src/gba/cheats.c index fc396bacf..ec92bc3c2 100644 --- a/src/gba/cheats.c +++ b/src/gba/cheats.c @@ -13,8 +13,6 @@ #define MAX_LINE_LENGTH 128 -DEFINE_VECTOR(GBACheatPatchList, struct GBACheatPatch); - static void _addBreakpoint(struct mCheatDevice* device, struct GBACheatSet* cheats) { if (!device->p || !cheats->hook) { return; @@ -37,36 +35,6 @@ static void _removeBreakpoint(struct mCheatDevice* device, struct GBACheatSet* c GBAClearBreakpoint(device->p->board, cheats->hook->address, cheats->hook->mode, cheats->hook->patchedOpcode); } -static void _patchROM(struct mCheatDevice* device, struct GBACheatSet* cheats) { - if (!device->p) { - return; - } - size_t i; - for (i = 0; i < GBACheatPatchListSize(&cheats->romPatches); ++i) { - struct GBACheatPatch* patch = GBACheatPatchListGetPointer(&cheats->romPatches, i); - if (patch->applied) { - continue; - } - GBAPatch16(device->p->cpu, patch->address, patch->newValue, &patch->oldValue); - patch->applied = true; - } -} - -static void _unpatchROM(struct mCheatDevice* device, struct GBACheatSet* cheats) { - if (!device->p) { - return; - } - size_t i; - for (i = 0; i < GBACheatPatchListSize(&cheats->romPatches); ++i) { - struct GBACheatPatch* patch = GBACheatPatchListGetPointer(&cheats->romPatches, i); - if (!patch->applied) { - continue; - } - GBAPatch16(device->p->cpu, patch->address, patch->oldValue, NULL); - patch->applied = false; - } -} - static void GBACheatSetDeinit(struct mCheatSet* set); static void GBACheatAddSet(struct mCheatSet* cheats, struct mCheatDevice* device); static void GBACheatRemoveSet(struct mCheatSet* cheats, struct mCheatDevice* device); @@ -101,7 +69,6 @@ static struct mCheatSet* GBACheatSetCreate(struct mCheatDevice* device, const ch set->d.refresh = GBACheatRefresh; - GBACheatPatchListInit(&set->romPatches, 4); return &set->d; } @@ -120,18 +87,15 @@ static void GBACheatSetDeinit(struct mCheatSet* set) { free(gbaset->hook); } } - GBACheatPatchListDeinit(&gbaset->romPatches); } static void GBACheatAddSet(struct mCheatSet* cheats, struct mCheatDevice* device) { struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats; _addBreakpoint(device, gbaset); - _patchROM(device, gbaset); } static void GBACheatRemoveSet(struct mCheatSet* cheats, struct mCheatDevice* device) { struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats; - _unpatchROM(device, gbaset); _removeBreakpoint(device, gbaset); } @@ -276,13 +240,8 @@ bool GBACheatAddLine(struct mCheatSet* set, const char* line, int type) { static void GBACheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device) { struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats; - if (cheats->enabled) { - _patchROM(device, gbaset); - if (gbaset->hook && !gbaset->hook->reentries) { - _addBreakpoint(device, gbaset); - } - } else { - _unpatchROM(device, gbaset); + if (cheats->enabled && gbaset->hook && !gbaset->hook->reentries) { + _addBreakpoint(device, gbaset); } } diff --git a/src/gba/cheats/gameshark.c b/src/gba/cheats/gameshark.c index ba49cec3d..88f48ab49 100644 --- a/src/gba/cheats/gameshark.c +++ b/src/gba/cheats/gameshark.c @@ -93,7 +93,7 @@ void GBACheatSetGameSharkVersion(struct GBACheatSet* cheats, enum GBACheatGameSh bool GBACheatAddGameSharkRaw(struct GBACheatSet* cheats, uint32_t op1, uint32_t op2) { enum GBAGameSharkType type = op1 >> 28; struct mCheat* cheat = 0; - struct GBACheatPatch* romPatch; + struct mCheatPatch* romPatch; if (cheats->incompleteCheat != COMPLETE) { struct mCheat* incompleteCheat = mCheatListGetPointer(&cheats->d.list, cheats->incompleteCheat); @@ -149,10 +149,12 @@ bool GBACheatAddGameSharkRaw(struct GBACheatSet* cheats, uint32_t op1, uint32_t cheats->incompleteCheat = mCheatListIndex(&cheats->d.list, cheat); break; case GSA_PATCH: - romPatch = GBACheatPatchListAppend(&cheats->romPatches); + romPatch = mCheatPatchListAppend(&cheats->d.romPatches); romPatch->address = BASE_CART0 | ((op1 & 0xFFFFFF) << 1); - romPatch->newValue = op2; + romPatch->value = op2; romPatch->applied = false; + romPatch->width = 2; + romPatch->check = false; return true; case GSA_BUTTON: switch (op1 & 0x00F00000) { diff --git a/src/gba/cheats/parv3.c b/src/gba/cheats/parv3.c index 4f042f1ed..1a82f084c 100644 --- a/src/gba/cheats/parv3.c +++ b/src/gba/cheats/parv3.c @@ -230,9 +230,11 @@ static bool _addPAR3Special(struct GBACheatSet* cheats, uint32_t op2) { break; } if (romPatch >= 0) { - struct GBACheatPatch* patch = GBACheatPatchListAppend(&cheats->romPatches); + struct mCheatPatch* patch = mCheatPatchListAppend(&cheats->d.romPatches); patch->address = BASE_CART0 | ((op2 & 0xFFFFFF) << 1); patch->applied = false; + patch->check = false; + patch->width = 2; cheats->incompletePatch = patch; } return true; @@ -240,8 +242,8 @@ static bool _addPAR3Special(struct GBACheatSet* cheats, uint32_t op2) { bool GBACheatAddProActionReplayRaw(struct GBACheatSet* cheats, uint32_t op1, uint32_t op2) { if (cheats->incompletePatch) { - cheats->incompletePatch->newValue = op1; - cheats->incompletePatch = 0; + cheats->incompletePatch->value = op1; + cheats->incompletePatch = NULL; return true; } if (cheats->incompleteCheat != COMPLETE) {