GBA Unlicensed Carts: Prevent multicarts from remapping after being locked

This commit is contained in:
Vicki Pfau 2024-11-23 19:46:44 -08:00
parent 822a2c8df5
commit a5e3e746b2
5 changed files with 20 additions and 8 deletions

View File

@ -42,6 +42,7 @@ struct GBAMulticart {
uint8_t offset; uint8_t offset;
uint8_t size; uint8_t size;
bool sramActive; bool sramActive;
bool locked;
uint8_t unk; uint8_t unk;
}; };

View File

@ -295,6 +295,7 @@ DECL_BITS(GBASerializedUnlCartFlags, Subtype, 5, 3);
DECL_BITFIELD(GBASerializedMulticartFlags, uint32_t); DECL_BITFIELD(GBASerializedMulticartFlags, uint32_t);
DECL_BIT(GBASerializedMulticartFlags, DustSettling, 0); DECL_BIT(GBASerializedMulticartFlags, DustSettling, 0);
DECL_BIT(GBASerializedMulticartFlags, Locked, 1);
DECL_BITFIELD(GBASerializedSavedataFlags, uint8_t); DECL_BITFIELD(GBASerializedSavedataFlags, uint8_t);
DECL_BITS(GBASerializedSavedataFlags, FlashState, 0, 2); DECL_BITS(GBASerializedSavedataFlags, FlashState, 0, 2);

View File

@ -69,6 +69,7 @@ void GBAUnlCartReset(struct GBA* gba) {
gba->memory.unl.multi.bank = 0; gba->memory.unl.multi.bank = 0;
gba->memory.unl.multi.offset = 0; gba->memory.unl.multi.offset = 0;
gba->memory.unl.multi.size = 0; gba->memory.unl.multi.size = 0;
gba->memory.unl.multi.locked = false;
gba->memory.rom = gba->memory.unl.multi.rom; gba->memory.rom = gba->memory.unl.multi.rom;
gba->memory.romSize = GBA_SIZE_ROM0; gba->memory.romSize = GBA_SIZE_ROM0;
} }
@ -93,22 +94,25 @@ void GBAUnlCartWriteSRAM(struct GBA* gba, uint32_t address, uint8_t value) {
mLOG(GBA_MEM, DEBUG, "Multicart writing SRAM %06X:%02X", address, value); mLOG(GBA_MEM, DEBUG, "Multicart writing SRAM %06X:%02X", address, value);
switch (address) { switch (address) {
case GBA_MULTICART_CFG_BANK: case GBA_MULTICART_CFG_BANK:
unl->multi.bank = value >> 4; if (!unl->multi.locked) {
if (!(unl->multi.offset & 0x80)) { unl->multi.bank = value >> 4;
mTimingDeschedule(&gba->timing, &unl->multi.settle); mTimingDeschedule(&gba->timing, &unl->multi.settle);
mTimingSchedule(&gba->timing, &unl->multi.settle, MULTI_SETTLE); mTimingSchedule(&gba->timing, &unl->multi.settle, MULTI_SETTLE);
} }
break; break;
case GBA_MULTICART_CFG_OFFSET: case GBA_MULTICART_CFG_OFFSET:
unl->multi.offset = value; if (!unl->multi.locked) {
if (!(unl->multi.offset & 0x80)) { unl->multi.offset = value;
mTimingDeschedule(&gba->timing, &unl->multi.settle); mTimingDeschedule(&gba->timing, &unl->multi.settle);
mTimingSchedule(&gba->timing, &unl->multi.settle, MULTI_SETTLE); mTimingSchedule(&gba->timing, &unl->multi.settle, MULTI_SETTLE);
if (unl->multi.offset & 0x80) {
unl->multi.locked = true;
}
} }
break; break;
case GBA_MULTICART_CFG_SIZE: case GBA_MULTICART_CFG_SIZE:
unl->multi.size = 0x40 - (value & 0x3F); unl->multi.size = 0x40 - (value & 0x3F);
if (!(unl->multi.offset & 0x80)) { if (!unl->multi.locked) {
mTimingDeschedule(&gba->timing, &unl->multi.settle); mTimingDeschedule(&gba->timing, &unl->multi.settle);
mTimingSchedule(&gba->timing, &unl->multi.settle, MULTI_SETTLE); mTimingSchedule(&gba->timing, &unl->multi.settle, MULTI_SETTLE);
} }
@ -167,6 +171,7 @@ static void _multicartSettle(struct mTiming* timing, void* context, uint32_t cyc
void GBAUnlCartSerialize(const struct GBA* gba, struct GBASerializedState* state) { void GBAUnlCartSerialize(const struct GBA* gba, struct GBASerializedState* state) {
GBASerializedUnlCartFlags flags = 0; GBASerializedUnlCartFlags flags = 0;
GBASerializedMulticartFlags multiFlags = 0;
const struct GBAUnlCart* unl = &gba->memory.unl; const struct GBAUnlCart* unl = &gba->memory.unl;
switch (unl->type) { switch (unl->type) {
case GBA_UNL_CART_NONE: case GBA_UNL_CART_NONE:
@ -187,11 +192,13 @@ void GBAUnlCartSerialize(const struct GBA* gba, struct GBASerializedState* state
state->multicart.sramActive = unl->multi.sramActive; state->multicart.sramActive = unl->multi.sramActive;
state->multicart.unk = unl->multi.unk; state->multicart.unk = unl->multi.unk;
state->multicart.currentSize = gba->memory.romSize / MULTI_BLOCK; state->multicart.currentSize = gba->memory.romSize / MULTI_BLOCK;
multiFlags = GBASerializedMulticartFlagsSetLocked(flags, unl->multi.locked);
STORE_16((gba->memory.rom - unl->multi.rom) / 0x20000, 0, &state->multicart.currentOffset); STORE_16((gba->memory.rom - unl->multi.rom) / 0x20000, 0, &state->multicart.currentOffset);
STORE_32(unl->multi.settle.when, 0, &state->multicart.settleNextEvent); STORE_32(unl->multi.settle.when, 0, &state->multicart.settleNextEvent);
if (mTimingIsScheduled(&gba->timing, &unl->multi.settle)) { if (mTimingIsScheduled(&gba->timing, &unl->multi.settle)) {
STORE_32(GBASerializedMulticartFlagsFillDustSettling(0), 0, &state->multicart.flags); multiFlags = GBASerializedMulticartFlagsFillDustSettling(multiFlags);
} }
STORE_32(multiFlags, 0, &state->multicart.flags);
break; break;
} }
STORE_32(flags, 0, &state->hw.unlCartFlags); STORE_32(flags, 0, &state->hw.unlCartFlags);
@ -238,6 +245,7 @@ void GBAUnlCartDeserialize(struct GBA* gba, const struct GBASerializedState* sta
gba->memory.rom = unl->multi.rom + offset; gba->memory.rom = unl->multi.rom + offset;
} }
LOAD_32(multiFlags, 0, &state->multicart.flags); LOAD_32(multiFlags, 0, &state->multicart.flags);
unl->multi.locked = GBASerializedMulticartFlagsGetLocked(multiFlags);
if (GBASerializedMulticartFlagsIsDustSettling(multiFlags)) { if (GBASerializedMulticartFlagsIsDustSettling(multiFlags)) {
LOAD_32(when, 0, &state->multicart.settleNextEvent); LOAD_32(when, 0, &state->multicart.settleNextEvent);
mTimingSchedule(&gba->timing, &unl->multi.settle, when); mTimingSchedule(&gba->timing, &unl->multi.settle, when);

View File

@ -1033,7 +1033,6 @@ void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) {
STORE_32(gba->bus, 0, &state->bus); STORE_32(gba->bus, 0, &state->bus);
GBAHardwareSerialize(&gba->memory.hw, state); GBAHardwareSerialize(&gba->memory.hw, state);
GBAUnlCartSerialize(gba, state);
} }
void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) { void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
@ -1083,5 +1082,4 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
GBADMARecalculateCycles(gba); GBADMARecalculateCycles(gba);
GBADMAUpdate(gba); GBADMAUpdate(gba);
GBAHardwareDeserialize(&gba->memory.hw, state); GBAHardwareDeserialize(&gba->memory.hw, state);
GBAUnlCartDeserialize(gba, state);
} }

View File

@ -83,6 +83,7 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) {
GBAMemorySerialize(&gba->memory, state); GBAMemorySerialize(&gba->memory, state);
GBAIOSerialize(gba, state); GBAIOSerialize(gba, state);
GBAUnlCartSerialize(gba, state);
GBAVideoSerialize(&gba->video, state); GBAVideoSerialize(&gba->video, state);
GBAAudioSerialize(&gba->audio, state); GBAAudioSerialize(&gba->audio, state);
GBASavedataSerialize(&gba->memory.savedata, state); GBASavedataSerialize(&gba->memory.savedata, state);
@ -180,6 +181,9 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
mLOG(GBA_STATE, WARN, "Savestate has unaligned PC and is probably corrupted"); mLOG(GBA_STATE, WARN, "Savestate has unaligned PC and is probably corrupted");
gba->cpu->gprs[ARM_PC] &= ~1; gba->cpu->gprs[ARM_PC] &= ~1;
} }
// Since this can remap the ROM, we need to do this before we reset the pipeline
GBAUnlCartDeserialize(gba, state);
gba->memory.activeRegion = -1; gba->memory.activeRegion = -1;
gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]); gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
if (state->biosPrefetch) { if (state->biosPrefetch) {