Files
lr-snes9x/snapshot.cpp
LibretroAdmin ce77b3c0e2 Replace the Stream class hierarchy with a byte cursor
Stream existed to let the savestate and patch code work over either a
file or a buffer. Nothing needs the file half any more: savestates go
through S9xFreezeGameMem / S9xUnfreezeGameMem, and patches now arrive as
buffers whether they came from a loose file, a zip entry or an .msu1
pack member. What was left was three subclasses, a virtual dispatch per
byte in places, and a macro block in snes9x.h that had to be hidden
behind __cplusplus so the C sources could include the header at all.

bytestream.h replaces it: a struct with buf/size/pos and inline
read/write/seek/tell. A NULL buffer counts bytes without storing them,
which is the S9xFreezeSize sizing pass that nulStream used to do. Reads
past the end come up short and seeks are clamped, so a malformed
savestate or patch cannot walk off the buffer.

Removed with it:

  - stream.cpp and stream.h, and the STREAM macro family in snes9x.h,
    which means snes9x.h no longer needs its __cplusplus guard.
  - S9xFreezeGame and S9xUnfreezeGame, the file-backed savestate entry
    points, along with S9xOpenSnapshotFile and S9xCloseSnapshotFile.
  - S9xResetSaveTimer and the oops-snapshot machinery it drove, whose
    only purpose was writing a .oops file through those entry points,
    and S9xMessageFromResult, which reported their failures.
  - the WDM debug opcode that wrote a .wdm snapshot. It was inside
    #ifdef DEBUGGER, which this tree does not build, and it was the only
    other S9xFreezeGame caller.
  - conffile.cpp and conffile.h. Nothing constructs a ConfigFile; the
    single reference outside the file was an empty S9xParsePortConfig
    stub in the port, implementing a declaration nobody calls.
  - ReadStreamUntilEOF, which existed because Stream had no size(). The
    UPS and BPS readers wanted the whole patch in memory and now just
    address the cursor's buffer directly, so a patch is no longer copied
    a second time to be read.

Verified: Killer Instinct is unchanged on four input scripts - same
audio hash, same sample count, same video. Savestates round-trip
(preemptive-frames replay 0/240 mismatches, save/restore audio
deterministic), IPS and BPS patches apply from a loose file and from
both stored and deflated zips, ROMs load from loose files and zips, and
the MSU-1, input and peripheral suites all pass.
2026-08-08 01:07:22 +02:00

2029 lines
52 KiB
C++

/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include <assert.h>
#include "snes9x.h"
#include "memmap.h"
#include "dma.h"
#include "apu/apu.h"
#include "fxinst.h"
#include "fxemu.h"
#include "sdd1.h"
#include "srtc.h"
#include "bsflash.h"
#include "snapshot.h"
#include "bytestream.h"
#include "controls.h"
#include "display.h"
#include "language.h"
#include "gfx.h"
#ifndef min
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif
typedef struct
{
int offset;
int offset2;
int size;
int type;
uint16 debuted_in;
uint16 deleted_in;
const char *name;
} FreezeData;
enum
{
INT_V,
uint8_ARRAY_V,
uint16_ARRAY_V,
uint32_ARRAY_V,
uint8_INDIR_ARRAY_V,
uint16_INDIR_ARRAY_V,
uint32_INDIR_ARRAY_V,
POINTER_V
};
#define COUNT(ARRAY) (sizeof(ARRAY) / sizeof(ARRAY[0]))
#define Offset(field, structure) ((int) (((char *) (&(((structure) 1)->field))) - ((char *) 1)))
#define OFFSET(f) Offset(f, STRUCT *)
#define DUMMY(f) Offset(f, struct Obsolete *)
#define DELETED(f) (-1)
#define INT_ENTRY(save_version_introduced, field) \
{ \
OFFSET(field), \
0, \
sizeof(((STRUCT *) NULL)->field), \
INT_V, \
save_version_introduced, \
9999, \
#field \
}
#define ARRAY_ENTRY(save_version_introduced, field, count, elemType) \
{ \
OFFSET(field), \
0, \
count, \
elemType, \
save_version_introduced, \
9999, \
#field \
}
#define POINTER_ENTRY(save_version_introduced, field, relativeToField) \
{ \
OFFSET(field), \
OFFSET(relativeToField), \
4, \
POINTER_V, \
save_version_introduced, \
9999, \
#field \
}
#define OBSOLETE_INT_ENTRY(save_version_introduced, save_version_removed, field) \
{ \
DUMMY(field), \
0, \
sizeof(((struct Obsolete *) NULL)->field), \
INT_V, \
save_version_introduced, \
save_version_removed, \
#field \
}
#define OBSOLETE_ARRAY_ENTRY(save_version_introduced, save_version_removed, field, count, elemType) \
{ \
DUMMY(field), \
0, \
count, \
elemType, \
save_version_introduced, \
save_version_removed, \
#field \
}
#define OBSOLETE_POINTER_ENTRY(save_version_introduced, save_version_removed, field, relativeToField) \
{ \
DUMMY(field), \
DUMMY(relativeToField), \
4, \
POINTER_V, \
save_version_introduced, \
save_version_removed, \
#field \
}
#define DELETED_INT_ENTRY(save_version_introduced, save_version_removed, field, size) \
{ \
DELETED(field), \
0, \
size, \
INT_V, \
save_version_introduced, \
save_version_removed, \
#field \
}
#define DELETED_ARRAY_ENTRY(save_version_introduced, save_version_removed, field, count, elemType) \
{ \
DELETED(field), \
0, \
count, \
elemType, \
save_version_introduced, \
save_version_removed, \
#field \
}
#define DELETED_POINTER_ENTRY(save_version_introduced, save_version_removed, field, relativeToField) \
{ \
DELETED(field), \
DELETED(relativeToField), \
4, \
POINTER_V, \
save_version_introduced, \
save_version_removed, \
#field \
}
struct SDMASnapshot
{
struct SDMA dma[8];
};
static struct Obsolete
{
uint8 CPU_IRQActive;
} Obsolete;
#define STRUCT struct SCPUState
static FreezeData SnapCPU[] =
{
INT_ENTRY(6, Cycles),
INT_ENTRY(6, PrevCycles),
INT_ENTRY(6, V_Counter),
INT_ENTRY(6, Flags),
OBSOLETE_INT_ENTRY(6, 7, CPU_IRQActive),
INT_ENTRY(6, IRQPending),
INT_ENTRY(6, MemSpeed),
INT_ENTRY(6, MemSpeedx2),
INT_ENTRY(6, FastROMSpeed),
INT_ENTRY(6, InDMA),
INT_ENTRY(6, InHDMA),
INT_ENTRY(6, InDMAorHDMA),
INT_ENTRY(6, InWRAMDMAorHDMA),
INT_ENTRY(6, HDMARanInDMA),
INT_ENTRY(6, WhichEvent),
INT_ENTRY(6, NextEvent),
INT_ENTRY(6, WaitingForInterrupt),
DELETED_INT_ENTRY(6, 7, WaitAddress, 4),
DELETED_INT_ENTRY(6, 7, WaitCounter, 4),
DELETED_INT_ENTRY(6, 7, PBPCAtOpcodeStart, 4),
INT_ENTRY(7, NMIPending),
INT_ENTRY(7, IRQLine),
INT_ENTRY(7, IRQTransition),
INT_ENTRY(7, IRQLastState),
INT_ENTRY(7, IRQExternal)
};
#undef STRUCT
#define STRUCT struct SRegisters
static FreezeData SnapRegisters[] =
{
INT_ENTRY(6, PB),
INT_ENTRY(6, DB),
INT_ENTRY(6, P.W),
INT_ENTRY(6, A.W),
INT_ENTRY(6, D.W),
INT_ENTRY(6, S.W),
INT_ENTRY(6, X.W),
INT_ENTRY(6, Y.W),
INT_ENTRY(6, PCw)
};
#undef STRUCT
#define STRUCT struct SPPU
static FreezeData SnapPPU[] =
{
INT_ENTRY(6, VMA.High),
INT_ENTRY(6, VMA.Increment),
INT_ENTRY(6, VMA.Address),
INT_ENTRY(6, VMA.Mask1),
INT_ENTRY(6, VMA.FullGraphicCount),
INT_ENTRY(6, VMA.Shift),
INT_ENTRY(6, WRAM),
#define O(N) \
INT_ENTRY(6, BG[N].SCBase), \
INT_ENTRY(6, BG[N].HOffset), \
INT_ENTRY(6, BG[N].VOffset), \
INT_ENTRY(6, BG[N].BGSize), \
INT_ENTRY(6, BG[N].NameBase), \
INT_ENTRY(6, BG[N].SCSize)
O(0), O(1), O(2), O(3),
#undef O
INT_ENTRY(6, BGMode),
INT_ENTRY(6, BG3Priority),
INT_ENTRY(6, CGFLIP),
INT_ENTRY(6, CGFLIPRead),
INT_ENTRY(6, CGADD),
INT_ENTRY(11, CGSavedByte),
ARRAY_ENTRY(6, CGDATA, 256, uint16_ARRAY_V),
#define O(N) \
INT_ENTRY(6, OBJ[N].HPos), \
INT_ENTRY(6, OBJ[N].VPos), \
INT_ENTRY(6, OBJ[N].HFlip), \
INT_ENTRY(6, OBJ[N].VFlip), \
INT_ENTRY(6, OBJ[N].Name), \
INT_ENTRY(6, OBJ[N].Priority), \
INT_ENTRY(6, OBJ[N].Palette), \
INT_ENTRY(6, OBJ[N].Size)
O( 0), O( 1), O( 2), O( 3), O( 4), O( 5), O( 6), O( 7),
O( 8), O( 9), O( 10), O( 11), O( 12), O( 13), O( 14), O( 15),
O( 16), O( 17), O( 18), O( 19), O( 20), O( 21), O( 22), O( 23),
O( 24), O( 25), O( 26), O( 27), O( 28), O( 29), O( 30), O( 31),
O( 32), O( 33), O( 34), O( 35), O( 36), O( 37), O( 38), O( 39),
O( 40), O( 41), O( 42), O( 43), O( 44), O( 45), O( 46), O( 47),
O( 48), O( 49), O( 50), O( 51), O( 52), O( 53), O( 54), O( 55),
O( 56), O( 57), O( 58), O( 59), O( 60), O( 61), O( 62), O( 63),
O( 64), O( 65), O( 66), O( 67), O( 68), O( 69), O( 70), O( 71),
O( 72), O( 73), O( 74), O( 75), O( 76), O( 77), O( 78), O( 79),
O( 80), O( 81), O( 82), O( 83), O( 84), O( 85), O( 86), O( 87),
O( 88), O( 89), O( 90), O( 91), O( 92), O( 93), O( 94), O( 95),
O( 96), O( 97), O( 98), O( 99), O(100), O(101), O(102), O(103),
O(104), O(105), O(106), O(107), O(108), O(109), O(110), O(111),
O(112), O(113), O(114), O(115), O(116), O(117), O(118), O(119),
O(120), O(121), O(122), O(123), O(124), O(125), O(126), O(127),
#undef O
INT_ENTRY(6, OBJThroughMain),
INT_ENTRY(6, OBJThroughSub),
INT_ENTRY(6, OBJAddition),
INT_ENTRY(6, OBJNameBase),
INT_ENTRY(6, OBJNameSelect),
INT_ENTRY(6, OBJSizeSelect),
INT_ENTRY(6, OAMAddr),
INT_ENTRY(6, SavedOAMAddr),
INT_ENTRY(6, OAMPriorityRotation),
INT_ENTRY(6, OAMFlip),
INT_ENTRY(6, OAMReadFlip),
INT_ENTRY(6, OAMTileAddress),
INT_ENTRY(6, OAMWriteRegister),
ARRAY_ENTRY(6, OAMData, 512 + 32, uint8_ARRAY_V),
INT_ENTRY(6, FirstSprite),
INT_ENTRY(6, LastSprite),
INT_ENTRY(6, HTimerEnabled),
INT_ENTRY(6, VTimerEnabled),
INT_ENTRY(6, HTimerPosition),
INT_ENTRY(6, VTimerPosition),
INT_ENTRY(6, IRQHBeamPos),
INT_ENTRY(6, IRQVBeamPos),
INT_ENTRY(6, HBeamFlip),
INT_ENTRY(6, VBeamFlip),
INT_ENTRY(6, HBeamPosLatched),
INT_ENTRY(6, VBeamPosLatched),
INT_ENTRY(6, GunHLatch),
INT_ENTRY(6, GunVLatch),
INT_ENTRY(6, HVBeamCounterLatched),
INT_ENTRY(6, Mode7HFlip),
INT_ENTRY(6, Mode7VFlip),
INT_ENTRY(6, Mode7Repeat),
INT_ENTRY(6, MatrixA),
INT_ENTRY(6, MatrixB),
INT_ENTRY(6, MatrixC),
INT_ENTRY(6, MatrixD),
INT_ENTRY(6, CentreX),
INT_ENTRY(6, CentreY),
INT_ENTRY(6, M7HOFS),
INT_ENTRY(6, M7VOFS),
INT_ENTRY(6, Mosaic),
INT_ENTRY(6, MosaicStart),
ARRAY_ENTRY(6, BGMosaic, 4, uint8_ARRAY_V),
INT_ENTRY(6, Window1Left),
INT_ENTRY(6, Window1Right),
INT_ENTRY(6, Window2Left),
INT_ENTRY(6, Window2Right),
INT_ENTRY(6, RecomputeClipWindows),
#define O(N) \
INT_ENTRY(6, ClipCounts[N]), \
INT_ENTRY(6, ClipWindowOverlapLogic[N]), \
INT_ENTRY(6, ClipWindow1Enable[N]), \
INT_ENTRY(6, ClipWindow2Enable[N]), \
INT_ENTRY(6, ClipWindow1Inside[N]), \
INT_ENTRY(6, ClipWindow2Inside[N])
O(0), O(1), O(2), O(3), O(4), O(5),
#undef O
INT_ENTRY(6, ForcedBlanking),
INT_ENTRY(6, FixedColourRed),
INT_ENTRY(6, FixedColourGreen),
INT_ENTRY(6, FixedColourBlue),
INT_ENTRY(6, Brightness),
INT_ENTRY(6, ScreenHeight),
INT_ENTRY(6, Need16x8Mulitply),
INT_ENTRY(6, BGnxOFSbyte),
INT_ENTRY(6, M7byte),
INT_ENTRY(6, HDMA),
INT_ENTRY(6, HDMAEnded),
INT_ENTRY(6, OpenBus1),
INT_ENTRY(6, OpenBus2),
INT_ENTRY(11, VRAMReadBuffer)
};
#undef STRUCT
#define STRUCT struct SDMASnapshot
static FreezeData SnapDMA[] =
{
#define O(N) \
INT_ENTRY(6, dma[N].ReverseTransfer), \
INT_ENTRY(6, dma[N].HDMAIndirectAddressing), \
INT_ENTRY(6, dma[N].UnusedBit43x0), \
INT_ENTRY(6, dma[N].AAddressFixed), \
INT_ENTRY(6, dma[N].AAddressDecrement), \
INT_ENTRY(6, dma[N].TransferMode), \
INT_ENTRY(6, dma[N].BAddress), \
INT_ENTRY(6, dma[N].AAddress), \
INT_ENTRY(6, dma[N].ABank), \
INT_ENTRY(6, dma[N].DMACount_Or_HDMAIndirectAddress), \
INT_ENTRY(6, dma[N].IndirectBank), \
INT_ENTRY(6, dma[N].Address), \
INT_ENTRY(6, dma[N].Repeat), \
INT_ENTRY(6, dma[N].LineCount), \
INT_ENTRY(6, dma[N].UnknownByte), \
INT_ENTRY(6, dma[N].DoTransfer)
O(0), O(1), O(2), O(3), O(4), O(5), O(6), O(7)
#undef O
};
#undef STRUCT
#define STRUCT struct SControlSnapshot
static FreezeData SnapControls[] =
{
INT_ENTRY(6, ver),
ARRAY_ENTRY(6, port1_read_idx, 2, uint8_ARRAY_V),
ARRAY_ENTRY(6, dummy1, 4, uint8_ARRAY_V),
ARRAY_ENTRY(6, port2_read_idx, 2, uint8_ARRAY_V),
ARRAY_ENTRY(6, dummy2, 4, uint8_ARRAY_V),
ARRAY_ENTRY(6, mouse_speed, 2, uint8_ARRAY_V),
INT_ENTRY(6, justifier_select),
ARRAY_ENTRY(6, dummy3, 8, uint8_ARRAY_V),
INT_ENTRY(6, pad_read),
INT_ENTRY(6, pad_read_last),
ARRAY_ENTRY(6, internal, 60, uint8_ARRAY_V),
ARRAY_ENTRY(10, internal_macs, 5, uint8_ARRAY_V)
};
#undef STRUCT
#define STRUCT struct STimings
static FreezeData SnapTimings[] =
{
INT_ENTRY(6, H_Max_Master),
INT_ENTRY(6, H_Max),
INT_ENTRY(6, V_Max_Master),
INT_ENTRY(6, V_Max),
INT_ENTRY(6, HBlankStart),
INT_ENTRY(6, HBlankEnd),
INT_ENTRY(6, HDMAInit),
INT_ENTRY(6, HDMAStart),
INT_ENTRY(6, NMITriggerPos),
INT_ENTRY(6, WRAMRefreshPos),
INT_ENTRY(6, RenderPos),
INT_ENTRY(6, InterlaceField),
INT_ENTRY(6, DMACPUSync),
INT_ENTRY(6, NMIDMADelay),
INT_ENTRY(6, IRQFlagChanging),
INT_ENTRY(6, APUSpeedup),
INT_ENTRY(7, IRQTriggerCycles),
INT_ENTRY(7, APUAllowTimeOverflow),
INT_ENTRY(11, NextIRQTimer)
};
#undef STRUCT
#define STRUCT struct FxRegs_s
static FreezeData SnapFX[] =
{
ARRAY_ENTRY(6, avReg, 16, uint32_ARRAY_V),
INT_ENTRY(6, vColorReg),
INT_ENTRY(6, vPlotOptionReg),
INT_ENTRY(6, vStatusReg),
INT_ENTRY(6, vPrgBankReg),
INT_ENTRY(6, vRomBankReg),
INT_ENTRY(6, vRamBankReg),
INT_ENTRY(6, vCacheBaseReg),
INT_ENTRY(6, vCacheFlags),
INT_ENTRY(6, vLastRamAdr),
POINTER_ENTRY(6, pvDreg, avRegAddr),
POINTER_ENTRY(6, pvSreg, avRegAddr),
INT_ENTRY(6, vRomBuffer),
INT_ENTRY(6, vPipe),
INT_ENTRY(6, vPipeAdr),
INT_ENTRY(6, vSign),
INT_ENTRY(6, vZero),
INT_ENTRY(6, vCarry),
INT_ENTRY(6, vOverflow),
INT_ENTRY(6, vErrorCode),
INT_ENTRY(6, vIllegalAddress),
INT_ENTRY(6, bBreakPoint),
INT_ENTRY(6, vBreakPoint),
INT_ENTRY(6, vStepPoint),
INT_ENTRY(6, nRamBanks),
INT_ENTRY(6, nRomBanks),
INT_ENTRY(6, vMode),
INT_ENTRY(6, vPrevMode),
POINTER_ENTRY(6, pvScreenBase, pvRam),
#define O(N) \
POINTER_ENTRY(6, apvScreen[N], pvRam)
O( 0), O( 1), O( 2), O( 3), O( 4), O( 5), O( 6), O( 7),
O( 8), O( 9), O( 10), O( 11), O( 12), O( 13), O( 14), O( 15),
O( 16), O( 17), O( 18), O( 19), O( 20), O( 21), O( 22), O( 23),
O( 24), O( 25), O( 26), O( 27), O( 28), O( 29), O( 30), O( 31),
#undef O
ARRAY_ENTRY(6, x, 32, uint32_ARRAY_V),
INT_ENTRY(6, vScreenHeight),
INT_ENTRY(6, vScreenRealHeight),
INT_ENTRY(6, vPrevScreenHeight),
INT_ENTRY(6, vScreenSize),
POINTER_ENTRY(6, pvRamBank, apvRamBank),
POINTER_ENTRY(6, pvRomBank, apvRomBank),
POINTER_ENTRY(6, pvPrgBank, apvRomBank),
#define O(N) \
POINTER_ENTRY(6, apvRamBank[N], pvRam)
O(0), O(1), O(2), O(3),
#undef O
INT_ENTRY(6, bCacheActive),
POINTER_ENTRY(6, pvCache, pvRegisters),
ARRAY_ENTRY(6, avCacheBackup, 512, uint8_ARRAY_V),
INT_ENTRY(6, vCounter),
INT_ENTRY(6, vInstCount),
INT_ENTRY(6, vSCBRDirty)
};
#undef STRUCT
#define STRUCT struct SSA1
static FreezeData SnapSA1[] =
{
DELETED_INT_ENTRY(6, 7, CPUExecuting, 1),
INT_ENTRY(6, ShiftedPB),
INT_ENTRY(6, ShiftedDB),
INT_ENTRY(6, Flags),
DELETED_INT_ENTRY(6, 7, IRQActive, 1),
DELETED_INT_ENTRY(6, 7, Waiting, 1),
INT_ENTRY(6, WaitingForInterrupt),
DELETED_INT_ENTRY(6, 7, WaitAddress, 4),
DELETED_INT_ENTRY(6, 7, WaitCounter, 4),
DELETED_INT_ENTRY(6, 7, PBPCAtOpcodeStart, 4),
DELETED_INT_ENTRY(6, 7, Executing, 1),
INT_ENTRY(6, overflow),
INT_ENTRY(6, in_char_dma),
INT_ENTRY(6, op1),
INT_ENTRY(6, op2),
INT_ENTRY(6, arithmetic_op),
INT_ENTRY(6, sum),
INT_ENTRY(6, VirtualBitmapFormat),
INT_ENTRY(6, variable_bit_pos),
INT_ENTRY(7, Cycles),
INT_ENTRY(7, PrevCycles),
INT_ENTRY(7, TimerIRQLastState),
INT_ENTRY(7, HTimerIRQPos),
INT_ENTRY(7, VTimerIRQPos),
INT_ENTRY(7, HCounter),
INT_ENTRY(7, VCounter),
INT_ENTRY(7, PrevHCounter),
INT_ENTRY(7, MemSpeed),
INT_ENTRY(7, MemSpeedx2)
};
#undef STRUCT
#define STRUCT struct SSA1Registers
static FreezeData SnapSA1Registers[] =
{
INT_ENTRY(6, PB),
INT_ENTRY(6, DB),
INT_ENTRY(6, P.W),
INT_ENTRY(6, A.W),
INT_ENTRY(6, D.W),
INT_ENTRY(6, S.W),
INT_ENTRY(6, X.W),
INT_ENTRY(6, Y.W),
INT_ENTRY(6, PCw)
};
#undef STRUCT
#define STRUCT struct SDSP1
static FreezeData SnapDSP1[] =
{
INT_ENTRY(6, waiting4command),
INT_ENTRY(6, first_parameter),
INT_ENTRY(6, command),
INT_ENTRY(6, in_count),
INT_ENTRY(6, in_index),
INT_ENTRY(6, out_count),
INT_ENTRY(6, out_index),
ARRAY_ENTRY(6, parameters, 512, uint8_ARRAY_V),
ARRAY_ENTRY(6, output, 512, uint8_ARRAY_V),
INT_ENTRY(6, CentreX),
INT_ENTRY(6, CentreY),
INT_ENTRY(6, VOffset),
INT_ENTRY(6, VPlane_C),
INT_ENTRY(6, VPlane_E),
INT_ENTRY(6, SinAas),
INT_ENTRY(6, CosAas),
INT_ENTRY(6, SinAzs),
INT_ENTRY(6, CosAzs),
INT_ENTRY(6, SinAZS),
INT_ENTRY(6, CosAZS),
INT_ENTRY(6, SecAZS_C1),
INT_ENTRY(6, SecAZS_E1),
INT_ENTRY(6, SecAZS_C2),
INT_ENTRY(6, SecAZS_E2),
INT_ENTRY(6, Nx),
INT_ENTRY(6, Ny),
INT_ENTRY(6, Nz),
INT_ENTRY(6, Gx),
INT_ENTRY(6, Gy),
INT_ENTRY(6, Gz),
INT_ENTRY(6, C_Les),
INT_ENTRY(6, E_Les),
INT_ENTRY(6, G_Les),
#define O(N) \
ARRAY_ENTRY(6, matrixA[N], 3, uint16_ARRAY_V), \
ARRAY_ENTRY(6, matrixB[N], 3, uint16_ARRAY_V), \
ARRAY_ENTRY(6, matrixC[N], 3, uint16_ARRAY_V)
O(0), O(1), O(2),
#undef O
INT_ENTRY(6, Op00Multiplicand),
INT_ENTRY(6, Op00Multiplier),
INT_ENTRY(6, Op00Result),
INT_ENTRY(6, Op20Multiplicand),
INT_ENTRY(6, Op20Multiplier),
INT_ENTRY(6, Op20Result),
INT_ENTRY(6, Op10Coefficient),
INT_ENTRY(6, Op10Exponent),
INT_ENTRY(6, Op10CoefficientR),
INT_ENTRY(6, Op10ExponentR),
INT_ENTRY(6, Op04Angle),
INT_ENTRY(6, Op04Radius),
INT_ENTRY(6, Op04Sin),
INT_ENTRY(6, Op04Cos),
INT_ENTRY(6, Op0CA),
INT_ENTRY(6, Op0CX1),
INT_ENTRY(6, Op0CY1),
INT_ENTRY(6, Op0CX2),
INT_ENTRY(6, Op0CY2),
INT_ENTRY(6, Op02FX),
INT_ENTRY(6, Op02FY),
INT_ENTRY(6, Op02FZ),
INT_ENTRY(6, Op02LFE),
INT_ENTRY(6, Op02LES),
INT_ENTRY(6, Op02AAS),
INT_ENTRY(6, Op02AZS),
INT_ENTRY(6, Op02VOF),
INT_ENTRY(6, Op02VVA),
INT_ENTRY(6, Op02CX),
INT_ENTRY(6, Op02CY),
INT_ENTRY(6, Op0AVS),
INT_ENTRY(6, Op0AA),
INT_ENTRY(6, Op0AB),
INT_ENTRY(6, Op0AC),
INT_ENTRY(6, Op0AD),
INT_ENTRY(6, Op06X),
INT_ENTRY(6, Op06Y),
INT_ENTRY(6, Op06Z),
INT_ENTRY(6, Op06H),
INT_ENTRY(6, Op06V),
INT_ENTRY(6, Op06M),
INT_ENTRY(6, Op01m),
INT_ENTRY(6, Op01Zr),
INT_ENTRY(6, Op01Xr),
INT_ENTRY(6, Op01Yr),
INT_ENTRY(6, Op11m),
INT_ENTRY(6, Op11Zr),
INT_ENTRY(6, Op11Xr),
INT_ENTRY(6, Op11Yr),
INT_ENTRY(6, Op21m),
INT_ENTRY(6, Op21Zr),
INT_ENTRY(6, Op21Xr),
INT_ENTRY(6, Op21Yr),
INT_ENTRY(6, Op0DX),
INT_ENTRY(6, Op0DY),
INT_ENTRY(6, Op0DZ),
INT_ENTRY(6, Op0DF),
INT_ENTRY(6, Op0DL),
INT_ENTRY(6, Op0DU),
INT_ENTRY(6, Op1DX),
INT_ENTRY(6, Op1DY),
INT_ENTRY(6, Op1DZ),
INT_ENTRY(6, Op1DF),
INT_ENTRY(6, Op1DL),
INT_ENTRY(6, Op1DU),
INT_ENTRY(6, Op2DX),
INT_ENTRY(6, Op2DY),
INT_ENTRY(6, Op2DZ),
INT_ENTRY(6, Op2DF),
INT_ENTRY(6, Op2DL),
INT_ENTRY(6, Op2DU),
INT_ENTRY(6, Op03F),
INT_ENTRY(6, Op03L),
INT_ENTRY(6, Op03U),
INT_ENTRY(6, Op03X),
INT_ENTRY(6, Op03Y),
INT_ENTRY(6, Op03Z),
INT_ENTRY(6, Op13F),
INT_ENTRY(6, Op13L),
INT_ENTRY(6, Op13U),
INT_ENTRY(6, Op13X),
INT_ENTRY(6, Op13Y),
INT_ENTRY(6, Op13Z),
INT_ENTRY(6, Op23F),
INT_ENTRY(6, Op23L),
INT_ENTRY(6, Op23U),
INT_ENTRY(6, Op23X),
INT_ENTRY(6, Op23Y),
INT_ENTRY(6, Op23Z),
INT_ENTRY(6, Op14Zr),
INT_ENTRY(6, Op14Xr),
INT_ENTRY(6, Op14Yr),
INT_ENTRY(6, Op14U),
INT_ENTRY(6, Op14F),
INT_ENTRY(6, Op14L),
INT_ENTRY(6, Op14Zrr),
INT_ENTRY(6, Op14Xrr),
INT_ENTRY(6, Op14Yrr),
INT_ENTRY(6, Op0EH),
INT_ENTRY(6, Op0EV),
INT_ENTRY(6, Op0EX),
INT_ENTRY(6, Op0EY),
INT_ENTRY(6, Op0BX),
INT_ENTRY(6, Op0BY),
INT_ENTRY(6, Op0BZ),
INT_ENTRY(6, Op0BS),
INT_ENTRY(6, Op1BX),
INT_ENTRY(6, Op1BY),
INT_ENTRY(6, Op1BZ),
INT_ENTRY(6, Op1BS),
INT_ENTRY(6, Op2BX),
INT_ENTRY(6, Op2BY),
INT_ENTRY(6, Op2BZ),
INT_ENTRY(6, Op2BS),
INT_ENTRY(6, Op28X),
INT_ENTRY(6, Op28Y),
INT_ENTRY(6, Op28Z),
INT_ENTRY(6, Op28R),
INT_ENTRY(6, Op1CX),
INT_ENTRY(6, Op1CY),
INT_ENTRY(6, Op1CZ),
INT_ENTRY(6, Op1CXBR),
INT_ENTRY(6, Op1CYBR),
INT_ENTRY(6, Op1CZBR),
INT_ENTRY(6, Op1CXAR),
INT_ENTRY(6, Op1CYAR),
INT_ENTRY(6, Op1CZAR),
INT_ENTRY(6, Op1CX1),
INT_ENTRY(6, Op1CY1),
INT_ENTRY(6, Op1CZ1),
INT_ENTRY(6, Op1CX2),
INT_ENTRY(6, Op1CY2),
INT_ENTRY(6, Op1CZ2),
INT_ENTRY(6, Op0FRamsize),
INT_ENTRY(6, Op0FPass),
INT_ENTRY(6, Op2FUnknown),
INT_ENTRY(6, Op2FSize),
INT_ENTRY(6, Op08X),
INT_ENTRY(6, Op08Y),
INT_ENTRY(6, Op08Z),
INT_ENTRY(6, Op08Ll),
INT_ENTRY(6, Op08Lh),
INT_ENTRY(6, Op18X),
INT_ENTRY(6, Op18Y),
INT_ENTRY(6, Op18Z),
INT_ENTRY(6, Op18R),
INT_ENTRY(6, Op18D),
INT_ENTRY(6, Op38X),
INT_ENTRY(6, Op38Y),
INT_ENTRY(6, Op38Z),
INT_ENTRY(6, Op38R),
INT_ENTRY(6, Op38D)
};
#undef STRUCT
#define STRUCT struct SDSP2
static FreezeData SnapDSP2[] =
{
INT_ENTRY(6, waiting4command),
INT_ENTRY(6, command),
INT_ENTRY(6, in_count),
INT_ENTRY(6, in_index),
INT_ENTRY(6, out_count),
INT_ENTRY(6, out_index),
ARRAY_ENTRY(6, parameters, 512, uint8_ARRAY_V),
ARRAY_ENTRY(6, output, 512, uint8_ARRAY_V),
INT_ENTRY(6, Op05HasLen),
INT_ENTRY(6, Op05Len),
INT_ENTRY(6, Op05Transparent),
INT_ENTRY(6, Op06HasLen),
INT_ENTRY(6, Op06Len),
INT_ENTRY(6, Op09Word1),
INT_ENTRY(6, Op09Word2),
INT_ENTRY(6, Op0DHasLen),
INT_ENTRY(6, Op0DOutLen),
INT_ENTRY(6, Op0DInLen)
};
#undef STRUCT
#define STRUCT struct SDSP4
static FreezeData SnapDSP4[] =
{
INT_ENTRY(6, waiting4command),
INT_ENTRY(6, half_command),
INT_ENTRY(6, command),
INT_ENTRY(6, in_count),
INT_ENTRY(6, in_index),
INT_ENTRY(6, out_count),
INT_ENTRY(6, out_index),
ARRAY_ENTRY(6, parameters, 512, uint8_ARRAY_V),
ARRAY_ENTRY(6, output, 512, uint8_ARRAY_V),
INT_ENTRY(6, byte),
INT_ENTRY(6, address),
INT_ENTRY(6, Logic),
INT_ENTRY(6, lcv),
INT_ENTRY(6, distance),
INT_ENTRY(6, raster),
INT_ENTRY(6, segments),
INT_ENTRY(6, world_x),
INT_ENTRY(6, world_y),
INT_ENTRY(6, world_dx),
INT_ENTRY(6, world_dy),
INT_ENTRY(6, world_ddx),
INT_ENTRY(6, world_ddy),
INT_ENTRY(6, world_xenv),
INT_ENTRY(6, world_yofs),
INT_ENTRY(6, view_x1),
INT_ENTRY(6, view_y1),
INT_ENTRY(6, view_x2),
INT_ENTRY(6, view_y2),
INT_ENTRY(6, view_dx),
INT_ENTRY(6, view_dy),
INT_ENTRY(6, view_xofs1),
INT_ENTRY(6, view_yofs1),
INT_ENTRY(6, view_xofs2),
INT_ENTRY(6, view_yofs2),
INT_ENTRY(6, view_yofsenv),
INT_ENTRY(6, view_turnoff_x),
INT_ENTRY(6, view_turnoff_dx),
INT_ENTRY(6, viewport_cx),
INT_ENTRY(6, viewport_cy),
INT_ENTRY(6, viewport_left),
INT_ENTRY(6, viewport_right),
INT_ENTRY(6, viewport_top),
INT_ENTRY(6, viewport_bottom),
INT_ENTRY(6, sprite_x),
INT_ENTRY(6, sprite_y),
INT_ENTRY(6, sprite_attr),
INT_ENTRY(6, sprite_size),
INT_ENTRY(6, sprite_clipy),
INT_ENTRY(6, sprite_count),
#define O(N) \
ARRAY_ENTRY(6, poly_clipLf[N], 2, uint16_ARRAY_V), \
ARRAY_ENTRY(6, poly_clipRt[N], 2, uint16_ARRAY_V), \
ARRAY_ENTRY(6, poly_ptr[N], 2, uint16_ARRAY_V), \
ARRAY_ENTRY(6, poly_raster[N], 2, uint16_ARRAY_V), \
ARRAY_ENTRY(6, poly_top[N], 2, uint16_ARRAY_V), \
ARRAY_ENTRY(6, poly_bottom[N], 2, uint16_ARRAY_V), \
ARRAY_ENTRY(6, poly_cx[N], 2, uint16_ARRAY_V)
O(0), O(1),
#undef O
ARRAY_ENTRY(6, poly_start, 2, uint16_ARRAY_V),
ARRAY_ENTRY(6, poly_plane, 2, uint16_ARRAY_V),
ARRAY_ENTRY(6, OAM_attr, 16, uint16_ARRAY_V),
INT_ENTRY(6, OAM_index),
INT_ENTRY(6, OAM_bits),
INT_ENTRY(6, OAM_RowMax),
ARRAY_ENTRY(6, OAM_Row, 32, uint16_ARRAY_V)
};
#undef STRUCT
#define STRUCT struct SST010
static FreezeData SnapST010[] =
{
ARRAY_ENTRY(6, input_params, 16, uint8_ARRAY_V),
ARRAY_ENTRY(6, output_params, 16, uint8_ARRAY_V),
INT_ENTRY(6, op_reg),
INT_ENTRY(6, execute),
INT_ENTRY(6, control_enable)
};
#undef STRUCT
#define STRUCT struct SOBC1
static FreezeData SnapOBC1[] =
{
INT_ENTRY(6, address),
INT_ENTRY(6, basePtr),
INT_ENTRY(6, shift)
};
#undef STRUCT
#define STRUCT struct SSPC7110Snapshot
static FreezeData SnapSPC7110Snap[] =
{
INT_ENTRY(6, r4801),
INT_ENTRY(6, r4802),
INT_ENTRY(6, r4803),
INT_ENTRY(6, r4804),
INT_ENTRY(6, r4805),
INT_ENTRY(6, r4806),
INT_ENTRY(6, r4807),
INT_ENTRY(6, r4808),
INT_ENTRY(6, r4809),
INT_ENTRY(6, r480a),
INT_ENTRY(6, r480b),
INT_ENTRY(6, r480c),
INT_ENTRY(6, r4811),
INT_ENTRY(6, r4812),
INT_ENTRY(6, r4813),
INT_ENTRY(6, r4814),
INT_ENTRY(6, r4815),
INT_ENTRY(6, r4816),
INT_ENTRY(6, r4817),
INT_ENTRY(6, r4818),
INT_ENTRY(6, r481x),
INT_ENTRY(6, r4814_latch),
INT_ENTRY(6, r4815_latch),
INT_ENTRY(6, r4820),
INT_ENTRY(6, r4821),
INT_ENTRY(6, r4822),
INT_ENTRY(6, r4823),
INT_ENTRY(6, r4824),
INT_ENTRY(6, r4825),
INT_ENTRY(6, r4826),
INT_ENTRY(6, r4827),
INT_ENTRY(6, r4828),
INT_ENTRY(6, r4829),
INT_ENTRY(6, r482a),
INT_ENTRY(6, r482b),
INT_ENTRY(6, r482c),
INT_ENTRY(6, r482d),
INT_ENTRY(6, r482e),
INT_ENTRY(6, r482f),
INT_ENTRY(6, r4830),
INT_ENTRY(6, r4831),
INT_ENTRY(6, r4832),
INT_ENTRY(6, r4833),
INT_ENTRY(6, r4834),
INT_ENTRY(6, dx_offset),
INT_ENTRY(6, ex_offset),
INT_ENTRY(6, fx_offset),
INT_ENTRY(6, r4840),
INT_ENTRY(6, r4841),
INT_ENTRY(6, r4842),
INT_ENTRY(6, rtc_state),
INT_ENTRY(6, rtc_mode),
INT_ENTRY(6, rtc_index),
INT_ENTRY(6, decomp_mode),
INT_ENTRY(6, decomp_offset),
ARRAY_ENTRY(6, decomp_buffer, SPC7110_DECOMP_BUFFER_SIZE, uint8_ARRAY_V),
INT_ENTRY(6, decomp_buffer_rdoffset),
INT_ENTRY(6, decomp_buffer_wroffset),
INT_ENTRY(6, decomp_buffer_length),
#define O(N) \
INT_ENTRY(6, context[N].index), \
INT_ENTRY(6, context[N].invert)
O( 0), O( 1), O( 2), O( 3), O( 4), O( 5), O( 6), O( 7),
O( 8), O( 9), O( 10), O( 11), O( 12), O( 13), O( 14), O( 15),
O( 16), O( 17), O( 18), O( 19), O( 20), O( 21), O( 22), O( 23),
O( 24), O( 25), O( 26), O( 27), O( 28), O( 29), O( 30), O( 31)
#undef O
};
#undef STRUCT
#define STRUCT struct SSRTCSnapshot
static FreezeData SnapSRTCSnap[] =
{
INT_ENTRY(6, rtc_mode),
INT_ENTRY(6, rtc_index)
};
#undef STRUCT
#define STRUCT struct SBSX
static FreezeData SnapBSX[] =
{
INT_ENTRY(6, dirty),
INT_ENTRY(6, dirty2),
INT_ENTRY(6, bootup),
INT_ENTRY(6, flash_enable),
INT_ENTRY(6, write_enable),
INT_ENTRY(6, read_enable),
INT_ENTRY(6, flash_command),
INT_ENTRY(6, old_write),
INT_ENTRY(6, new_write),
INT_ENTRY(6, out_index),
ARRAY_ENTRY(6, output, 32, uint8_ARRAY_V),
ARRAY_ENTRY(6, PPU, 32, uint8_ARRAY_V),
ARRAY_ENTRY(6, MMC, 16, uint8_ARRAY_V),
ARRAY_ENTRY(6, prevMMC, 16, uint8_ARRAY_V),
ARRAY_ENTRY(6, test2192, 32, uint8_ARRAY_V)
};
#undef STRUCT
#define STRUCT struct SMSU1
/* struct SMSU1 was replaced wholesale at version 13: the flags moved out of
the packed status byte into individual fields (rebuilt into STATUS on every
read), the offsets were renamed to say what they address, and the resampler
carries its interpolation state here so a savestate resumes mid-waveform
instead of reseating. Version 12 and older snapshots have none of these
fields, so they load with MSU1 state zeroed and S9xMSU1PostLoadState
re-derives what it can from the track and offsets. */
static FreezeData SnapMSU1[] =
{
INT_ENTRY(13, MSU1_STATUS),
INT_ENTRY(13, MSU1_DataSeekOffset),
INT_ENTRY(13, MSU1_DataReadOffset),
INT_ENTRY(13, MSU1_AudioPlayOffset),
INT_ENTRY(13, MSU1_AudioLoopOffset),
INT_ENTRY(13, MSU1_CurrentTrack),
INT_ENTRY(13, MSU1_VolumeB),
INT_ENTRY(13, MSU1_AudioResumeTrack),
INT_ENTRY(13, MSU1_AudioResumeOffset),
INT_ENTRY(13, MSU1_Control),
INT_ENTRY(13, MSU1_AudioError),
INT_ENTRY(13, MSU1_AudioPlay),
INT_ENTRY(13, MSU1_AudioRepeat),
INT_ENTRY(13, MSU1_AudioBusy),
INT_ENTRY(13, MSU1_DataBusy),
INT_ENTRY(13, MSU1_RsmpFrac),
INT_ENTRY(13, MSU1_RsmpPrvL),
INT_ENTRY(13, MSU1_RsmpPrvR),
INT_ENTRY(13, MSU1_RsmpCurL),
INT_ENTRY(13, MSU1_RsmpCurR),
INT_ENTRY(13, MSU1_RsmpNxtL),
INT_ENTRY(13, MSU1_RsmpNxtR),
INT_ENTRY(13, MSU1_RsmpNx2L),
INT_ENTRY(13, MSU1_RsmpNx2R),
INT_ENTRY(13, MSU1_RsmpPrimed),
/* The SPC -> 44.1 kHz upsampler that feeds MSU-1 Enhanced Audio. Its
frame pair and 32.32 phase are emulation-timeline state, not scratch:
they carry across batch boundaries by design, and Preemptive Frames
rewinds the timeline. Left un-rewound, every rollback reseats the
interpolator mid-waveform against a frame pair belonging to the
discarded future. */
INT_ENTRY(14, MSU1_EnhFrac),
INT_ENTRY(14, MSU1_EnhCurL),
INT_ENTRY(14, MSU1_EnhCurR),
INT_ENTRY(14, MSU1_EnhNxtL),
INT_ENTRY(14, MSU1_EnhNxtR),
INT_ENTRY(14, MSU1_EnhFill)
};
#undef STRUCT
static int UnfreezeBlock (ByteStream *, const char *, uint8 *, int);
static int UnfreezeBlockCopy (ByteStream *, const char *, uint8 **, int);
static int UnfreezeStructCopy (ByteStream *, const char *, uint8 **, FreezeData *, int, int);
static void UnfreezeStructFromCopy (void *, FreezeData *, int, uint8 *, int);
static void FreezeBlock (ByteStream *, const char *, uint8 *, int);
static void FreezeStruct (ByteStream *, const char *, void *, FreezeData *, int);
static bool CheckBlockName(ByteStream *stream, const char *name, int &len);
static void SkipBlockWithName(ByteStream *stream, const char *name);
uint32 S9xFreezeSize()
{
/* Sizing pass: no buffer, so the writes only advance the cursor. */
ByteStream stream;
bs_init(&stream, NULL, 0);
S9xFreezeToStream(&stream);
return (uint32) bs_pos(&stream);
}
bool8 S9xFreezeGameMem (uint8 *buf, uint32 bufSize)
{
ByteStream stream;
bs_init(&stream, buf, bufSize);
S9xFreezeToStream(&stream);
return (TRUE);
}
int S9xUnfreezeGameMem (const uint8 *buf, uint32 bufSize)
{
ByteStream stream;
bs_init(&stream, (void *) buf, bufSize);
return S9xUnfreezeFromStream(&stream);
}
void S9xFreezeToStream (ByteStream *stream)
{
char buffer[8192];
uint8 *soundsnapshot = new uint8[SPC_SAVE_STATE_BLOCK_SIZE];
sprintf(buffer, "%s:%04d\n", SNAPSHOT_MAGIC, SNAPSHOT_VERSION);
bs_write(stream, buffer, strlen(buffer));
sprintf(buffer, "NAM:%06d:%s%c", 8, "Removed", 0);
bs_write(stream, buffer, strlen(buffer) + 1);
FreezeStruct(stream, "CPU", &CPU, SnapCPU, COUNT(SnapCPU));
FreezeStruct(stream, "REG", &Registers, SnapRegisters, COUNT(SnapRegisters));
FreezeStruct(stream, "PPU", &PPU, SnapPPU, COUNT(SnapPPU));
struct SDMASnapshot dma_snap;
for (int d = 0; d < 8; d++)
dma_snap.dma[d] = DMA[d];
FreezeStruct(stream, "DMA", &dma_snap, SnapDMA, COUNT(SnapDMA));
FreezeBlock (stream, "VRA", Memory.VRAM, sizeof(Memory.VRAM));
FreezeBlock (stream, "RAM", Memory.RAM, sizeof(Memory.RAM));
FreezeBlock (stream, "SRA", Memory.SRAM, Memory.SRAM_SIZE);
FreezeBlock (stream, "FIL", Memory.FillRAM, 0x8000);
S9xAPUSaveState(soundsnapshot);
FreezeBlock (stream, "SND", soundsnapshot, SPC_SAVE_STATE_BLOCK_SIZE);
struct SControlSnapshot ctl_snap;
S9xControlPreSaveState(&ctl_snap);
FreezeStruct(stream, "CTL", &ctl_snap, SnapControls, COUNT(SnapControls));
Timings.InterlaceField = S9xInterlaceField();
FreezeStruct(stream, "TIM", &Timings, SnapTimings, COUNT(SnapTimings));
if (Settings.SuperFX)
{
GSU.avRegAddr = (uint8 *) &GSU.avReg;
FreezeStruct(stream, "SFX", &GSU, SnapFX, COUNT(SnapFX));
}
if (Settings.SA1)
{
S9xSA1PackStatus();
FreezeStruct(stream, "SA1", &SA1, SnapSA1, COUNT(SnapSA1));
FreezeStruct(stream, "SAR", &SA1Registers, SnapSA1Registers, COUNT(SnapSA1Registers));
}
if (Settings.DSP == 1)
FreezeStruct(stream, "DP1", &DSP1, SnapDSP1, COUNT(SnapDSP1));
if (Settings.DSP == 2)
FreezeStruct(stream, "DP2", &DSP2, SnapDSP2, COUNT(SnapDSP2));
if (Settings.DSP == 4)
FreezeStruct(stream, "DP4", &DSP4, SnapDSP4, COUNT(SnapDSP4));
if (Settings.C4)
FreezeBlock (stream, "CX4", Memory.C4RAM, 8192);
if (Settings.SETA == ST_010)
FreezeStruct(stream, "ST0", &ST010, SnapST010, COUNT(SnapST010));
if (Settings.OBC1)
{
FreezeStruct(stream, "OBC", &OBC1, SnapOBC1, COUNT(SnapOBC1));
FreezeBlock (stream, "OBM", Memory.OBC1RAM, 8192);
}
if (Settings.SPC7110)
{
S9xSPC7110PreSaveState();
FreezeStruct(stream, "S71", &s7snap, SnapSPC7110Snap, COUNT(SnapSPC7110Snap));
}
if (Settings.SRTC)
{
S9xSRTCPreSaveState();
FreezeStruct(stream, "SRT", &srtcsnap, SnapSRTCSnap, COUNT(SnapSRTCSnap));
}
if (Settings.SRTC || Settings.SPC7110RTC)
FreezeBlock (stream, "CLK", RTCData.reg, 20);
if (Settings.BS)
FreezeStruct(stream, "BSX", &BSX, SnapBSX, COUNT(SnapBSX));
if (Settings.MSU1)
{
S9xMSU1PreSaveState();
FreezeStruct(stream, "MSU", &MSU1, SnapMSU1, COUNT(SnapMSU1));
}
delete [] soundsnapshot;
}
int S9xUnfreezeFromStream (ByteStream *stream)
{
const bool8 fast = Settings.FastSavestates;
int result = SUCCESS;
int version, len;
char buffer[PATH_MAX + 1];
len = strlen(SNAPSHOT_MAGIC) + 1 + 4 + 1;
if (bs_read(stream, buffer, len) != (unsigned int ) len)
return (WRONG_FORMAT);
if (strncmp(buffer, SNAPSHOT_MAGIC, strlen(SNAPSHOT_MAGIC)) != 0)
return (WRONG_FORMAT);
version = atoi(&buffer[strlen(SNAPSHOT_MAGIC) + 1]);
if (version > SNAPSHOT_VERSION)
return (WRONG_VERSION);
result = UnfreezeBlock(stream, "NAM", (uint8 *) buffer, PATH_MAX);
if (result != SUCCESS)
return (result);
uint8 *local_cpu = NULL;
uint8 *local_registers = NULL;
uint8 *local_ppu = NULL;
uint8 *local_dma = NULL;
uint8 *local_vram = NULL;
uint8 *local_ram = NULL;
uint8 *local_sram = NULL;
uint8 *local_fillram = NULL;
uint8 *local_apu_sound = NULL;
uint8 *local_control_data = NULL;
uint8 *local_timing_data = NULL;
uint8 *local_superfx = NULL;
uint8 *local_sa1 = NULL;
uint8 *local_sa1_registers = NULL;
uint8 *local_dsp1 = NULL;
uint8 *local_dsp2 = NULL;
uint8 *local_dsp4 = NULL;
uint8 *local_cx4_data = NULL;
uint8 *local_st010 = NULL;
uint8 *local_obc1 = NULL;
uint8 *local_obc1_data = NULL;
uint8 *local_spc7110 = NULL;
uint8 *local_srtc = NULL;
uint8 *local_rtc_data = NULL;
uint8 *local_bsx_data = NULL;
uint8 *local_msu1_data = NULL;
do
{
result = UnfreezeStructCopy(stream, "CPU", &local_cpu, SnapCPU, COUNT(SnapCPU), version);
if (result != SUCCESS)
break;
result = UnfreezeStructCopy(stream, "REG", &local_registers, SnapRegisters, COUNT(SnapRegisters), version);
if (result != SUCCESS)
break;
result = UnfreezeStructCopy(stream, "PPU", &local_ppu, SnapPPU, COUNT(SnapPPU), version);
if (result != SUCCESS)
break;
result = UnfreezeStructCopy(stream, "DMA", &local_dma, SnapDMA, COUNT(SnapDMA), version);
if (result != SUCCESS)
break;
if (fast)
result = UnfreezeBlock(stream, "VRA", Memory.VRAM, 0x10000);
else
result = UnfreezeBlockCopy(stream, "VRA", &local_vram, 0x10000);
if (result != SUCCESS)
break;
if (fast)
result = UnfreezeBlock(stream, "RAM", Memory.RAM, sizeof(Memory.RAM));
else
result = UnfreezeBlockCopy(stream, "RAM", &local_ram, sizeof(Memory.RAM));
if (result != SUCCESS)
break;
if (fast)
result = UnfreezeBlock(stream, "SRA", Memory.SRAM, Memory.SRAM_SIZE);
else
result = UnfreezeBlockCopy (stream, "SRA", &local_sram, Memory.SRAM_SIZE);
if (result != SUCCESS)
break;
if (fast)
result = UnfreezeBlock(stream, "FIL", Memory.FillRAM, 0x8000);
else
result = UnfreezeBlockCopy(stream, "FIL", &local_fillram, 0x8000);
if (result != SUCCESS)
break;
result = UnfreezeBlockCopy (stream, "SND", &local_apu_sound, SPC_SAVE_STATE_BLOCK_SIZE);
if (result != SUCCESS)
break;
result = UnfreezeStructCopy(stream, "CTL", &local_control_data, SnapControls, COUNT(SnapControls), version);
if (result != SUCCESS)
break;
result = UnfreezeStructCopy(stream, "TIM", &local_timing_data, SnapTimings, COUNT(SnapTimings), version);
if (result != SUCCESS)
break;
result = UnfreezeStructCopy(stream, "SFX", &local_superfx, SnapFX, COUNT(SnapFX), version);
if (result != SUCCESS && Settings.SuperFX)
break;
result = UnfreezeStructCopy(stream, "SA1", &local_sa1, SnapSA1, COUNT(SnapSA1), version);
if (result != SUCCESS && Settings.SA1)
break;
result = UnfreezeStructCopy(stream, "SAR", &local_sa1_registers, SnapSA1Registers, COUNT(SnapSA1Registers), version);
if (result != SUCCESS && Settings.SA1)
break;
result = UnfreezeStructCopy(stream, "DP1", &local_dsp1, SnapDSP1, COUNT(SnapDSP1), version);
if (result != SUCCESS && Settings.DSP == 1)
break;
result = UnfreezeStructCopy(stream, "DP2", &local_dsp2, SnapDSP2, COUNT(SnapDSP2), version);
if (result != SUCCESS && Settings.DSP == 2)
break;
result = UnfreezeStructCopy(stream, "DP4", &local_dsp4, SnapDSP4, COUNT(SnapDSP4), version);
if (result != SUCCESS && Settings.DSP == 4)
break;
if (Settings.C4)
{
if (fast)
result = UnfreezeBlock(stream, "CX4", Memory.C4RAM, 8192);
else
result = UnfreezeBlockCopy(stream, "CX4", &local_cx4_data, 8192);
if (result != SUCCESS)
break;
}
else
{
SkipBlockWithName(stream, "CX4");
}
result = UnfreezeStructCopy(stream, "ST0", &local_st010, SnapST010, COUNT(SnapST010), version);
if (result != SUCCESS && Settings.SETA == ST_010)
break;
result = UnfreezeStructCopy(stream, "OBC", &local_obc1, SnapOBC1, COUNT(SnapOBC1), version);
if (result != SUCCESS && Settings.OBC1)
break;
if (Settings.OBC1)
{
if (fast)
result = UnfreezeBlock(stream, "OBM", Memory.OBC1RAM, 8192);
else
result = UnfreezeBlockCopy(stream, "OBM", &local_obc1_data, 8192);
if (result != SUCCESS)
break;
}
else
{
SkipBlockWithName(stream, "OBM");
}
result = UnfreezeStructCopy(stream, "S71", &local_spc7110, SnapSPC7110Snap, COUNT(SnapSPC7110Snap), version);
if (result != SUCCESS && Settings.SPC7110)
break;
result = UnfreezeStructCopy(stream, "SRT", &local_srtc, SnapSRTCSnap, COUNT(SnapSRTCSnap), version);
if (result != SUCCESS && Settings.SRTC)
break;
result = UnfreezeBlockCopy (stream, "CLK", &local_rtc_data, 20);
if (result != SUCCESS && (Settings.SRTC || Settings.SPC7110RTC))
break;
result = UnfreezeStructCopy(stream, "BSX", &local_bsx_data, SnapBSX, COUNT(SnapBSX), version);
if (result != SUCCESS && Settings.BS)
break;
result = UnfreezeStructCopy(stream, "MSU", &local_msu1_data, SnapMSU1, COUNT(SnapMSU1), version);
if (result != SUCCESS && Settings.MSU1)
break;
result = SUCCESS;
} while (false);
if (result == SUCCESS)
{
uint32 old_flags = CPU.Flags;
uint32 sa1_old_flags = SA1.Flags;
if (fast)
{
S9xResetPPUFast();
}
else
{
//Do not call this if you have written directly to "Memory." arrays
S9xReset();
}
UnfreezeStructFromCopy(&CPU, SnapCPU, COUNT(SnapCPU), local_cpu, version);
UnfreezeStructFromCopy(&Registers, SnapRegisters, COUNT(SnapRegisters), local_registers, version);
UnfreezeStructFromCopy(&PPU, SnapPPU, COUNT(SnapPPU), local_ppu, version);
struct SDMASnapshot dma_snap;
UnfreezeStructFromCopy(&dma_snap, SnapDMA, COUNT(SnapDMA), local_dma, version);
if (local_vram)
memcpy(Memory.VRAM, local_vram, 0x10000);
if (local_ram)
memcpy(Memory.RAM, local_ram, 0x20000);
if (local_sram)
memcpy(Memory.SRAM, local_sram, Memory.SRAM_SIZE);
if (local_fillram)
memcpy(Memory.FillRAM, local_fillram, 0x8000);
if (version < SNAPSHOT_VERSION_BAPU)
{
printf("Using Blargg APU snapshot loading (snapshot version %d, current is %d)\n...", version, SNAPSHOT_VERSION);
S9xAPULoadBlarggState(local_apu_sound);
}
else if (version < 12)
{
printf("Adjusting old APU snapshot (snapshot version %d, current is %d)\n", version, SNAPSHOT_VERSION);
const size_t spc_block_size = 65700;
const size_t old_dsp_block_size = 514;
const size_t added_bytes_v12 = 128;
const size_t bytes_afterward = 16;
// Shift end to make room for extra 128 bytes
memmove(local_apu_sound + spc_block_size + old_dsp_block_size + added_bytes_v12,
local_apu_sound + spc_block_size + old_dsp_block_size,
bytes_afterward);
// Copy saved internal registers to external registers
const size_t new_dsp_registers_position = spc_block_size + 513;
memmove(local_apu_sound + new_dsp_registers_position,
local_apu_sound + spc_block_size,
added_bytes_v12);
// the extra 0 byte between external registers and bytes_afterward is already present due to memset in S9xAPUSaveState
S9xAPULoadState(local_apu_sound);
}
else if (version >= 12)
{
S9xAPULoadState(local_apu_sound);
}
struct SControlSnapshot ctl_snap;
UnfreezeStructFromCopy(&ctl_snap, SnapControls, COUNT(SnapControls), local_control_data, version);
UnfreezeStructFromCopy(&Timings, SnapTimings, COUNT(SnapTimings), local_timing_data, version);
if (local_superfx)
{
GSU.avRegAddr = (uint8 *) &GSU.avReg;
UnfreezeStructFromCopy(&GSU, SnapFX, COUNT(SnapFX), local_superfx, version);
}
if (local_sa1)
UnfreezeStructFromCopy(&SA1, SnapSA1, COUNT(SnapSA1), local_sa1, version);
if (local_sa1_registers)
UnfreezeStructFromCopy(&SA1Registers, SnapSA1Registers, COUNT(SnapSA1Registers), local_sa1_registers, version);
if (local_dsp1)
UnfreezeStructFromCopy(&DSP1, SnapDSP1, COUNT(SnapDSP1), local_dsp1, version);
if (local_dsp2)
UnfreezeStructFromCopy(&DSP2, SnapDSP2, COUNT(SnapDSP2), local_dsp2, version);
if (local_dsp4)
UnfreezeStructFromCopy(&DSP4, SnapDSP4, COUNT(SnapDSP4), local_dsp4, version);
if (local_cx4_data)
memcpy(Memory.C4RAM, local_cx4_data, 8192);
if (local_st010)
UnfreezeStructFromCopy(&ST010, SnapST010, COUNT(SnapST010), local_st010, version);
if (local_obc1)
UnfreezeStructFromCopy(&OBC1, SnapOBC1, COUNT(SnapOBC1), local_obc1, version);
if (local_obc1_data)
memcpy(Memory.OBC1RAM, local_obc1_data, 8192);
if (local_spc7110)
UnfreezeStructFromCopy(&s7snap, SnapSPC7110Snap, COUNT(SnapSPC7110Snap), local_spc7110, version);
if (local_srtc)
UnfreezeStructFromCopy(&srtcsnap, SnapSRTCSnap, COUNT(SnapSRTCSnap), local_srtc, version);
if (local_rtc_data)
memcpy(RTCData.reg, local_rtc_data, 20);
if (local_bsx_data)
UnfreezeStructFromCopy(&BSX, SnapBSX, COUNT(SnapBSX), local_bsx_data, version);
if (local_msu1_data)
UnfreezeStructFromCopy(&MSU1, SnapMSU1, COUNT(SnapMSU1), local_msu1_data, version);
if (version < SNAPSHOT_VERSION_IRQ)
{
printf("Converting old snapshot version %d to %d\n...", version, SNAPSHOT_VERSION);
CPU.NMIPending = (CPU.Flags & (1 << 7)) ? TRUE : FALSE;
CPU.IRQLine = (CPU.Flags & (1 << 11)) ? TRUE : FALSE;
CPU.IRQTransition = FALSE;
CPU.IRQLastState = FALSE;
CPU.IRQExternal = (Obsolete.CPU_IRQActive & ~(1 << 1)) ? TRUE : FALSE;
switch (CPU.WhichEvent)
{
case 12: case 1: CPU.WhichEvent = 1; break;
case 2: case 3: CPU.WhichEvent = 2; break;
case 4: case 5: CPU.WhichEvent = 3; break;
case 6: case 7: CPU.WhichEvent = 4; break;
case 8: case 9: CPU.WhichEvent = 5; break;
case 10: case 11: CPU.WhichEvent = 6; break;
}
if (local_sa1) // FIXME
{
SA1.Cycles = SA1.PrevCycles = 0;
SA1.TimerIRQLastState = FALSE;
SA1.HTimerIRQPos = Memory.FillRAM[0x2212] | (Memory.FillRAM[0x2213] << 8);
SA1.VTimerIRQPos = Memory.FillRAM[0x2214] | (Memory.FillRAM[0x2215] << 8);
SA1.HCounter = 0;
SA1.VCounter = 0;
SA1.PrevHCounter = 0;
SA1.MemSpeed = ONE_CYCLE;
SA1.MemSpeedx2 = ONE_CYCLE * 2;
}
}
CPU.Flags |= old_flags & (DEBUG_MODE_FLAG | TRACE_FLAG | SINGLE_STEP_FLAG | FRAME_ADVANCE_FLAG);
ICPU.ShiftedPB = Registers.PB << 16;
ICPU.ShiftedDB = Registers.DB << 16;
S9xSetPCBase(Registers.PBPC);
S9xUnpackStatus();
if(version < SNAPSHOT_VERSION_IRQ_2018)
S9xUpdateIRQPositions(false); // calculate the new trigger pos from saved PPU data
S9xFixCycles();
for (int d = 0; d < 8; d++)
DMA[d] = dma_snap.dma[d];
// TODO: these should already be correct since they are stored in the snapshot
CPU.InDMA = CPU.InHDMA = FALSE;
CPU.InDMAorHDMA = CPU.InWRAMDMAorHDMA = FALSE;
CPU.HDMARanInDMA = 0;
S9xFixColourBrightness();
S9xBuildDirectColourMaps();
IPPU.ColorsChanged = TRUE;
IPPU.OBJChanged = TRUE;
IPPU.RenderThisFrame = TRUE;
GFX.DoInterlace = 0;
S9xGraphicsScreenResize();
if (Settings.FastSavestates == 0)
memset(GFX.Screen,0,GFX.Pitch * MAX_SNES_HEIGHT);
// TODO: this seems to be a relic from 1.43 changes, completely remove if no issues in the future
/*uint8 hdma_byte = Memory.FillRAM[0x420c];
S9xSetCPU(hdma_byte, 0x420c);*/
S9xControlPostLoadState(&ctl_snap);
if (local_superfx)
{
// Opcode/plot dispatch rebinds from vMode inside
// fx_readRegisterSpace() on the next S9xSuperFXExec; only the
// register-window pointer needs reseating here.
GSU.avRegAddr = (uint8 *) &GSU.avReg;
}
if (local_sa1 && local_sa1_registers)
{
SA1.Flags |= sa1_old_flags & TRACE_FLAG;
S9xSA1PostLoadState();
}
if (Settings.SDD1)
S9xSDD1PostLoadState();
if (local_spc7110)
S9xSPC7110PostLoadState();
if (local_srtc)
S9xSRTCPostLoadState();
if (local_bsx_data)
{
S9xBSXPostLoadState();
// The flash chip's transient command state is not serialized;
// return it to array-read mode, as a real chip after power-up.
S9xBSFlashReset();
}
if (local_msu1_data)
S9xMSU1PostLoadState();
}
if (local_cpu) delete [] local_cpu;
if (local_registers) delete [] local_registers;
if (local_ppu) delete [] local_ppu;
if (local_dma) delete [] local_dma;
if (local_vram) delete [] local_vram;
if (local_ram) delete [] local_ram;
if (local_sram) delete [] local_sram;
if (local_fillram) delete [] local_fillram;
if (local_apu_sound) delete [] local_apu_sound;
if (local_control_data) delete [] local_control_data;
if (local_timing_data) delete [] local_timing_data;
if (local_superfx) delete [] local_superfx;
if (local_sa1) delete [] local_sa1;
if (local_sa1_registers) delete [] local_sa1_registers;
if (local_dsp1) delete [] local_dsp1;
if (local_dsp2) delete [] local_dsp2;
if (local_dsp4) delete [] local_dsp4;
if (local_cx4_data) delete [] local_cx4_data;
if (local_st010) delete [] local_st010;
if (local_obc1) delete [] local_obc1;
if (local_obc1_data) delete [] local_obc1_data;
if (local_spc7110) delete [] local_spc7110;
if (local_srtc) delete [] local_srtc;
if (local_rtc_data) delete [] local_rtc_data;
if (local_bsx_data) delete [] local_bsx_data;
return (result);
}
static int FreezeSize (int size, int type)
{
switch (type)
{
case uint32_ARRAY_V:
case uint32_INDIR_ARRAY_V:
return (size * 4);
case uint16_ARRAY_V:
case uint16_INDIR_ARRAY_V:
return (size * 2);
default:
return (size);
}
}
static void FreezeStruct (ByteStream *stream, const char *name, void *base, FreezeData *fields, int num_fields)
{
int len = 0;
int i, j;
for (i = 0; i < num_fields; i++)
{
if (SNAPSHOT_VERSION < fields[i].debuted_in)
{
fprintf(stderr, "%s[%p]: field has bad debuted_in value %d, > %d.", name, (void *) fields, fields[i].debuted_in, SNAPSHOT_VERSION);
continue;
}
if (SNAPSHOT_VERSION < fields[i].deleted_in)
len += FreezeSize(fields[i].size, fields[i].type);
}
uint8 *block = new uint8[len];
uint8 *ptr = block;
uint8 *addr;
uint16 word;
uint32 dword;
int64 qaword;
int relativeAddr;
for (i = 0; i < num_fields; i++)
{
if (SNAPSHOT_VERSION >= fields[i].deleted_in || SNAPSHOT_VERSION < fields[i].debuted_in)
continue;
addr = (uint8 *) base + fields[i].offset;
// determine real address of indirect-type fields
// (where the structure contains a pointer to an array rather than the array itself)
if (fields[i].type == uint8_INDIR_ARRAY_V || fields[i].type == uint16_INDIR_ARRAY_V || fields[i].type == uint32_INDIR_ARRAY_V)
addr = (uint8 *) (*((pint *) addr));
// convert pointer-type saves from absolute to relative pointers
if (fields[i].type == POINTER_V)
{
uint8 *pointer = (uint8 *) *((pint *) ((uint8 *) base + fields[i].offset));
uint8 *relativeTo = (uint8 *) *((pint *) ((uint8 *) base + fields[i].offset2));
relativeAddr = pointer - relativeTo;
addr = (uint8 *) &relativeAddr;
}
switch (fields[i].type)
{
case INT_V:
case POINTER_V:
switch (fields[i].size)
{
case 1:
*ptr++ = *(addr);
break;
case 2:
word = *((uint16 *) (addr));
*ptr++ = (uint8) (word >> 8);
*ptr++ = (uint8) word;
break;
case 4:
dword = *((uint32 *) (addr));
*ptr++ = (uint8) (dword >> 24);
*ptr++ = (uint8) (dword >> 16);
*ptr++ = (uint8) (dword >> 8);
*ptr++ = (uint8) dword;
break;
case 8:
qaword = *((int64 *) (addr));
*ptr++ = (uint8) (qaword >> 56);
*ptr++ = (uint8) (qaword >> 48);
*ptr++ = (uint8) (qaword >> 40);
*ptr++ = (uint8) (qaword >> 32);
*ptr++ = (uint8) (qaword >> 24);
*ptr++ = (uint8) (qaword >> 16);
*ptr++ = (uint8) (qaword >> 8);
*ptr++ = (uint8) qaword;
break;
}
break;
case uint8_ARRAY_V:
case uint8_INDIR_ARRAY_V:
memmove(ptr, addr, fields[i].size);
ptr += fields[i].size;
break;
case uint16_ARRAY_V:
case uint16_INDIR_ARRAY_V:
for (j = 0; j < fields[i].size; j++)
{
word = *((uint16 *) (addr + j * 2));
*ptr++ = (uint8) (word >> 8);
*ptr++ = (uint8) word;
}
break;
case uint32_ARRAY_V:
case uint32_INDIR_ARRAY_V:
for (j = 0; j < fields[i].size; j++)
{
dword = *((uint32 *) (addr + j * 4));
*ptr++ = (uint8) (dword >> 24);
*ptr++ = (uint8) (dword >> 16);
*ptr++ = (uint8) (dword >> 8);
*ptr++ = (uint8) dword;
}
break;
}
}
FreezeBlock(stream, name, block, len);
delete [] block;
}
static void FreezeBlock (ByteStream *stream, const char *name, uint8 *block, int size)
{
char buffer[20];
// check if it fits in 6 digits. (letting it go over and using strlen isn't safe)
if (size <= 999999)
sprintf(buffer, "%s:%06d:", name, size);
else
{
// to make it fit, pack it in the bytes instead of as digits
sprintf(buffer, "%s:------:", name);
buffer[6] = (unsigned char) ((unsigned) size >> 24);
buffer[7] = (unsigned char) ((unsigned) size >> 16);
buffer[8] = (unsigned char) ((unsigned) size >> 8);
buffer[9] = (unsigned char) ((unsigned) size >> 0);
}
buffer[11] = 0;
bs_write(stream, buffer, 11);
bs_write(stream, block, size);
}
static bool CheckBlockName(ByteStream *stream, const char *name, int &len)
{
char buffer[16];
len = 0;
size_t l = bs_read(stream, buffer, 11);
buffer[l] = 0;
bs_seek(stream, bs_pos(stream) - l);
if (buffer[4] == '-')
{
len = (((unsigned char)buffer[6]) << 24)
| (((unsigned char)buffer[7]) << 16)
| (((unsigned char)buffer[8]) << 8)
| (((unsigned char)buffer[9]) << 0);
}
else
len = atoi(buffer + 4);
if (l != 11 || strncmp(buffer, name, 3) != 0 || buffer[3] != ':')
{
return false;
}
if (len <= 0)
{
return false;
}
return true;
}
static void SkipBlockWithName(ByteStream *stream, const char *name)
{
int len;
bool matchesName = CheckBlockName(stream, name, len);
if (matchesName)
{
long rewind = bs_pos(stream);
rewind += len + 11;
bs_seek(stream, rewind);
}
}
static int UnfreezeBlock (ByteStream *stream, const char *name, uint8 *block, int size)
{
char buffer[20];
int len = 0, rem = 0;
long rewind = bs_pos(stream);
size_t l = bs_read(stream, buffer, 11);
buffer[l] = 0;
if (l != 11 || strncmp(buffer, name, 3) != 0 || buffer[3] != ':')
{
err:
#ifdef DEBUGGER
fprintf(stdout, "absent: %s(%d); next: '%.11s'\n", name, size, buffer);
#endif
bs_seek(stream, bs_pos(stream) - l);
return (WRONG_FORMAT);
}
if (buffer[4] == '-')
{
len = (((unsigned char) buffer[6]) << 24)
| (((unsigned char) buffer[7]) << 16)
| (((unsigned char) buffer[8]) << 8)
| (((unsigned char) buffer[9]) << 0);
}
else
len = atoi(buffer + 4);
if (len <= 0)
goto err;
if (len > size)
{
rem = len - size;
len = size;
}
if (!Settings.FastSavestates)
{
memset(block, 0, size);
}
if (bs_read(stream, block, len) != (unsigned int) len)
{
bs_seek(stream, rewind);
return (WRONG_FORMAT);
}
if (rem)
{
char *junk = new char[rem];
len = bs_read(stream, junk, rem);
delete [] junk;
if (len != rem)
{
bs_seek(stream, rewind);
return (WRONG_FORMAT);
}
}
return (SUCCESS);
}
static int UnfreezeBlockCopy (ByteStream *stream, const char *name, uint8 **block, int size)
{
int result;
//check name first to avoid memory allocation
int blockLength;
if (!CheckBlockName(stream, name, blockLength))
{
return 0;
}
*block = new uint8[size];
result = UnfreezeBlock(stream, name, *block, size);
if (result != SUCCESS)
{
delete [] (*block);
*block = NULL;
return (result);
}
return (SUCCESS);
}
static int UnfreezeStructCopy (ByteStream *stream, const char *name, uint8 **block, FreezeData *fields, int num_fields, int version)
{
int len = 0;
for (int i = 0; i < num_fields; i++)
{
if (version >= fields[i].debuted_in && version < fields[i].deleted_in)
len += FreezeSize(fields[i].size, fields[i].type);
}
return (UnfreezeBlockCopy(stream, name, block, len));
}
static void UnfreezeStructFromCopy (void *sbase, FreezeData *fields, int num_fields, uint8 *block, int version)
{
uint8 *ptr = block;
uint16 word;
uint32 dword;
int64 qaword;
uint8 *addr;
void *base;
int relativeAddr;
int i, j;
for (i = 0; i < num_fields; i++)
{
if (version < fields[i].debuted_in || version >= fields[i].deleted_in)
continue;
base = (SNAPSHOT_VERSION >= fields[i].deleted_in) ? ((void *) &Obsolete) : sbase;
addr = (uint8 *) base + fields[i].offset;
if (fields[i].type == uint8_INDIR_ARRAY_V || fields[i].type == uint16_INDIR_ARRAY_V || fields[i].type == uint32_INDIR_ARRAY_V)
addr = (uint8 *) (*((pint *) addr));
switch (fields[i].type)
{
case INT_V:
case POINTER_V:
switch (fields[i].size)
{
case 1:
if (fields[i].offset < 0)
{
ptr++;
break;
}
*(addr) = *ptr++;
break;
case 2:
if (fields[i].offset < 0)
{
ptr += 2;
break;
}
word = *ptr++ << 8;
word |= *ptr++;
*((uint16 *) (addr)) = word;
break;
case 4:
if (fields[i].offset < 0)
{
ptr += 4;
break;
}
dword = *ptr++ << 24;
dword |= *ptr++ << 16;
dword |= *ptr++ << 8;
dword |= *ptr++;
*((uint32 *) (addr)) = dword;
break;
case 8:
if (fields[i].offset < 0)
{
ptr += 8;
break;
}
qaword = (int64) *ptr++ << 56;
qaword |= (int64) *ptr++ << 48;
qaword |= (int64) *ptr++ << 40;
qaword |= (int64) *ptr++ << 32;
qaword |= (int64) *ptr++ << 24;
qaword |= (int64) *ptr++ << 16;
qaword |= (int64) *ptr++ << 8;
qaword |= (int64) *ptr++;
*((int64 *) (addr)) = qaword;
break;
default:
assert(0);
break;
}
break;
case uint8_ARRAY_V:
case uint8_INDIR_ARRAY_V:
if (fields[i].offset >= 0)
memmove(addr, ptr, fields[i].size);
ptr += fields[i].size;
break;
case uint16_ARRAY_V:
case uint16_INDIR_ARRAY_V:
if (fields[i].offset < 0)
{
ptr += fields[i].size * 2;
break;
}
for (j = 0; j < fields[i].size; j++)
{
word = *ptr++ << 8;
word |= *ptr++;
*((uint16 *) (addr + j * 2)) = word;
}
break;
case uint32_ARRAY_V:
case uint32_INDIR_ARRAY_V:
if (fields[i].offset < 0)
{
ptr += fields[i].size * 4;
break;
}
for (j = 0; j < fields[i].size; j++)
{
dword = *ptr++ << 24;
dword |= *ptr++ << 16;
dword |= *ptr++ << 8;
dword |= *ptr++;
*((uint32 *) (addr + j * 4)) = dword;
}
break;
}
if (fields[i].type == POINTER_V)
{
relativeAddr = (int) *((pint *) ((uint8 *) base + fields[i].offset));
uint8 *relativeTo = (uint8 *) *((pint *) ((uint8 *) base + fields[i].offset2));
*((pint *) (addr)) = (pint) (relativeTo + relativeAddr);
}
}
}