bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) { bool error = false; int32_t check; uint32_t ucheck; int16_t check16; uint16_t ucheck16; LOAD_32LE(ucheck, 0, &state->versionMagic); if (ucheck > GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION) { mLOG(GB_STATE, WARN, "Invalid or too new savestate: expected %08X, got %08X", GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION, ucheck); error = true; } else if (ucheck < GB_SAVESTATE_MAGIC) { mLOG(GB_STATE, WARN, "Invalid savestate: expected %08X, got %08X", GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION, ucheck); error = true; } else if (ucheck < GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION) { mLOG(GB_STATE, WARN, "Old savestate: expected %08X, got %08X, continuing anyway", GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION, ucheck); } bool canSgb = ucheck >= GB_SAVESTATE_MAGIC + 2; if (gb->memory.rom && memcmp(state->title, ((struct GBCartridge*) &gb->memory.rom[0x100])->titleLong, sizeof(state->title))) { LOAD_32LE(ucheck, 0, &state->versionMagic); if (ucheck > GB_SAVESTATE_MAGIC + 2 || memcmp(state->title, ((struct GBCartridge*) gb->memory.rom)->titleLong, sizeof(state->title))) { // There was a bug in previous versions where the memory address being compared was wrong mLOG(GB_STATE, WARN, "Savestate is for a different game"); error = true; } } LOAD_32LE(ucheck, 0, &state->romCrc32); if (ucheck != gb->romCrc32) { mLOG(GB_STATE, WARN, "Savestate is for a different version of the game"); } LOAD_32LE(check, 0, &state->cpu.cycles); if (check < 0) { mLOG(GB_STATE, WARN, "Savestate is corrupted: CPU cycles are negative"); error = true; } if (state->cpu.executionState != LR35902_CORE_FETCH) { mLOG(GB_STATE, WARN, "Savestate is corrupted: Execution state is not FETCH"); error = true; } if (check >= (int32_t) DMG_LR35902_FREQUENCY) { mLOG(GB_STATE, WARN, "Savestate is corrupted: CPU cycles are too high"); error = true; } LOAD_16LE(check16, 0, &state->video.x); if (check16 < 0 || check16 > GB_VIDEO_HORIZONTAL_PIXELS) { mLOG(GB_STATE, WARN, "Savestate is corrupted: video x is out of range"); error = true; } LOAD_16LE(check16, 0, &state->video.ly); if (check16 < 0 || check16 > GB_VIDEO_VERTICAL_TOTAL_PIXELS) { mLOG(GB_STATE, WARN, "Savestate is corrupted: video y is out of range"); error = true; } LOAD_16LE(ucheck16, 0, &state->memory.dmaDest); if (ucheck16 + state->memory.dmaRemaining > GB_SIZE_OAM) { mLOG(GB_STATE, WARN, "Savestate is corrupted: DMA destination is out of range"); error = true; } LOAD_16LE(ucheck16, 0, &state->video.bcpIndex); if (ucheck16 >= 0x40) { mLOG(GB_STATE, WARN, "Savestate is corrupted: BCPS is out of range"); } LOAD_16LE(ucheck16, 0, &state->video.ocpIndex); if (ucheck16 >= 0x40) { mLOG(GB_STATE, WARN, "Savestate is corrupted: OCPS is out of range"); } if (error) { return false; } gb->timing.root = NULL; LOAD_32LE(gb->timing.masterCycles, 0, &state->masterCycles); gb->cpu->a = state->cpu.a; gb->cpu->f.packed = state->cpu.f; gb->cpu->b = state->cpu.b; gb->cpu->c = state->cpu.c; gb->cpu->d = state->cpu.d; gb->cpu->e = state->cpu.e; gb->cpu->h = state->cpu.h; gb->cpu->l = state->cpu.l; LOAD_16LE(gb->cpu->sp, 0, &state->cpu.sp); LOAD_16LE(gb->cpu->pc, 0, &state->cpu.pc); LOAD_16LE(gb->cpu->index, 0, &state->cpu.index); gb->cpu->bus = state->cpu.bus; gb->cpu->executionState = state->cpu.executionState; GBSerializedCpuFlags flags; LOAD_32LE(flags, 0, &state->cpu.flags); gb->cpu->condition = GBSerializedCpuFlagsGetCondition(flags); gb->cpu->irqPending = GBSerializedCpuFlagsGetIrqPending(flags); gb->doubleSpeed = GBSerializedCpuFlagsGetDoubleSpeed(flags); gb->audio.timingFactor = gb->doubleSpeed + 1; LOAD_32LE(gb->cpu->cycles, 0, &state->cpu.cycles); LOAD_32LE(gb->cpu->nextEvent, 0, &state->cpu.nextEvent); gb->timing.root = NULL; uint32_t when; LOAD_32LE(when, 0, &state->cpu.eiPending); if (GBSerializedCpuFlagsIsEiPending(flags)) { mTimingSchedule(&gb->timing, &gb->eiPending, when); } gb->model = state->model; if (gb->model < GB_MODEL_CGB) { gb->audio.style = GB_AUDIO_DMG; } else { gb->audio.style = GB_AUDIO_CGB; } GBMemoryDeserialize(gb, state); GBVideoDeserialize(&gb->video, state); GBIODeserialize(gb, state); GBTimerDeserialize(&gb->timer, state); GBAudioDeserialize(&gb->audio, state); if (gb->model & GB_MODEL_SGB && canSgb) { GBSGBDeserialize(gb, state); } gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc); gb->timing.reroot = gb->timing.root; gb->timing.root = NULL; return true; }
bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) { bool error = false; int32_t check; uint32_t ucheck; LOAD_32LE(ucheck, 0, &state->versionMagic); if (ucheck > GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION) { mLOG(GB_STATE, WARN, "Invalid or too new savestate: expected %08X, got %08X", GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION, ucheck); error = true; } else if (ucheck < GB_SAVESTATE_MAGIC) { mLOG(GB_STATE, WARN, "Invalid savestate: expected %08X, got %08X", GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION, ucheck); error = true; } else if (ucheck < GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION) { mLOG(GB_STATE, WARN, "Old savestate: expected %08X, got %08X, continuing anyway", GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION, ucheck); } if (gb->memory.rom && memcmp(state->title, ((struct GBCartridge*) gb->memory.rom)->titleLong, sizeof(state->title))) { mLOG(GB_STATE, WARN, "Savestate is for a different game"); error = true; } LOAD_32LE(ucheck, 0, &state->romCrc32); if (ucheck != gb->romCrc32) { mLOG(GB_STATE, WARN, "Savestate is for a different version of the game"); } LOAD_32LE(check, 0, &state->cpu.cycles); if (check < 0) { mLOG(GB_STATE, WARN, "Savestate is corrupted: CPU cycles are negative"); error = true; } if (check >= (int32_t) DMG_LR35902_FREQUENCY) { mLOG(GB_STATE, WARN, "Savestate is corrupted: CPU cycles are too high"); error = true; } LOAD_32LE(check, 0, &state->video.eventDiff); if (check < 0) { mLOG(GB_STATE, WARN, "Savestate is corrupted: video eventDiff is negative"); error = true; } if (error) { return false; } gb->cpu->a = state->cpu.a; gb->cpu->f.packed = state->cpu.f; gb->cpu->b = state->cpu.b; gb->cpu->c = state->cpu.c; gb->cpu->d = state->cpu.d; gb->cpu->e = state->cpu.e; gb->cpu->h = state->cpu.h; gb->cpu->l = state->cpu.l; LOAD_16LE(gb->cpu->sp, 0, &state->cpu.sp); LOAD_16LE(gb->cpu->pc, 0, &state->cpu.pc); LOAD_16LE(gb->cpu->index, 0, &state->cpu.index); gb->cpu->bus = state->cpu.bus; gb->cpu->executionState = state->cpu.executionState; LOAD_16LE(gb->cpu->irqVector, 0, &state->cpu.irqVector); LOAD_32LE(gb->eiPending, 0, &state->cpu.eiPending); GBSerializedCpuFlags flags; LOAD_32LE(flags, 0, &state->cpu.flags); gb->cpu->condition = GBSerializedCpuFlagsGetCondition(flags); gb->cpu->irqPending = GBSerializedCpuFlagsGetIrqPending(flags); gb->doubleSpeed = GBSerializedCpuFlagsGetDoubleSpeed(flags); LOAD_32LE(gb->cpu->cycles, 0, &state->cpu.cycles); LOAD_32LE(gb->cpu->nextEvent, 0, &state->cpu.nextEvent); gb->model = state->model; if (gb->model < GB_MODEL_CGB) { gb->audio.style = GB_AUDIO_DMG; } else { gb->audio.style = GB_AUDIO_CGB; } GBMemoryDeserialize(&gb->memory, state); GBIODeserialize(gb, state); GBVideoDeserialize(&gb->video, state); GBTimerDeserialize(&gb->timer, state); GBAudioDeserialize(&gb->audio, state); gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc); return true; }
void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { struct GBMemory* memory = &gb->memory; memcpy(memory->wram, state->wram, GB_SIZE_WORKING_RAM); memcpy(memory->hram, state->hram, GB_SIZE_HRAM); LOAD_16LE(memory->currentBank, 0, &state->memory.currentBank); memory->wramCurrentBank = state->memory.wramCurrentBank; memory->sramCurrentBank = state->memory.sramCurrentBank; GBMBCSwitchBank(gb, memory->currentBank); GBMemorySwitchWramBank(memory, memory->wramCurrentBank); GBMBCSwitchSramBank(gb, memory->sramCurrentBank); LOAD_16LE(memory->dmaSource, 0, &state->memory.dmaSource); LOAD_16LE(memory->dmaDest, 0, &state->memory.dmaDest); LOAD_16LE(memory->hdmaSource, 0, &state->memory.hdmaSource); LOAD_16LE(memory->hdmaDest, 0, &state->memory.hdmaDest); LOAD_16LE(memory->hdmaRemaining, 0, &state->memory.hdmaRemaining); memory->dmaRemaining = state->memory.dmaRemaining; memcpy(memory->rtcRegs, state->memory.rtcRegs, sizeof(state->memory.rtcRegs)); uint32_t when; LOAD_32LE(when, 0, &state->memory.dmaNext); if (memory->dmaRemaining) { mTimingSchedule(&gb->timing, &memory->dmaEvent, when); } LOAD_32LE(when, 0, &state->memory.hdmaNext); if (memory->hdmaRemaining) { mTimingSchedule(&gb->timing, &memory->hdmaEvent, when); } GBSerializedMemoryFlags flags; LOAD_16LE(flags, 0, &state->memory.flags); memory->sramAccess = GBSerializedMemoryFlagsGetSramAccess(flags); memory->rtcAccess = GBSerializedMemoryFlagsGetRtcAccess(flags); memory->rtcLatched = GBSerializedMemoryFlagsGetRtcLatched(flags); memory->ime = GBSerializedMemoryFlagsGetIme(flags); memory->isHdma = GBSerializedMemoryFlagsGetIsHdma(flags); memory->activeRtcReg = GBSerializedMemoryFlagsGetActiveRtcReg(flags); switch (memory->mbcType) { case GB_MBC1: memory->mbcState.mbc1.mode = state->memory.mbc1.mode; memory->mbcState.mbc1.multicartStride = state->memory.mbc1.multicartStride; if (memory->mbcState.mbc1.mode) { GBMBCSwitchBank0(gb, memory->currentBank >> memory->mbcState.mbc1.multicartStride); } break; case GB_MBC3_RTC: LOAD_64LE(gb->memory.rtcLastLatch, 0, &state->memory.rtc.lastLatch); break; case GB_MBC7: memory->mbcState.mbc7.state = state->memory.mbc7.state; memory->mbcState.mbc7.eeprom = state->memory.mbc7.eeprom; memory->mbcState.mbc7.address = state->memory.mbc7.address & 0x7F; memory->mbcState.mbc7.access = state->memory.mbc7.access; memory->mbcState.mbc7.latch = state->memory.mbc7.latch; memory->mbcState.mbc7.srBits = state->memory.mbc7.srBits; LOAD_16LE(memory->mbcState.mbc7.sr, 0, &state->memory.mbc7.sr); LOAD_32LE(memory->mbcState.mbc7.writable, 0, &state->memory.mbc7.writable); break; default: break; }