GBA: Restore savestates

This commit is contained in:
Jeffrey Pfau 2016-02-13 01:00:24 -08:00
parent 61378f3926
commit adf20bea09
3 changed files with 94 additions and 49 deletions

View File

@ -327,33 +327,44 @@ static void _sample(struct GBAAudio* audio) {
void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state) { void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state) {
uint32_t flags = 0; uint32_t flags = 0;
uint32_t ch1Flags = 0;
uint32_t ch2Flags = 0;
uint32_t ch4Flags = 0;
flags = GBASerializedAudioFlagsSetFrame(flags, audio->psg.frame);
flags = GBASerializedAudioFlagsSetCh1Volume(flags, audio->psg.ch1.envelope.currentVolume); flags = GBASerializedAudioFlagsSetCh1Volume(flags, audio->psg.ch1.envelope.currentVolume);
flags = GBASerializedAudioFlagsSetCh1Dead(flags, audio->psg.ch1.envelope.dead); flags = GBASerializedAudioFlagsSetCh1Dead(flags, audio->psg.ch1.envelope.dead);
flags = GBASerializedAudioFlagsSetCh1Hi(flags, audio->psg.ch1.control.hi); flags = GBASerializedAudioFlagsSetCh1Hi(flags, audio->psg.ch1.control.hi);
/*STORE_32(audio->psg.ch1.envelope.nextStep, 0, &state->audio.ch1.envelopeNextStep); flags = GBASerializedAudioFlagsSetCh1SweepEnabled(flags, audio->psg.ch1.sweepEnable);
STORE_32(audio->psg.ch1.control.nextStep, 0, &state->audio.ch1.waveNextStep); flags = GBASerializedAudioFlagsSetCh1SweepOccurred(flags, audio->psg.ch1.sweepOccurred);
STORE_32(audio->psg.ch1.nextSweep, 0, &state->audio.ch1.sweepNextStep); ch1Flags = GBASerializedAudioEnvelopeSetLength(ch1Flags, audio->psg.ch1.control.length);
STORE_32(audio->psg.ch1.control.endTime, 0, &state->audio.ch1.endTime);*/ ch1Flags = GBASerializedAudioEnvelopeSetNextStep(ch1Flags, audio->psg.ch1.envelope.nextStep);
ch1Flags = GBASerializedAudioEnvelopeSetFrequency(ch1Flags, audio->psg.ch1.realFrequency);
STORE_32(ch1Flags, 0, &state->audio.ch1.envelope);
STORE_32(audio->psg.nextFrame, 0, &state->audio.ch1.nextFrame);
STORE_32(audio->psg.nextCh1, 0, &state->audio.ch1.nextEvent); STORE_32(audio->psg.nextCh1, 0, &state->audio.ch1.nextEvent);
flags = GBASerializedAudioFlagsSetCh2Volume(flags, audio->psg.ch2.envelope.currentVolume); flags = GBASerializedAudioFlagsSetCh2Volume(flags, audio->psg.ch2.envelope.currentVolume);
flags = GBASerializedAudioFlagsSetCh2Dead(flags, audio->psg.ch2.envelope.dead); flags = GBASerializedAudioFlagsSetCh2Dead(flags, audio->psg.ch2.envelope.dead);
flags = GBASerializedAudioFlagsSetCh2Hi(flags, audio->psg.ch2.control.hi); flags = GBASerializedAudioFlagsSetCh2Hi(flags, audio->psg.ch2.control.hi);
/*STORE_32(audio->psg.ch2.envelope.nextStep, 0, &state->audio.ch2.envelopeNextStep); ch2Flags = GBASerializedAudioEnvelopeSetLength(ch2Flags, audio->psg.ch2.control.length);
STORE_32(audio->psg.ch2.control.nextStep, 0, &state->audio.ch2.waveNextStep); ch2Flags = GBASerializedAudioEnvelopeSetNextStep(ch2Flags, audio->psg.ch2.envelope.nextStep);
STORE_32(audio->psg.ch2.control.endTime, 0, &state->audio.ch2.endTime);*/ STORE_32(ch2Flags, 0, &state->audio.ch2.envelope);
STORE_32(audio->psg.nextCh2, 0, &state->audio.ch2.nextEvent); STORE_32(audio->psg.nextCh2, 0, &state->audio.ch2.nextEvent);
memcpy(state->audio.ch3.wavebanks, audio->psg.ch3.wavedata, sizeof(state->audio.ch3.wavebanks)); memcpy(state->audio.ch3.wavebanks, audio->psg.ch3.wavedata, sizeof(state->audio.ch3.wavebanks));
//STORE_32(audio->psg.ch3.endTime, 0, &state->audio.ch3.endTime); STORE_16(audio->psg.ch3.length, 0, &state->audio.ch3.length);
STORE_32(audio->psg.nextCh3, 0, &state->audio.ch3.nextEvent); STORE_32(audio->psg.nextCh3, 0, &state->audio.ch3.nextEvent);
flags = GBASerializedAudioFlagsSetCh4Volume(flags, audio->psg.ch4.envelope.currentVolume);
flags = GBASerializedAudioFlagsSetCh4Dead(flags, audio->psg.ch4.envelope.dead);
state->audio.flags = GBASerializedAudioFlagsSetCh4Volume(flags, audio->psg.ch4.envelope.currentVolume); state->audio.flags = GBASerializedAudioFlagsSetCh4Volume(flags, audio->psg.ch4.envelope.currentVolume);
state->audio.flags = GBASerializedAudioFlagsSetCh4Dead(flags, audio->psg.ch4.envelope.dead); state->audio.flags = GBASerializedAudioFlagsSetCh4Dead(flags, audio->psg.ch4.envelope.dead);
STORE_32(audio->psg.ch4.envelope.nextStep, 0, &state->audio.ch4.envelopeNextStep);
STORE_32(audio->psg.ch4.lfsr, 0, &state->audio.ch4.lfsr); STORE_32(audio->psg.ch4.lfsr, 0, &state->audio.ch4.lfsr);
//STORE_32(audio->psg.ch4.endTime, 0, &state->audio.ch4.endTime); ch4Flags = GBASerializedAudioEnvelopeSetLength(ch4Flags, audio->psg.ch4.length);
ch4Flags = GBASerializedAudioEnvelopeSetNextStep(ch4Flags, audio->psg.ch4.envelope.nextStep);
STORE_32(ch4Flags, 0, &state->audio.ch4.envelope);
STORE_32(audio->psg.nextCh4, 0, &state->audio.ch4.nextEvent); STORE_32(audio->psg.nextCh4, 0, &state->audio.ch4.nextEvent);
STORE_32(flags, 0, &state->audio.flags); STORE_32(flags, 0, &state->audio.flags);
@ -370,34 +381,42 @@ void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState*
void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState* state) { void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState* state) {
uint32_t flags; uint32_t flags;
uint32_t ch1Flags = 0;
uint32_t ch2Flags = 0;
uint32_t ch4Flags = 0;
LOAD_32(flags, 0, &state->audio.flags); LOAD_32(flags, 0, &state->audio.flags);
LOAD_32(ch1Flags, 0, &state->audio.ch1.envelope);
audio->psg.ch1.envelope.currentVolume = GBASerializedAudioFlagsGetCh1Volume(flags); audio->psg.ch1.envelope.currentVolume = GBASerializedAudioFlagsGetCh1Volume(flags);
audio->psg.ch1.envelope.dead = GBASerializedAudioFlagsGetCh1Dead(flags); audio->psg.ch1.envelope.dead = GBASerializedAudioFlagsGetCh1Dead(flags);
audio->psg.ch1.control.hi = GBASerializedAudioFlagsGetCh1Hi(flags); audio->psg.ch1.control.hi = GBASerializedAudioFlagsGetCh1Hi(flags);
LOAD_32(audio->psg.ch1.envelope.nextStep, 0, &state->audio.ch1.envelopeNextStep); audio->psg.ch1.sweepEnable = GBASerializedAudioFlagsGetCh1SweepEnabled(flags);
/*LOAD_32(audio->psg.ch1.control.nextStep, 0, &state->audio.ch1.waveNextStep); audio->psg.ch1.sweepOccurred = GBASerializedAudioFlagsGetCh1SweepOccurred(flags);
LOAD_32(audio->psg.ch1.nextSweep, 0, &state->audio.ch1.sweepNextStep); audio->psg.ch1.control.length = GBASerializedAudioEnvelopeGetLength(ch1Flags);
LOAD_32(audio->psg.ch1.control.endTime, 0, &state->audio.ch1.endTime);*/ audio->psg.ch1.envelope.nextStep = GBASerializedAudioEnvelopeGetNextStep(ch1Flags);
audio->psg.ch1.realFrequency = GBASerializedAudioEnvelopeGetFrequency(ch1Flags);
LOAD_32(audio->psg.nextFrame, 0, &state->audio.ch1.nextFrame);
LOAD_32(audio->psg.nextCh1, 0, &state->audio.ch1.nextEvent); LOAD_32(audio->psg.nextCh1, 0, &state->audio.ch1.nextEvent);
LOAD_32(ch2Flags, 0, &state->audio.ch1.envelope);
audio->psg.ch2.envelope.currentVolume = GBASerializedAudioFlagsGetCh2Volume(flags); audio->psg.ch2.envelope.currentVolume = GBASerializedAudioFlagsGetCh2Volume(flags);
audio->psg.ch2.envelope.dead = GBASerializedAudioFlagsGetCh2Dead(flags); audio->psg.ch2.envelope.dead = GBASerializedAudioFlagsGetCh2Dead(flags);
audio->psg.ch2.control.hi = GBASerializedAudioFlagsGetCh2Hi(flags); audio->psg.ch2.control.hi = GBASerializedAudioFlagsGetCh2Hi(flags);
LOAD_32(audio->psg.ch2.envelope.nextStep, 0, &state->audio.ch2.envelopeNextStep); audio->psg.ch2.control.length = GBASerializedAudioEnvelopeGetLength(ch2Flags);
/*LOAD_32(audio->psg.ch2.control.nextStep, 0, &state->audio.ch2.waveNextStep); audio->psg.ch2.envelope.nextStep = GBASerializedAudioEnvelopeGetNextStep(ch2Flags);
LOAD_32(audio->psg.ch2.control.endTime, 0, &state->audio.ch2.endTime);*/
LOAD_32(audio->psg.nextCh2, 0, &state->audio.ch2.nextEvent); LOAD_32(audio->psg.nextCh2, 0, &state->audio.ch2.nextEvent);
// TODO: Big endian? // TODO: Big endian?
memcpy(audio->psg.ch3.wavedata, state->audio.ch3.wavebanks, sizeof(audio->psg.ch3.wavedata)); memcpy(audio->psg.ch3.wavedata, state->audio.ch3.wavebanks, sizeof(audio->psg.ch3.wavedata));
//LOAD_32(audio->psg.ch3.endTime, 0, &state->audio.ch3.endTime); LOAD_16(audio->psg.ch3.length, 0, &state->audio.ch3.length);
LOAD_32(audio->psg.nextCh3, 0, &state->audio.ch3.nextEvent); LOAD_32(audio->psg.nextCh3, 0, &state->audio.ch3.nextEvent);
LOAD_32(ch4Flags, 0, &state->audio.ch1.envelope);
audio->psg.ch4.envelope.currentVolume = GBASerializedAudioFlagsGetCh4Volume(flags); audio->psg.ch4.envelope.currentVolume = GBASerializedAudioFlagsGetCh4Volume(flags);
audio->psg.ch4.envelope.dead = GBASerializedAudioFlagsGetCh4Dead(flags); audio->psg.ch4.envelope.dead = GBASerializedAudioFlagsGetCh4Dead(flags);
LOAD_32(audio->psg.ch4.envelope.nextStep, 0, &state->audio.ch4.envelopeNextStep); audio->psg.ch4.length = GBASerializedAudioEnvelopeGetLength(ch4Flags);
audio->psg.ch4.envelope.nextStep = GBASerializedAudioEnvelopeGetNextStep(ch4Flags);
LOAD_32(audio->psg.ch4.lfsr, 0, &state->audio.ch4.lfsr); LOAD_32(audio->psg.ch4.lfsr, 0, &state->audio.ch4.lfsr);
//LOAD_32(audio->psg.ch4.endTime, 0, &state->audio.ch4.endTime);
LOAD_32(audio->psg.nextCh4, 0, &state->audio.ch4.nextEvent); LOAD_32(audio->psg.nextCh4, 0, &state->audio.ch4.nextEvent);
CircleBufferClear(&audio->chA.fifo); CircleBufferClear(&audio->chA.fifo);

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau /* Copyright (c) 2013-2016 Jeffrey Pfau
* *
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
@ -25,6 +25,7 @@
#endif #endif
const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000; const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000;
const uint32_t GBA_SAVESTATE_VERSION = 0x00000001;
mLOG_DEFINE_CATEGORY(GBA_STATE, "GBA Savestate"); mLOG_DEFINE_CATEGORY(GBA_STATE, "GBA Savestate");
@ -40,7 +41,7 @@ struct GBAExtdataHeader {
}; };
void GBASerialize(struct GBA* gba, struct GBASerializedState* state) { void GBASerialize(struct GBA* gba, struct GBASerializedState* state) {
STORE_32(GBA_SAVESTATE_MAGIC, 0, &state->versionMagic); STORE_32(GBA_SAVESTATE_MAGIC + GBA_SAVESTATE_VERSION, 0, &state->versionMagic);
STORE_32(gba->biosChecksum, 0, &state->biosChecksum); STORE_32(gba->biosChecksum, 0, &state->biosChecksum);
STORE_32(gba->romCrc32, 0, &state->romCrc32); STORE_32(gba->romCrc32, 0, &state->romCrc32);
@ -97,9 +98,14 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
int32_t check; int32_t check;
uint32_t ucheck; uint32_t ucheck;
LOAD_32(ucheck, 0, &state->versionMagic); LOAD_32(ucheck, 0, &state->versionMagic);
if (ucheck != GBA_SAVESTATE_MAGIC) { if (ucheck > GBA_SAVESTATE_MAGIC + GBA_SAVESTATE_VERSION) {
mLOG(GBA_STATE, WARN, "Invalid or too new savestate: expected %08X, got %08X", GBA_SAVESTATE_MAGIC, ucheck); mLOG(GBA_STATE, WARN, "Invalid or too new savestate: expected %08X, got %08X", GBA_SAVESTATE_MAGIC + GBA_SAVESTATE_VERSION, ucheck);
error = true; error = true;
} else if (ucheck < GBA_SAVESTATE_MAGIC) {
mLOG(GBA_STATE, WARN, "Invalid savestate: expected %08X, got %08X", GBA_SAVESTATE_MAGIC + GBA_SAVESTATE_VERSION, ucheck);
error = true;
} else {
mLOG(GBA_STATE, WARN, "Old savestate: expected %08X, got %08X, continuing anyway", GBA_SAVESTATE_MAGIC + GBA_SAVESTATE_VERSION, ucheck);
} }
LOAD_32(ucheck, 0, &state->biosChecksum); LOAD_32(ucheck, 0, &state->biosChecksum);
if (ucheck != gba->biosChecksum) { if (ucheck != gba->biosChecksum) {

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau /* Copyright (c) 2013-2016 Jeffrey Pfau
* *
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
@ -12,11 +12,12 @@
#include "gba/gba.h" #include "gba/gba.h"
extern const uint32_t GBA_SAVESTATE_MAGIC; extern const uint32_t GBA_SAVESTATE_MAGIC;
extern const uint32_t GBA_SAVESTATE_VERSION;
mLOG_DECLARE_CATEGORY(GBA_STATE); mLOG_DECLARE_CATEGORY(GBA_STATE);
/* Savestate format: /* Savestate format:
* 0x00000 - 0x00003: Version Magic (0x01000000) * 0x00000 - 0x00003: Version Magic (0x01000001)
* 0x00004 - 0x00007: BIOS checksum (e.g. 0xBAAE187F for official BIOS) * 0x00004 - 0x00007: BIOS checksum (e.g. 0xBAAE187F for official BIOS)
* 0x00008 - 0x0000B: ROM CRC32 * 0x00008 - 0x0000B: ROM CRC32
* 0x0000C - 0x0000F: Reserved (leave zero) * 0x0000C - 0x0000F: Reserved (leave zero)
@ -30,25 +31,34 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* | 0x0006C - 0x0006F: Cycles until next event * | 0x0006C - 0x0006F: Cycles until next event
* | 0x00070 - 0x00117: Banked registers * | 0x00070 - 0x00117: Banked registers
* | 0x00118 - 0x0012F: Banked SPSRs * | 0x00118 - 0x0012F: Banked SPSRs
* 0x00130 - 0x00143: Audio channel 1 state * 0x00130 - 0x00143: Audio channel 1/framer state
* | 0x00130 - 0x00133: Next envelope step * | 0x00130 - 0x00133: Envelepe timing
* | 0x00134 - 0x00137: Next square wave step * | bits 0 - 6: Remaining length
* | 0x00138 - 0x0013B: Next sweep step * | bits 7 - 9: Next step
* | 0x0013C - 0x0013F: Channel end cycle * | bits 10 - 20: Shadow frequency register
* | bits 21 - 31: Reserved
* | 0x00134 - 0x00137: Next frame
* | 0x00138 - 0x0013F: Reserved
* | 0x00140 - 0x00143: Next event * | 0x00140 - 0x00143: Next event
* 0x00144 - 0x00153: Audio channel 2 state * 0x00144 - 0x00153: Audio channel 2 state
* | 0x00144 - 0x00147: Next envelope step * | 0x00144 - 0x00147: Envelepe timing
* | 0x00148 - 0x0014B: Next square wave step * | bits 0 - 2: Remaining length
* | 0x0014C - 0x0014F: Channel end cycle * | bits 3 - 5: Next step
* | bits 6 - 31: Reserved
* | 0x00148 - 0x0014F: Reserved
* | 0x00150 - 0x00153: Next event * | 0x00150 - 0x00153: Next event
* 0x00154 - 0x0017B: Audio channel 3 state * 0x00154 - 0x0017B: Audio channel 3 state
* | 0x00154 - 0x00173: Wave banks * | 0x00154 - 0x00173: Wave banks
* | 0x00174 - 0x00177: Channel end cycle * | 0x00174 - 0x00175: Remaining length
* | 0x00176 - 0x00177: Reserved
* | 0x00178 - 0x0017B: Next event * | 0x00178 - 0x0017B: Next event
* 0x0017C - 0x0018B: Audio channel 4 state * 0x0017C - 0x0018B: Audio channel 4 state
* | 0x0017C - 0x0017F: Linear feedback shift register state * | 0x0017C - 0x0017F: Linear feedback shift register state
* | 0x00180 - 0x00183: Next enveleope step * | 0x00180 - 0x00183: Envelepe timing
* | 0x00184 - 0x00187: Channel end cycle * | bits 0 - 2: Remaining length
* | bits 3 - 5: Next step
* | bits 6 - 31: Reserved
* | 0x00184 - 0x00187: Reserved
* | 0x00188 - 0x0018B: Next event * | 0x00188 - 0x0018B: Next event
* 0x0018C - 0x001AB: Audio FIFO 1 * 0x0018C - 0x001AB: Audio FIFO 1
* 0x001AC - 0x001CB: Audio FIFO 2 * 0x001AC - 0x001CB: Audio FIFO 2
@ -57,11 +67,13 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* | 0x001D0 - 0x001D3: Event diff * | 0x001D0 - 0x001D3: Event diff
* | 0x001D4 - 0x001D7: Next sample * | 0x001D4 - 0x001D7: Next sample
* | 0x001D8 - 0x001DB: FIFO size * | 0x001D8 - 0x001DB: FIFO size
* | TODO: Fix this, they're in big-endian order, but field is little-endian
* | 0x001DC - 0x001DC: Channel 1 envelope state * | 0x001DC - 0x001DC: Channel 1 envelope state
* | bits 0 - 3: Current volume * | bits 0 - 3: Current volume
* | bit 4: Is dead? * | bit 4: Is dead?
* | bit 5: Is high? * | bit 5: Is high?
* | bits 6 - 7: Reserved * | bit 6: Is sweep enabled?
* | bit 7: Has sweep occurred?
* | 0x001DD - 0x001DD: Channel 2 envelope state * | 0x001DD - 0x001DD: Channel 2 envelope state
* | bits 0 - 3: Current volume * | bits 0 - 3: Current volume
* | bit 4: Is dead? * | bit 4: Is dead?
@ -71,7 +83,8 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* | bits 0 - 3: Current volume * | bits 0 - 3: Current volume
* | bit 4: Is dead? * | bit 4: Is dead?
* | bits 5 - 7: Reserved * | bits 5 - 7: Reserved
* | 0x001DF - 0x001DF: Reserved * | 0x001DF - 0x001DF: Miscellaneous audio flags
* | bits 0 - 3: Current frame
* 0x001E0 - 0x001FF: Video miscellaneous state * 0x001E0 - 0x001FF: Video miscellaneous state
* | 0x001E0 - 0x001E3: Next event * | 0x001E0 - 0x001E3: Next event
* | 0x001E4 - 0x001E7: Event diff * | 0x001E4 - 0x001E7: Event diff
@ -193,11 +206,19 @@ DECL_BITFIELD(GBASerializedAudioFlags, uint32_t);
DECL_BITS(GBASerializedAudioFlags, Ch1Volume, 0, 4); DECL_BITS(GBASerializedAudioFlags, Ch1Volume, 0, 4);
DECL_BIT(GBASerializedAudioFlags, Ch1Dead, 4); DECL_BIT(GBASerializedAudioFlags, Ch1Dead, 4);
DECL_BIT(GBASerializedAudioFlags, Ch1Hi, 5); DECL_BIT(GBASerializedAudioFlags, Ch1Hi, 5);
DECL_BIT(GBASerializedAudioFlags, Ch1SweepEnabled, 6);
DECL_BIT(GBASerializedAudioFlags, Ch1SweepOccurred, 7);
DECL_BITS(GBASerializedAudioFlags, Ch2Volume, 8, 4); DECL_BITS(GBASerializedAudioFlags, Ch2Volume, 8, 4);
DECL_BIT(GBASerializedAudioFlags, Ch2Dead, 12); DECL_BIT(GBASerializedAudioFlags, Ch2Dead, 12);
DECL_BIT(GBASerializedAudioFlags, Ch2Hi, 13); DECL_BIT(GBASerializedAudioFlags, Ch2Hi, 13);
DECL_BITS(GBASerializedAudioFlags, Ch4Volume, 16, 4); DECL_BITS(GBASerializedAudioFlags, Ch4Volume, 16, 4);
DECL_BIT(GBASerializedAudioFlags, Ch4Dead, 20); DECL_BIT(GBASerializedAudioFlags, Ch4Dead, 20);
DECL_BITS(GBASerializedAudioFlags, Frame, 21, 3);
DECL_BITFIELD(GBASerializedAudioEnvelope, uint32_t);
DECL_BITS(GBASerializedAudioEnvelope, Length, 0, 7);
DECL_BITS(GBASerializedAudioEnvelope, NextStep, 7, 3);
DECL_BITS(GBASerializedAudioEnvelope, Frequency, 10, 11);
DECL_BITFIELD(GBASerializedHWFlags1, uint16_t); DECL_BITFIELD(GBASerializedHWFlags1, uint16_t);
DECL_BIT(GBASerializedHWFlags1, ReadWrite, 0); DECL_BIT(GBASerializedHWFlags1, ReadWrite, 0);
@ -239,27 +260,26 @@ struct GBASerializedState {
struct { struct {
struct { struct {
int32_t envelopeNextStep; GBASerializedAudioEnvelope envelope;
int32_t waveNextStep; int32_t nextFrame;
int32_t sweepNextStep; int32_t reserved[2];
int32_t endTime;
int32_t nextEvent; int32_t nextEvent;
} ch1; } ch1;
struct { struct {
int32_t envelopeNextStep; GBASerializedAudioEnvelope envelope;
int32_t waveNextStep; int32_t reserved[2];
int32_t endTime;
int32_t nextEvent; int32_t nextEvent;
} ch2; } ch2;
struct { struct {
uint32_t wavebanks[8]; uint32_t wavebanks[8];
int32_t endTime; int16_t length;
int16_t reserved;
int32_t nextEvent; int32_t nextEvent;
} ch3; } ch3;
struct { struct {
int32_t lfsr; int32_t lfsr;
int32_t envelopeNextStep; GBASerializedAudioEnvelope envelope;
int32_t endTime; int32_t reserved;
int32_t nextEvent; int32_t nextEvent;
} ch4; } ch4;
uint8_t fifoA[32]; uint8_t fifoA[32];