// serialize.cpp : save states #include "types.h" #include "hw/aica/dsp.h" #include "hw/aica/aica.h" #include "hw/aica/sgc_if.h" #include "hw/arm7/arm7.h" #include "hw/holly/sb_mem.h" #include "hw/holly/sb.h" #include "hw/flashrom/flashrom.h" #include "hw/mem/_vmem.h" #include "hw/gdrom/gdromv3.h" #include "hw/maple/maple_devs.h" #include "hw/maple/maple_cfg.h" #include "hw/pvr/spg.h" #include "hw/pvr/Renderer_if.h" #include "hw/pvr/ta_structs.h" #include "hw/sh4/sh4_interrupts.h" #include "hw/sh4/sh4_sched.h" #include "hw/sh4/sh4_mmr.h" #include "hw/sh4/modules/mmu.h" #include "imgread/common.h" #include "reios/reios.h" #include "reios/gdrom_hle.h" #include #include #include "rend/gles/gles.h" #include "hw/sh4/dyna/blockmanager.h" #include "hw/sh4/dyna/ngen.h" #include "hw/naomi/naomi.h" #include "hw/naomi/naomi_cart.h" #include "hw/sh4/sh4_cache.h" #include "hw/bba/bba.h" #define LIBRETRO_SKIP(size) do { if (*data) *(u8**)data += (size); *total_size += (size); } while (false) /* * search for "maybe" to find items that were left out that may be needed */ //./core/hw/arm7/arm_mem.cpp extern bool aica_interr; extern u32 aica_reg_L; extern bool e68k_out; extern u32 e68k_reg_L; extern u32 e68k_reg_M; //./core/hw/arm7/arm7.cpp extern DECL_ALIGN(8) reg_pair arm_Reg[RN_ARM_REG_SLOTS]; extern bool armIrqEnable; extern bool armFiqEnable; extern int armMode; extern bool Arm7Enabled; //./core/hw/aica/dsp.o extern DECL_ALIGN(4096) dsp_t dsp; //./core/hw/aica/aica.o extern AicaTimer timers[3]; //./core/hw/aica/aica_if.o extern VArray2 aica_ram; extern u32 VREG;//video reg =P extern u32 ARMRST;//arm reset reg extern u32 rtc_EN; extern u32 RealTimeClock; extern int dma_sched_id; //./core/hw/aica/aica_mem.o extern u8 aica_reg[0x8000]; //./core/hw/aica/sgc_if.o struct ChannelEx; #define AicaChannel ChannelEx //special handling //extern AicaChannel AicaChannel::Chans[64]; #define Chans AicaChannel::Chans #define CDDA_SIZE (2352/2) extern s16 cdda_sector[CDDA_SIZE]; extern u32 cdda_index; //./core/hw/holly/sb.o extern Array sb_regs; extern u32 SB_ISTNRM; extern u32 SB_FFST_rc; extern u32 SB_FFST; //./core/hw/holly/sb_mem.o extern SRamChip sys_nvmem_sram; extern DCFlashChip sys_nvmem_flash; //./core/hw/pvr/elan_host.o #include "hw/pvr/elan.h" #include "hw/pvr/elan_host.h" #include "hw/holly/holly_intc.h" //./core/hw/gdrom/gdromv3.o extern int gdrom_sched; extern int gd_swap_schid; extern signed int sns_asc; extern signed int sns_ascq; extern signed int sns_key; extern packet_cmd_t packet_cmd; extern u32 set_mode_offset; extern read_params_t read_params ; extern packet_cmd_t packet_cmd; //Buffer for sector reads [dma] extern read_buff_t read_buff ; //pio buffer extern pio_buff_t pio_buff ; extern u32 set_mode_offset; extern ata_cmd_t ata_cmd ; extern cdda_t cdda ; extern gd_states gd_state; extern DiscType gd_disk_type; extern u32 data_write_mode; //Registers extern u32 DriveSel; extern GD_ErrRegT Error; extern GD_InterruptReasonT IntReason; extern GD_FeaturesT Features; extern GD_SecCountT SecCount; extern GD_SecNumbT SecNumber; extern GD_StatusT GDStatus; extern ByteCount_t GDByteCount ; extern int GDROM_TICK; //./core/hw/maple/maple_devs.o extern char EEPROM[0x100]; extern bool EEPROM_loaded; //./core/hw/maple/maple_if.o //needs special handler extern maple_device* MapleDevices[4][6]; extern int maple_sched; #include "hw/maple/maple_if.h" extern bool maple_ddt_pending_reset; #ifdef ENABLE_MODEM //./core/hw/modem/modem.cpp extern int modem_sched; #endif //./core/hw/pvr/Renderer_if.o extern bool pend_rend; extern u32 fb_w_cur; //./core/hw/pvr/pvr_mem.o extern u32 YUV_tempdata[512/4];//512 bytes extern u32 YUV_dest; extern u32 YUV_blockcount; extern u32 YUV_x_curr; extern u32 YUV_y_curr; extern u32 YUV_x_size; extern u32 YUV_y_size; //./core/hw/pvr/pvr_regs.o extern u8 pvr_regs[pvr_RegSize]; //./core/hw/pvr/spg.o extern u32 in_vblank; extern u32 clc_pvr_scanline; extern u32 pvr_cur_scanline; extern int render_end_schid; extern int vblank_schid; //./core/hw/pvr/ta.o extern u8 ta_fsm[2049]; //[2048] stores the current state extern u32 ta_fsm_cl; //./core/hw/pvr/ta_vtx.o //./core/rend/TexCache.o extern VArray2 vram; //./core/hw/sh4/sh4_mmr.o extern Array OnChipRAM; extern Array CCN; //CCN : 14 registers extern Array UBC; //UBC : 9 registers extern Array BSC; //BSC : 18 registers extern Array DMAC; //DMAC : 17 registers extern Array CPG; //CPG : 5 registers extern Array RTC; //RTC : 16 registers extern Array INTC; //INTC : 4 registers extern Array TMU; //TMU : 12 registers extern Array SCI; //SCI : 8 registers extern Array SCIF; //SCIF : 10 registers //./core/hw/sh4/sh4_mem.o extern VArray2 mem_b; //./core/hw/sh4/sh4_interrupts.o extern DECL_ALIGN(64) u16 InterruptEnvId[32]; extern DECL_ALIGN(64) u32 InterruptBit[32]; extern DECL_ALIGN(64) u32 InterruptLevelBit[16]; extern u32 interrupt_vpend; // Vector of pending interrupts extern u32 interrupt_vmask; // Vector of masked interrupts (-1 inhibits all interrupts) extern u32 decoded_srimask; // Vector of interrupts allowed by SR.IMSK (-1 inhibits all interrupts) //./core/hw/sh4/sh4_core_regs.o extern Sh4RCB* p_sh4rcb; //./core/hw/sh4/sh4_sched.o extern u64 sh4_sched_ffb; extern std::vector sch_list; //./core/hw/sh4/interpr/sh4_interpreter.o extern int aica_schid; extern int rtc_schid; //./core/hw/sh4/modules/serial.o extern SCIF_SCFSR2_type SCIF_SCFSR2; extern SCIF_SCSCR2_type SCIF_SCSCR2; //./core/hw/sh4/modules/bsc.o extern BSC_PDTRA_type BSC_PDTRA; //./core/hw/sh4/modules/tmu.o extern u32 tmu_shift[3]; extern u32 tmu_mask[3]; extern u64 tmu_mask64[3]; extern u32 old_mode[3]; extern int tmu_sched[3]; extern u32 tmu_ch_base[3]; extern u64 tmu_ch_base64[3]; //./core/hw/sh4/modules/ccn.o extern u32 CCN_QACR_TR[2]; //./core/hw/sh4/modules/mmu.o extern u32 ITLB_LRU_USE[64]; //./core/imgread/common.o extern u32 NullDriveDiscType; extern u8 q_subchannel[96]; //./core/nullDC.o extern unsigned FLASH_SIZE; extern unsigned BBSRAM_SIZE; extern unsigned BIOS_SIZE; extern unsigned RAM_SIZE; extern unsigned ARAM_SIZE; extern unsigned VRAM_SIZE; extern unsigned RAM_MASK; extern unsigned ARAM_MASK; extern unsigned VRAM_MASK; //./core/hw/naomi/naomi.o extern u32 GSerialBuffer; extern u32 BSerialBuffer; extern int GBufPos; extern int BBufPos; extern int GState; extern int BState; extern int GOldClk; extern int BOldClk; extern int BControl; extern int BCmd; extern int BLastCmd; extern int GControl; extern int GCmd; extern int GLastCmd; extern int SerStep; extern int SerStep2; extern unsigned char BSerial[]; extern unsigned char GSerial[]; /* What each part of the machine has had in a state since V21, in a block of * its own at the end: written by X_serialize_v21(), read by * X_unserialize_v21() - which answers false if the rest of the state cannot * be found after what it read - and, for a state from before V21, set to * what such a state implies by X_state_before_v21(). */ #define V21_PART(x) \ void x##_serialize_v21(void **data, unsigned int *total_size); \ bool x##_unserialize_v21(void **data, unsigned int *total_size); \ void x##_state_before_v21(void) V21_PART(aica); V21_PART(gdrom); V21_PART(maple); V21_PART(naomi); V21_PART(modem); V21_PART(reios); V21_PART(gdhle); V21_PART(nbios); V21_PART(pvr); #undef V21_PART // and since V22: which way the MMU is done, and the translations Windows CE's way of it keeps (fastmmu.cpp) void mmu_serialize_v22(void **data, unsigned int *total_size); bool mmu_unserialize_v22(void **data, unsigned int *total_size); void mmu_state_before_v22(void); // and whether the game has written to the framebuffer since it was last shown (Renderer_if.cpp) void pvr_serialize_v22(void **data, unsigned int *total_size); void pvr_unserialize_v22(void **data, unsigned int *total_size); void pvr_state_before_v22(void); /* and since V23: the NAOMI 2 geometry processor's clipping planes * (elan_host.cpp), the NAOMI's communication board and the I/O board's * program on its way (naomi.cpp), and the libretro layer's mouse motion * not yet read and sound not yet sent (libretro.cpp) */ void pvr_serialize_v23(void **data, unsigned int *total_size); bool pvr_unserialize_v23(void **data, unsigned int *total_size); void pvr_state_before_v23(void); void naomi_serialize_v23(void **data, unsigned int *total_size); bool naomi_unserialize_v23(void **data, unsigned int *total_size); void naomi_state_before_v23(void); void lr_serialize_v23(void **data, unsigned int *total_size); bool lr_unserialize_v23(void **data, unsigned int *total_size); void lr_state_before_v23(void); /* and since V24: the network device in use and what the modem or the * Broadband Adapter holds (netstate.cpp), the VMU's beep (vmu_beep.cpp), * the NAOMI's serial port and the card readers, touchscreen and hopper on * it (naomi_periph.h), and a System SP's own hardware (systemsp.cpp) */ void net_serialize_v24(void **data, unsigned int *total_size); bool net_unserialize_v24(void **data, unsigned int *total_size); void net_state_before_v24(void); void vmu_beep_serialize_v24(void **data, unsigned int *total_size); bool vmu_beep_unserialize_v24(void **data, unsigned int *total_size); void vmu_beep_state_before_v24(void); void naomi_periph_serialize_v24(void **data, unsigned int *total_size); bool naomi_periph_unserialize_v24(void **data, unsigned int *total_size); void naomi_periph_state_before_v24(void); void systemsp_serialize_v24(void **data, unsigned int *total_size); bool systemsp_unserialize_v24(void **data, unsigned int *total_size); void systemsp_state_before_v24(void); /* and since V25: the dinokich IC card reader of a System SP (systemsp.cpp), * what a Mushiking I/O board holds back (maple_devs.cpp), and the DIMM * board of a NAOMI GD-ROM game (gdcartridge.cpp) */ void systemsp_serialize_v25(void **data, unsigned int *total_size); bool systemsp_unserialize_v25(void **data, unsigned int *total_size); void systemsp_state_before_v25(void); void maple_jvs_serialize_v25(void **data, unsigned int *total_size); bool maple_jvs_unserialize_v25(void **data, unsigned int *total_size); void maple_jvs_state_before_v25(void); void naomi_gd_serialize_v25(void **data, unsigned int *total_size); bool naomi_gd_unserialize_v25(void **data, unsigned int *total_size); void naomi_gd_state_before_v25(void); /* and since V26: the NAOMI's two X76F100 serial flash chips, the main * board's and the ROM board's (naomi.cpp) */ void naomi_serial_serialize_v26(void **data, unsigned int *total_size); bool naomi_serial_unserialize_v26(void **data, unsigned int *total_size); void naomi_serial_state_before_v26(void); /* and since V27: what the racing and other I/O boards keep (maple_devs.cpp), * the MIDI force feedback board of the driving games (midiffb.cpp), and * F355's printer status (sb_mem.cpp) */ void maple_jvs_serialize_v27(void **data, unsigned int *total_size); bool maple_jvs_unserialize_v27(void **data, unsigned int *total_size); void maple_jvs_state_before_v27(void); void midiffb_serialize_v27(void **data, unsigned int *total_size); bool midiffb_unserialize_v27(void **data, unsigned int *total_size); void midiffb_state_before_v27(void); void naomi_printer_serialize_v27(void **data, unsigned int *total_size); bool naomi_printer_unserialize_v27(void **data, unsigned int *total_size); void naomi_printer_state_before_v27(void); /* and since V28: a light gun's transfer owed its end at vblank-out * (spg.cpp) and how many renders ago each TA context was last used * (ta_ctx.cpp) */ void pvr_serialize_v28(void **data, unsigned int *total_size); bool pvr_unserialize_v28(void **data, unsigned int *total_size); void pvr_state_before_v28(void); /* and since V29: SB_ADST, the AICA DMA's start register, which is read * through a function and so is not in the register block (aica_if.cpp) */ void aica_sb_serialize_v29(void **data, unsigned int *total_size); bool aica_sb_unserialize_v29(void **data, unsigned int *total_size); void aica_sb_state_before_v29(void); /* and since V30: the cards the NAOMI's card readers and IC card readers * keep out of the reader, as the game last wrote them (card_reader.cpp) */ void naomi_cards_serialize_v30(void **data, unsigned int *total_size); bool naomi_cards_unserialize_v30(void **data, unsigned int *total_size); void naomi_cards_state_before_v30(void); /* and since V31: the network DIMM board of a NAOMI GD-ROM game * (gdcartridge.cpp) */ void naomi_gd_serialize_v31(void **data, unsigned int *total_size); bool naomi_gd_unserialize_v31(void **data, unsigned int *total_size); void naomi_gd_state_before_v31(void); void tmu_state_loaded(void); /* The modem's part is this long on a Dreamcast (modem_serialize_v21() in * modem.cpp: 33 registers, the DSP's 4096 bytes, and 36 more), and a build * without the modem has to leave room for it all the same: a state is the * same whichever build made it. */ #define MODEM_V21_SIZE 4165 /* The drive's DMA event in a state, on a machine with no drive: there is * no such event there and its id is -1. (The id used to start out as 0, * which on an arcade board is some other device's event: that one's times * were written here, and read back over it.) */ static sched_list no_event = { NULL, 0, -1, -1 }; #define GDROM_EVENT (gdrom_sched >= 0 ? sch_list[gdrom_sched] : no_event) // (and the modem's, the same way) #define MODEM_EVENT (modem_sched >= 0 ? sch_list[modem_sched] : no_event) /* * Deserialization input-buffer bounds tracking. * * The libretro frontend hands retro_unserialize() a buffer and its length. * Historically that length was only used to disambiguate two legacy save-state * versions and never to bound the reads, so a truncated or malformed state * (netplay peer, runahead, on-disk corruption) walked memcpy() straight off the * end of the input buffer. dc_unserialize() now publishes the input base and * length here; ra_unserialize() refuses to read past the end and latches a * failure that propagates to the retro_unserialize() return value. */ static const u8 *unserialize_base = NULL; static size_t unserialize_limit = 0; static bool unserialize_failed = false; void ra_unserialize_init(const void *base, size_t limit) { unserialize_base = (const u8 *)base; unserialize_limit = limit; unserialize_failed = false; } bool ra_unserialize_failed(void) { return unserialize_failed; } /* A state whose contents cannot be this machine's: the load is given up. */ void ra_unserialize_fail(void) { unserialize_failed = true; } /* (Nothing to copy is nothing to do: a part of the machine this one does * not have - the Dreamcast's SRAM - is no memory and a size of 0, and no * memory is no pointer to copy from or to, even for none of it.) */ bool ra_serialize(const void *src, unsigned int src_size, void **dest, unsigned int *total_size) { if ( *dest != NULL && src_size != 0 ) { memcpy(*dest, src, src_size) ; *dest = ((unsigned char*)*dest) + src_size ; } *total_size += src_size ; return true ; } bool ra_unserialize(void *src, unsigned int src_size, void **dest, unsigned int *total_size) { if ( *dest != NULL && src_size != 0 ) { /* Reject any read that would run past the end of the frontend-supplied * input buffer. base==NULL means the limit is unknown (legacy callers); * fall back to the original unchecked behavior in that case only. */ if (unserialize_base != NULL) { size_t offset = (size_t)((const u8 *)*dest - unserialize_base); if (offset > unserialize_limit || src_size > unserialize_limit - offset) { unserialize_failed = true; /* Leave the destination zero-initialized rather than reading * out of bounds, and stop consuming input. */ memset(src, 0, src_size); *total_size += src_size ; return false ; } } memcpy(src, *dest, src_size) ; *dest = ((unsigned char*)*dest) + src_size ; } *total_size += src_size ; return true ; } /* Whether the state being read has @size more bytes at @data. */ bool ra_unserialize_has(const void *data, size_t size) { if (data == NULL || unserialize_base == NULL) return false; const size_t offset = (size_t)((const u8 *)data - unserialize_base); return offset <= unserialize_limit && size <= unserialize_limit - offset; } /* Main memory from a state. With a recompiler it is not copied over * blindly: what was compiled from memory that the state does not change is * kept (bm_LoadRam()). A state that stops short of it leaves memory zeroed, * as any read past the end does, and nothing compiled. */ static void unserialize_ram(void **data, unsigned int *total_size) { #if FEAT_SHREC != DYNAREC_NONE if (ra_unserialize_has(*data, mem_b.size)) { bm_LoadRam((const u8 *)*data); *data = (u8 *)*data + mem_b.size; *total_size += mem_b.size; return; } // (no telling what is in memory now: everything is unlocked, and the caller empties the code cache when a load fails) bm_Reset(); #endif LIBRETRO_USA(mem_b.data, mem_b.size); } #if FEAT_AREC == DYNAREC_JIT void FlushCache(); bool arm7_rec_same_code(const u8 *aram); #endif /* Sound memory from a state. What the sound processor's recompiler has made * is kept when the state has the same program wherever something was * compiled from it: with run-ahead, every time. */ static void unserialize_aram(void **data, unsigned int *total_size) { #if FEAT_AREC == DYNAREC_JIT if (!ra_unserialize_has(*data, aica_ram.size) || !arm7_rec_same_code((const u8 *)*data)) FlushCache(); #endif LIBRETRO_USA(aica_ram.data, aica_ram.size); } /* Video memory from a state. The textures made from it are kept where the * state has the same in their pages (vram_load()). */ static void unserialize_vram(void **data, unsigned int *total_size) { if (ra_unserialize_has(*data, vram.size)) { vram_load((const u8 *)*data); *data = (u8 *)*data + vram.size; *total_size += vram.size; return; } // (not all of it is there: the textures are made again from whatever this leaves) KillTex = true; LIBRETRO_USA(vram.data, vram.size); } bool register_serialize(Array& regs,void **data, unsigned int *total_size ) { int i = 0 ; for ( i = 0 ; i < regs.Size ; i++ ) { /* A register that is read through a function keeps the function's * address where the others keep their value. That is not state: * register_unserialize() reads it into a dummy. Writing its low * half out all the same put a host address, different in every * process, into the state for each such register; write zero. */ u32 value = (regs.data[i].flags & REG_RF) ? 0 : regs.data[i].data32 ; LIBRETRO_S(regs.data[i].flags) ; LIBRETRO_S(value) ; } return true ; } bool register_unserialize(Array& regs,void **data, unsigned int *total_size, u32 force_size = 0) { int i = 0 ; u32 dummy = 0 ; if (force_size == 0) force_size = regs.Size; for ( i = 0 ; i < force_size ; i++ ) { /* What kind of register it is - a plain value or one read and * written through functions, whose addresses are in its place - * is this build's to say, not the state's: flags taken from the * state made a function's address of a value, or a value of an * address. The value is taken when both agree it is one. */ u32 flags = 0 ; LIBRETRO_US(flags) ; if ( ! ((flags | regs.data[i].flags) & REG_RF) ) LIBRETRO_US(regs.data[i].data32) ; else LIBRETRO_US(dummy) ; } return true ; } /* Where each part begins in the state dc_serialize() wrote or counted last, * in the order written, with the part's name: a field written here by name, * or the function that writes a part of its own. Nothing in the core reads * it. It is for tools/determinism, which finds it by its symbol and names * the part two states that should be the same differ in. Two stores a part, * a few hundred parts. */ #define STATE_PARTS_MAX 384 extern "C" { u32 dc_state_parts; u32 dc_state_part_at[STATE_PARTS_MAX]; const char *dc_state_part_name[STATE_PARTS_MAX]; } static inline int state_part(const char *name, unsigned int at) { if (dc_state_parts < STATE_PARTS_MAX) { dc_state_part_name[dc_state_parts] = name; dc_state_part_at[dc_state_parts++] = at; } return 0; } #define STATE_PART(name) state_part(name, *total_size) /* (in dc_serialize() alone: the parts it writes itself, by name) */ #undef LIBRETRO_S #undef LIBRETRO_SA #undef LIBRETRO_SKIP #define LIBRETRO_S(v) (STATE_PART(#v), ra_serialize(&(v), sizeof(v), data, total_size)) #define LIBRETRO_SA(v_arr,num) (STATE_PART(#v_arr), ra_serialize((v_arr), sizeof((v_arr)[0]) * (num), data, total_size)) /* (what is left out is written as zeros: the same state is the same bytes, * whatever the frontend's memory had in it before) */ #define LIBRETRO_SKIP(size) do { STATE_PART("(nothing)"); if (*data) { memset(*data, 0, (size)); *(u8**)data += (size); } *total_size += (size); } while (false) bool dc_serialize(void **data, unsigned int *total_size) { int i = 0; int j = 0; serialize_version_enum version = VCUR_LIBRETRO; *total_size = 0 ; dc_state_parts = 0; //dc not initialized yet if ( p_sh4rcb == NULL ) return false ; LIBRETRO_S(version) ; LIBRETRO_S(aica_interr) ; LIBRETRO_S(aica_reg_L) ; LIBRETRO_S(e68k_out) ; LIBRETRO_S(e68k_reg_L) ; LIBRETRO_S(e68k_reg_M) ; LIBRETRO_SA(arm_Reg,RN_ARM_REG_COUNT); LIBRETRO_S(armIrqEnable); LIBRETRO_S(armFiqEnable); LIBRETRO_S(armMode); LIBRETRO_S(Arm7Enabled); /* The DSP's code as this machine's recompiler made it is no part of the * machine, and is not the same on another (nor where the core was loaded * at another address): zeros in its place. A load skips it. */ LIBRETRO_SKIP(sizeof(dsp.DynCode)); STATE_PART("dsp"), ra_serialize((u8 *)&dsp + sizeof(dsp.DynCode), sizeof(dsp) - sizeof(dsp.DynCode), data, total_size); for ( i = 0 ; i < 3 ; i++) { LIBRETRO_S(timers[i].c_step); LIBRETRO_S(timers[i].m_step); } LIBRETRO_SA(aica_ram.data,aica_ram.size) ; LIBRETRO_S(VREG); LIBRETRO_S(ARMRST); LIBRETRO_S(rtc_EN); LIBRETRO_SA(aica_reg,0x8000); STATE_PART("channel_serialize"), channel_serialize(data, total_size) ; libCore_CDDA_Detach(); LIBRETRO_SA(cdda_sector,CDDA_SIZE); LIBRETRO_S(cdda_index); STATE_PART("sb_regs"), register_serialize(sb_regs, data, total_size) ; LIBRETRO_S(SB_ISTNRM); LIBRETRO_S(SB_FFST_rc); LIBRETRO_S(SB_FFST); LIBRETRO_S(sys_nvmem_sram.size); LIBRETRO_S(sys_nvmem_sram.mask); LIBRETRO_SA(sys_nvmem_sram.data,sys_nvmem_sram.size); LIBRETRO_S(sys_nvmem_flash.size); LIBRETRO_S(sys_nvmem_flash.mask); LIBRETRO_S(sys_nvmem_flash.state); LIBRETRO_SA(sys_nvmem_flash.data,sys_nvmem_flash.size); LIBRETRO_S(GD_HardwareInfo) ; LIBRETRO_S(sns_asc); LIBRETRO_S(sns_ascq); LIBRETRO_S(sns_key); LIBRETRO_S(packet_cmd); LIBRETRO_S(set_mode_offset); LIBRETRO_S(read_params); LIBRETRO_S(packet_cmd); LIBRETRO_S(pio_buff); LIBRETRO_S(set_mode_offset); LIBRETRO_S(ata_cmd); LIBRETRO_S(cdda); LIBRETRO_S(gd_state); LIBRETRO_S(gd_disk_type); LIBRETRO_S(data_write_mode); LIBRETRO_S(DriveSel); LIBRETRO_S(Error); LIBRETRO_S(IntReason); LIBRETRO_S(Features); LIBRETRO_S(SecCount); LIBRETRO_S(SecNumber); LIBRETRO_S(GDStatus); LIBRETRO_S(GDByteCount); LIBRETRO_S(GDROM_TICK); LIBRETRO_SA(EEPROM,0x100); LIBRETRO_S(EEPROM_loaded); LIBRETRO_S(maple_ddt_pending_reset); STATE_PART("mcfg_SerializeDevices"), mcfg_SerializeDevices(data, total_size); LIBRETRO_SA(YUV_tempdata,512/4); LIBRETRO_S(YUV_dest); LIBRETRO_S(YUV_blockcount); LIBRETRO_S(YUV_x_curr); LIBRETRO_S(YUV_y_curr); LIBRETRO_S(YUV_x_size); LIBRETRO_S(YUV_y_size); LIBRETRO_SA(pvr_regs,pvr_RegSize); LIBRETRO_S(in_vblank); LIBRETRO_S(clc_pvr_scanline); LIBRETRO_S(pvr_cur_scanline); LIBRETRO_S(fb_w_cur); LIBRETRO_S(ta_fsm[2048]); LIBRETRO_S(ta_fsm_cl); STATE_PART("SerializeTAContext"), SerializeTAContext(data, total_size); LIBRETRO_SA(vram.data, vram.size); LIBRETRO_SA(OnChipRAM.data,OnChipRAM_SIZE); STATE_PART("CCN"), register_serialize(CCN, data, total_size) ; STATE_PART("UBC"), register_serialize(UBC, data, total_size) ; STATE_PART("BSC"), register_serialize(BSC, data, total_size) ; STATE_PART("DMAC"), register_serialize(DMAC, data, total_size) ; STATE_PART("CPG"), register_serialize(CPG, data, total_size) ; STATE_PART("RTC"), register_serialize(RTC, data, total_size) ; STATE_PART("INTC"), register_serialize(INTC, data, total_size) ; STATE_PART("TMU"), register_serialize(TMU, data, total_size) ; STATE_PART("SCI"), register_serialize(SCI, data, total_size) ; STATE_PART("SCIF"), register_serialize(SCIF, data, total_size) ; STATE_PART("icache.Serialize"), icache.Serialize(data, total_size); STATE_PART("ocache.Serialize"), ocache.Serialize(data, total_size); LIBRETRO_SA(mem_b.data, mem_b.size); LIBRETRO_SA(InterruptEnvId,32); LIBRETRO_SA(InterruptBit,32); LIBRETRO_SA(InterruptLevelBit,16); LIBRETRO_S(interrupt_vpend); LIBRETRO_S(interrupt_vmask); LIBRETRO_S(decoded_srimask); //default to nommu_full i = 3 ; if ( do_sqw_nommu == &do_sqw_nommu_area_3) i = 0 ; else if (do_sqw_nommu == &do_sqw_nommu_area_3_nonvmem) i = 1 ; else if (do_sqw_nommu==(sqw_fp*)&TAWriteSQ) i = 2 ; else if (do_sqw_nommu==&do_sqw_nommu_full) i = 3 ; LIBRETRO_S(i) ; LIBRETRO_SA((*p_sh4rcb).sq_buffer,64/8); //store these before unserializing and then restore after //void *getptr = &((*p_sh4rcb).cntx.sr.GetFull) ; //void *setptr = &((*p_sh4rcb).cntx.sr.SetFull) ; LIBRETRO_S((*p_sh4rcb).cntx); LIBRETRO_S(sh4_sched_ffb); LIBRETRO_S(sch_list[aica_schid].tag) ; LIBRETRO_S(sch_list[aica_schid].start) ; LIBRETRO_S(sch_list[aica_schid].end) ; LIBRETRO_S(sch_list[rtc_schid].tag) ; LIBRETRO_S(sch_list[rtc_schid].start) ; LIBRETRO_S(sch_list[rtc_schid].end) ; LIBRETRO_S(GDROM_EVENT.tag) ; LIBRETRO_S(GDROM_EVENT.start) ; LIBRETRO_S(GDROM_EVENT.end) ; LIBRETRO_S(sch_list[maple_sched].tag) ; LIBRETRO_S(sch_list[maple_sched].start) ; LIBRETRO_S(sch_list[maple_sched].end) ; LIBRETRO_S(sch_list[dma_sched_id].tag) ; LIBRETRO_S(sch_list[dma_sched_id].start) ; LIBRETRO_S(sch_list[dma_sched_id].end) ; for (int i = 0; i < 3; i++) { LIBRETRO_S(sch_list[tmu_sched[i]].tag) ; LIBRETRO_S(sch_list[tmu_sched[i]].start) ; LIBRETRO_S(sch_list[tmu_sched[i]].end) ; } LIBRETRO_S(sch_list[render_end_schid].tag) ; LIBRETRO_S(sch_list[render_end_schid].start) ; LIBRETRO_S(sch_list[render_end_schid].end) ; LIBRETRO_S(sch_list[vblank_schid].tag) ; LIBRETRO_S(sch_list[vblank_schid].start) ; LIBRETRO_S(sch_list[vblank_schid].end) ; LIBRETRO_S(settings.network.EmulateBBA); #ifdef ENABLE_MODEM if (settings.network.EmulateBBA) { STATE_PART("bba_Serialize"), bba_Serialize(data, total_size); } else { LIBRETRO_S(MODEM_EVENT.tag) ; LIBRETRO_S(MODEM_EVENT.start) ; LIBRETRO_S(MODEM_EVENT.end) ; } #else LIBRETRO_S(i); LIBRETRO_S(i); LIBRETRO_S(i); #endif LIBRETRO_S(SCIF_SCFSR2); LIBRETRO_S(SCIF_SCSCR2); LIBRETRO_S(BSC_PDTRA); LIBRETRO_SA(tmu_shift,3); LIBRETRO_SA(tmu_mask,3); LIBRETRO_SA(tmu_mask64,3); LIBRETRO_SA(old_mode,3); LIBRETRO_SA(tmu_ch_base,3); LIBRETRO_SA(tmu_ch_base64,3); LIBRETRO_SA(CCN_QACR_TR,2); LIBRETRO_S(UTLB); LIBRETRO_S(ITLB); LIBRETRO_S(sq_remap); LIBRETRO_S(ITLB_LRU_USE); LIBRETRO_S(NullDriveDiscType); LIBRETRO_SA(q_subchannel,96); LIBRETRO_S(FLASH_SIZE); LIBRETRO_S(BBSRAM_SIZE); LIBRETRO_S(BIOS_SIZE); LIBRETRO_S(RAM_SIZE); LIBRETRO_S(ARAM_SIZE); LIBRETRO_S(VRAM_SIZE); LIBRETRO_S(RAM_MASK); LIBRETRO_S(ARAM_MASK); LIBRETRO_S(VRAM_MASK); LIBRETRO_S(GSerialBuffer); LIBRETRO_S(BSerialBuffer); LIBRETRO_S(GBufPos); LIBRETRO_S(BBufPos); LIBRETRO_S(GState); LIBRETRO_S(BState); LIBRETRO_S(GOldClk); LIBRETRO_S(BOldClk); LIBRETRO_S(BControl); LIBRETRO_S(BCmd); LIBRETRO_S(BLastCmd); LIBRETRO_S(GControl); LIBRETRO_S(GCmd); LIBRETRO_S(GLastCmd); LIBRETRO_S(SerStep); LIBRETRO_S(SerStep2); LIBRETRO_SA(BSerial,69); LIBRETRO_SA(GSerial,69); LIBRETRO_S(reg_dimm_command); LIBRETRO_S(reg_dimm_offsetl); LIBRETRO_S(reg_dimm_parameterl); LIBRETRO_S(reg_dimm_parameterh); LIBRETRO_S(reg_dimm_status); LIBRETRO_SKIP(1); // NaomiDataRead LIBRETRO_S(settings.dreamcast.broadcast); LIBRETRO_S(settings.dreamcast.cable); LIBRETRO_S(settings.dreamcast.region); if (CurrentCartridge != NULL) STATE_PART("CurrentCartridge->Serialize"), CurrentCartridge->Serialize(data, total_size); STATE_PART("gd_hle_state.Serialize"), gd_hle_state.Serialize(data, total_size); settings.network.EmulateBBA = false; /* V14: the AICA RTC. Its per-second increment is already deterministic * (driven by the SH4 scheduler), but the initial value is seeded from the * host clock and was never serialized, so netplay peers and runahead * rollbacks diverged whenever a game read the clock. Carry it in the state * so it is synced along with everything else. */ LIBRETRO_S(RealTimeClock); /* V18: the answers of a Maple transfer that is under way, which are not * in the game's memory until it is over. */ LIBRETRO_S(maple_out_used); LIBRETRO_SA(maple_out, MAPLE_OUT_WORDS); /* V19: the drive still looking at a disc that has just been put in */ { /* (a machine with no drive has no such event: its id is -1, and * what was written here was whatever is in memory before the list) */ int swap_start = gd_swap_schid >= 0 ? sch_list[gd_swap_schid].start : -1; int swap_end = gd_swap_schid >= 0 ? sch_list[gd_swap_schid].end : -1; LIBRETRO_S(swap_start); LIBRETRO_S(swap_end); } /* V20: what a NAOMI 2 has that a NAOMI does not - the second PowerVR's * interrupts, and the geometry processor with its memory. */ if (settings.System == DC_PLATFORM_NAOMI2 && elan_ram) { elan_state_t elan; elan_get_state(&elan); LIBRETRO_S(SB_ISTNRM1); LIBRETRO_S(elan); LIBRETRO_SA(elan_ram, ELAN_RAM_SIZE); LIBRETRO_S(sch_list[elan_schid].start); LIBRETRO_S(sch_list[elan_schid].end); } /* V21: what each part of the machine was missing from a state. Every * part is the same size each time for the machine that is running; * the tile accelerator's, which is not, is last and is counted at its * largest. */ STATE_PART("aica_serialize_v21"), aica_serialize_v21(data, total_size); STATE_PART("gdrom_serialize_v21"), gdrom_serialize_v21(data, total_size); STATE_PART("maple_serialize_v21"), maple_serialize_v21(data, total_size); STATE_PART("naomi_serialize_v21"), naomi_serialize_v21(data, total_size); #ifdef ENABLE_MODEM STATE_PART("modem_serialize_v21"), modem_serialize_v21(data, total_size); #else if (settings.System == DC_PLATFORM_DREAMCAST) { static const u8 no_modem[MODEM_V21_SIZE] = { 0 }; LIBRETRO_SA(no_modem, MODEM_V21_SIZE); } #endif STATE_PART("reios_serialize_v21"), reios_serialize_v21(data, total_size); STATE_PART("gdhle_serialize_v21"), gdhle_serialize_v21(data, total_size); STATE_PART("nbios_serialize_v21"), nbios_serialize_v21(data, total_size); STATE_PART("pvr_serialize_v21"), pvr_serialize_v21(data, total_size); // V22 STATE_PART("pvr_serialize_v22"), pvr_serialize_v22(data, total_size); STATE_PART("mmu_serialize_v22"), mmu_serialize_v22(data, total_size); // V23 STATE_PART("pvr_serialize_v23"), pvr_serialize_v23(data, total_size); STATE_PART("naomi_serialize_v23"), naomi_serialize_v23(data, total_size); STATE_PART("lr_serialize_v23"), lr_serialize_v23(data, total_size); // V24 STATE_PART("net_serialize_v24"), net_serialize_v24(data, total_size); STATE_PART("vmu_beep_serialize_v24"), vmu_beep_serialize_v24(data, total_size); STATE_PART("naomi_periph_serialize_v24"), naomi_periph_serialize_v24(data, total_size); STATE_PART("systemsp_serialize_v24"), systemsp_serialize_v24(data, total_size); // V25 STATE_PART("systemsp_serialize_v25"), systemsp_serialize_v25(data, total_size); STATE_PART("maple_jvs_serialize_v25"), maple_jvs_serialize_v25(data, total_size); STATE_PART("naomi_gd_serialize_v25"), naomi_gd_serialize_v25(data, total_size); // V26 STATE_PART("naomi_serial_serialize_v26"), naomi_serial_serialize_v26(data, total_size); // V27 STATE_PART("maple_jvs_serialize_v27"), maple_jvs_serialize_v27(data, total_size); STATE_PART("midiffb_serialize_v27"), midiffb_serialize_v27(data, total_size); STATE_PART("naomi_printer_serialize_v27"), naomi_printer_serialize_v27(data, total_size); // V28 STATE_PART("pvr_serialize_v28"), pvr_serialize_v28(data, total_size); // V29 STATE_PART("aica_sb_serialize_v29"), aica_sb_serialize_v29(data, total_size); // V30 STATE_PART("naomi_cards_serialize_v30"), naomi_cards_serialize_v30(data, total_size); // V31 STATE_PART("naomi_gd_serialize_v31"), naomi_gd_serialize_v31(data, total_size); return true ; } #undef LIBRETRO_S #undef LIBRETRO_SA #undef LIBRETRO_SKIP #define LIBRETRO_S(v) ra_serialize(&(v), sizeof(v), data, total_size) #define LIBRETRO_SA(v_arr,num) ra_serialize((v_arr), sizeof((v_arr)[0]) * (num), data, total_size) #define LIBRETRO_SKIP(size) do { if (*data) *(u8**)data += (size); *total_size += (size); } while (false) /* The scheduler's events from a state. One that is waiting was asked for * at most a second ahead (sh4_sched_request()), and is called once its time * has come. An event from a bad state that would come later than that, or * whose time has gone by, is not called until the clock comes round to it - * some 21 seconds - and nothing it drives (a frame, sound) happens till * then: it is made due now. A state as this build saves it has none such. */ static void sched_events_loaded(void) { const u32 now = sh4_sched_now(); for (size_t i = 0; i < sch_list.size(); i++) { sched_list &ev = sch_list[i]; if (ev.end == -1) continue; const u32 asked = (u32)ev.end - (u32)ev.start; const u32 gone = now - (u32)ev.start; if (asked <= SH4_MAIN_CLOCK && gone <= asked) continue; ev.start = (int)now; ev.end = (int)now; if (ev.end == -1) ev.end++; } } bool dc_unserialize(void **data, unsigned int *total_size, size_t actual_data_size) { int i = 0; int j = 0; u8 dummy ; int dummy_int ; serialize_version_enum version = V1 ; *total_size = 0 ; /* Publish the input buffer bounds so ra_unserialize() can reject reads * past the end of a truncated or malformed state. *data points at the * start of the frontend-supplied buffer on entry. */ ra_unserialize_init(*data, actual_data_size); /* A state has the whole of the machine's three memories in it: one * that is smaller than those is not this machine's (or is cut short), * and is turned down before any of it has been put in place. */ if (actual_data_size < (size_t)RAM_SIZE + VRAM_SIZE + ARAM_SIZE) { ra_unserialize_fail(); return false; } LIBRETRO_US(version) ; //This normally isn't necessary - but we need some way to differentiate between save states //that were created after the format change and before the new format had a new version saved in it if (version == V1 && actual_data_size != 48324799 && actual_data_size != 48855967) version = V2 ; /* A state of a version this build has not heard of - a later build's - * is laid out in a way it cannot know. Nothing has been put in place * yet. */ if ((u32)version > (u32)VCUR_LIBRETRO) { ra_unserialize_fail(); return false; } LIBRETRO_US(aica_interr) ; LIBRETRO_US(aica_reg_L) ; LIBRETRO_US(e68k_out) ; LIBRETRO_US(e68k_reg_L) ; LIBRETRO_US(e68k_reg_M) ; LIBRETRO_USA(arm_Reg,RN_ARM_REG_COUNT); // (the sound CPU fetches whole words: an address that is not one's is never in its pc) arm_Reg[R15_ARM_NEXT].I &= ~3; LIBRETRO_US(armIrqEnable); LIBRETRO_US(armFiqEnable); LIBRETRO_US(armMode); LIBRETRO_US(Arm7Enabled); if (version < V9) { LIBRETRO_SKIP(256); // cpuBitsSet LIBRETRO_SKIP(1); // intState LIBRETRO_SKIP(1); // stopState LIBRETRO_SKIP(1); // holdState } /* The first thing in it is the code a DSP recompiler makes where it * keeps it there: this program's, with its addresses in it, and not * the one that saved the state's. It stays as it is; the rest is the * DSP's state. (Whether it still fits the program is the recompiler's * to find out: dsp.dyndirty is set after every load.) */ LIBRETRO_SKIP(sizeof(dsp.DynCode)); LIBRETRO_USA((u8 *)&dsp + sizeof(dsp.DynCode), sizeof(dsp) - sizeof(dsp.DynCode)); for ( i = 0 ; i < 3 ; i++) { LIBRETRO_US(timers[i].c_step); LIBRETRO_US(timers[i].m_step); } unserialize_aram(data, total_size); LIBRETRO_US(VREG); LIBRETRO_US(ARMRST); LIBRETRO_US(rtc_EN); if (version < V3) LIBRETRO_US(dummy_int); //dma_sched_id LIBRETRO_USA(aica_reg,0x8000); /* A timer counts down from its prescaler's step, 1 << md, to 1: none * other is ever in it, and 0 had it count for 2^32 samples. */ for ( i = 0 ; i < 3 ; i++) { timers[i].m_step = 1 << timers[i].data->md; if (timers[i].c_step < 1 || (u32)timers[i].c_step > timers[i].m_step) timers[i].c_step = timers[i].m_step; } if (version < V7) { LIBRETRO_SKIP(4 * 16); // volume_lut LIBRETRO_SKIP(4 * 256 + 768); // tl_lut. Due to a previous bug this is not 4 * (256 + 768) LIBRETRO_SKIP(4 * 64); // AEG_ATT_SPS LIBRETRO_SKIP(4 * 64); // AEG_DSR_SPS LIBRETRO_SKIP(2); // pl LIBRETRO_SKIP(2); // pr } channel_unserialize(data, total_size, version); libCore_CDDA_Detach(); LIBRETRO_USA(cdda_sector,CDDA_SIZE); LIBRETRO_US(cdda_index); if (version < V9) { LIBRETRO_SKIP(4 * 64); // mxlr LIBRETRO_SKIP(4); // samples_gen } register_unserialize(sb_regs, data, total_size) ; LIBRETRO_US(SB_ISTNRM); LIBRETRO_US(SB_FFST_rc); LIBRETRO_US(SB_FFST); /* size/mask describe the live, already-allocated nvmem buffers. Read the * stored values into temporaries and only copy up to the real allocation, * so a corrupt state whose size exceeds the buffer can't overflow data[] * (and can't clobber the live size/mask either). */ { u32 stored_size, stored_mask; LIBRETRO_US(stored_size); LIBRETRO_US(stored_mask); u32 copy = stored_size < sys_nvmem_sram.size ? stored_size : sys_nvmem_sram.size; LIBRETRO_USA(sys_nvmem_sram.data, copy); if (stored_size > copy) LIBRETRO_SKIP(stored_size - copy); } { u32 stored_size, stored_mask; LIBRETRO_US(stored_size); LIBRETRO_US(stored_mask); LIBRETRO_US(sys_nvmem_flash.state); u32 copy = stored_size < sys_nvmem_flash.size ? stored_size : sys_nvmem_flash.size; LIBRETRO_USA(sys_nvmem_flash.data, copy); if (stored_size > copy) LIBRETRO_SKIP(stored_size - copy); } LIBRETRO_US(GD_HardwareInfo) ; if (version < V3) LIBRETRO_US(dummy_int); //gdrom_sched LIBRETRO_US(sns_asc); LIBRETRO_US(sns_ascq); LIBRETRO_US(sns_key); LIBRETRO_US(packet_cmd); LIBRETRO_US(set_mode_offset); LIBRETRO_US(read_params); LIBRETRO_US(packet_cmd); // read_buff read_buff.cache_size = 0; if (version < V9) LIBRETRO_SKIP(4 + 4 + 2352 * 8192); LIBRETRO_US(pio_buff); LIBRETRO_US(set_mode_offset); LIBRETRO_US(ata_cmd); LIBRETRO_US(cdda); /* The status used to be a flag, playing or not, with whatever followed * it in its word: a state from then has something here that is none of * the four states, and is read as the flag. This was done to every * state, and made a drive that was paused, or had played to the end, * play. */ if ((u32)cdda.status > (u32)cdda_t::Terminated) cdda.status = ((u32)cdda.status & 0xFF) ? cdda_t::Playing : cdda_t::NoInfo; // the drive keeps four bits of repeat count (15: for ever) cdda.repeats &= 0xF; LIBRETRO_US(gd_state); LIBRETRO_US(gd_disk_type); LIBRETRO_US(data_write_mode); LIBRETRO_US(DriveSel); LIBRETRO_US(Error); LIBRETRO_US(IntReason); LIBRETRO_US(Features); LIBRETRO_US(SecCount); LIBRETRO_US(SecNumber); LIBRETRO_US(GDStatus); LIBRETRO_US(GDByteCount); LIBRETRO_US(GDROM_TICK); gdrom_state_loaded(); LIBRETRO_USA(EEPROM,0x100); { // (a flag is 0 or 1, whatever byte the state has for it) u8 flag = 0; LIBRETRO_US(flag); EEPROM_loaded = flag != 0; } if ( version == V1 ) { //V1 saved NaomiState which was a struct of 3 u8 variables LIBRETRO_US(dummy); LIBRETRO_US(dummy); LIBRETRO_US(dummy); } { u8 flag = 0; LIBRETRO_US(flag); maple_ddt_pending_reset = flag != 0; } if ( version == V1 ) { for (i = 0 ; i < 4 ; i++) for (j = 0 ; j < 6 ; j++) if ( MapleDevices[i][j] != 0 ) { MapleDeviceType devtype = MapleDevices[i][j]->get_device_type() ; mcfg_DestroyDevice(i,j) ; mcfg_Create(devtype, i, j); MapleDevices[i][j]->maple_unserialize(data, total_size) ; } } else mcfg_UnserializeDevices(data, total_size); if (version < V9) { LIBRETRO_SKIP(4); // FrameCOunt LIBRETRO_SKIP(1); // pend_rend } pend_rend = false; LIBRETRO_USA(YUV_tempdata,512/4); LIBRETRO_US(YUV_dest); LIBRETRO_US(YUV_blockcount); LIBRETRO_US(YUV_x_curr); LIBRETRO_US(YUV_y_curr); LIBRETRO_US(YUV_x_size); LIBRETRO_US(YUV_y_size); if (version < V9) LIBRETRO_SKIP(1); // fog_needs_update LIBRETRO_USA(pvr_regs,pvr_RegSize); LIBRETRO_US(in_vblank); LIBRETRO_US(clc_pvr_scanline); /* pvr_cur_scanline: current raster line. Not saved before V15, where the * post-load CalculateSync() reset it to 0; keep that behaviour for old * states so their (approximate) load path is unchanged. */ if (version >= V15) LIBRETRO_US(pvr_cur_scanline); else pvr_cur_scanline = 0; // (the line counter has ten bits; lines are counted up to this one from wherever the beam is) if (pvr_cur_scanline > 1023) pvr_cur_scanline = 0; fb_w_cur = 1; if (version < V9) { LIBRETRO_US(i); // pvr_numscanlines LIBRETRO_US(i); // prv_cur_scanline LIBRETRO_US(i); // vblk_cnt LIBRETRO_US(i); // Line_Cycles LIBRETRO_US(i); // Frame_Cycles if (version < V3) { LIBRETRO_US(dummy_int); //render_end_schid LIBRETRO_US(dummy_int); //vblank_schid LIBRETRO_US(dummy_int); //time_sync } LIBRETRO_SKIP(8); // speed_load_mspdf LIBRETRO_US(dummy_int); // mips_counter LIBRETRO_SKIP(8); // full_rps LIBRETRO_SKIP(4 * 256); // ta_type_lut LIBRETRO_SKIP(2048); // ta_fsm } if (version >= V12) LIBRETRO_US(fb_w_cur); else fb_w_cur = 1; LIBRETRO_US(ta_fsm[2048]); LIBRETRO_US(ta_fsm_cl); // (the state the list is in is one of eight, and it is an index into the table of what follows each) if (ta_fsm[2048] > 7) ta_fsm[2048] = 0; if (ta_fsm_cl > 7) ta_fsm_cl = 7; if (version < V9) { LIBRETRO_SKIP(1); // pal_needs_update LIBRETRO_SKIP(4 * 4); // _pal_rev_256 LIBRETRO_SKIP(4 * 64); // _pal_rev_16 LIBRETRO_SKIP(4 * 4); // pal_rev_256 LIBRETRO_SKIP(4 * 64); // pal_rev_16 LIBRETRO_SKIP(4 * 65536 * 3); // decoded_colors LIBRETRO_US(i); // tileclip_val LIBRETRO_SKIP(65536); // f32_su8_tbl LIBRETRO_US(i); // FaceBaseColor LIBRETRO_US(i); // FaceOffsColor LIBRETRO_US(i); // SFaceBaseColor LIBRETRO_US(i); // SFaceOffsColor LIBRETRO_US(i); // palette_index LIBRETRO_US(dummy); // KillTex LIBRETRO_SKIP(4 * 1024); // palette16_ram LIBRETRO_SKIP(4 * 1024); // palette32_ram LIBRETRO_SKIP(4 * 1024 * 8 * 2); // detwiddle } if (version >= V10) UnserializeTAContext(data, total_size, version); // (the state's version, not this build's: a state of before V12 has no render passes to read) unserialize_vram(data, total_size); LIBRETRO_USA(OnChipRAM.data,OnChipRAM_SIZE); register_unserialize(CCN, data, total_size, version < V4 ? 16 : 0) ; register_unserialize(UBC, data, total_size) ; register_unserialize(BSC, data, total_size) ; register_unserialize(DMAC, data, total_size) ; register_unserialize(CPG, data, total_size) ; register_unserialize(RTC, data, total_size) ; register_unserialize(INTC, data, total_size, version < V4 ? 4 : 0) ; register_unserialize(TMU, data, total_size) ; register_unserialize(SCI, data, total_size) ; register_unserialize(SCIF, data, total_size) ; if (version >= V9) icache.Unserialize(data, total_size); else icache.Reset(true); if (version >= V10) ocache.Unserialize(data, total_size); else ocache.Reset(true); unserialize_ram(data, total_size); if (version < V9) { u16 dum16; LIBRETRO_US(dum16); // IRLPriority } LIBRETRO_USA(InterruptEnvId,32); LIBRETRO_USA(InterruptBit,32); LIBRETRO_USA(InterruptLevelBit,16); LIBRETRO_US(interrupt_vpend); LIBRETRO_US(interrupt_vmask); LIBRETRO_US(decoded_srimask); LIBRETRO_US(i) ; if ( i == 0 ) do_sqw_nommu = &do_sqw_nommu_area_3 ; else if ( i == 1 ) do_sqw_nommu = &do_sqw_nommu_area_3_nonvmem ; else if ( i == 2 ) do_sqw_nommu = (sqw_fp*)&TAWriteSQ ; else if ( i == 3 ) do_sqw_nommu = &do_sqw_nommu_full ; LIBRETRO_USA((*p_sh4rcb).sq_buffer,64/8); LIBRETRO_US((*p_sh4rcb).cntx); if (version < V9) { LIBRETRO_US(dummy_int); // old_rm LIBRETRO_US(dummy_int); // old_dn } LIBRETRO_US(sh4_sched_ffb); if (version < V9) { LIBRETRO_SKIP(4); // sh4_sched_intr if (version < V3) LIBRETRO_US(dummy_int); // sh4_sched_next_id } LIBRETRO_US(dummy_int) ; // the tag: what the device registered with, not state LIBRETRO_US(sch_list[aica_schid].start) ; LIBRETRO_US(sch_list[aica_schid].end) ; LIBRETRO_US(dummy_int) ; // the tag: what the device registered with, not state LIBRETRO_US(sch_list[rtc_schid].start) ; LIBRETRO_US(sch_list[rtc_schid].end) ; LIBRETRO_US(dummy_int) ; // the tag: what the device registered with, not state LIBRETRO_US(GDROM_EVENT.start) ; LIBRETRO_US(GDROM_EVENT.end) ; no_event.tag = 0; no_event.start = no_event.end = -1; LIBRETRO_US(dummy_int) ; // the tag: what the device registered with, not state LIBRETRO_US(sch_list[maple_sched].start) ; LIBRETRO_US(sch_list[maple_sched].end) ; LIBRETRO_US(dummy_int) ; // the tag: what the device registered with, not state LIBRETRO_US(sch_list[dma_sched_id].start) ; LIBRETRO_US(sch_list[dma_sched_id].end) ; for (int i = 0; i < 3; i++) { LIBRETRO_US(dummy_int) ; // the tag: what the device registered with, not state LIBRETRO_US(sch_list[tmu_sched[i]].start) ; LIBRETRO_US(sch_list[tmu_sched[i]].end) ; } LIBRETRO_US(dummy_int) ; // the tag: what the device registered with, not state LIBRETRO_US(sch_list[render_end_schid].start) ; LIBRETRO_US(sch_list[render_end_schid].end) ; LIBRETRO_US(dummy_int) ; // the tag: what the device registered with, not state LIBRETRO_US(sch_list[vblank_schid].start) ; LIBRETRO_US(sch_list[vblank_schid].end) ; spg_beam_forget(); if (version < V9) { LIBRETRO_US(dummy_int); // sch_list[time_sync].tag LIBRETRO_US(dummy_int); // sch_list[time_sync].start LIBRETRO_US(dummy_int); // sch_list[time_sync].end } { /* Whether the machine has a broadband adapter in place of the * modem. This build has none: a state that says it has one has * the adapter's contents next, which nothing here can take, and * the flag left set had every access to the modem go to an adapter * that was never set up. */ u8 bba = 0; if (version >= V13) LIBRETRO_US(bba); settings.network.EmulateBBA = false; if (bba != 0) { ra_unserialize_fail(); return false; } } if ( version >= V2 ) { #ifdef ENABLE_MODEM LIBRETRO_US(dummy_int) ; // the tag: what the device registered with, not state LIBRETRO_US(MODEM_EVENT.start) ; LIBRETRO_US(MODEM_EVENT.end) ; no_event.tag = 0; no_event.start = no_event.end = -1; #else LIBRETRO_US(dummy_int); LIBRETRO_US(dummy_int); LIBRETRO_US(dummy_int); #endif } if (version < V3) { LIBRETRO_US(dummy_int); //aica_sched LIBRETRO_US(dummy_int); //rtc_sched } LIBRETRO_US(SCIF_SCFSR2); if (version < V9) { LIBRETRO_SKIP(1); // SCIF_SCFRDR2 LIBRETRO_US(dummy_int); // SCIF_SCFDR2 } else if (version >= V11) LIBRETRO_US(SCIF_SCSCR2); LIBRETRO_US(BSC_PDTRA); LIBRETRO_USA(tmu_shift,3); LIBRETRO_USA(tmu_mask,3); LIBRETRO_USA(tmu_mask64,3); LIBRETRO_USA(old_mode,3); if (version < V3) { //tmu_sched*3 LIBRETRO_US(dummy_int); LIBRETRO_US(dummy_int); LIBRETRO_US(dummy_int); } LIBRETRO_USA(tmu_ch_base,3); LIBRETRO_USA(tmu_ch_base64,3); LIBRETRO_USA(CCN_QACR_TR,2); // not the routine the state names, which may be from before each queue went by its own register CCN_QACR_select(); if (version < V6) { for (int i = 0; i < 64; i++) { LIBRETRO_US(UTLB[i].Address); LIBRETRO_US(UTLB[i].Data); } for (int i = 0; i < 4; i++) { LIBRETRO_US(ITLB[i].Address); LIBRETRO_US(ITLB[i].Data); } } else { LIBRETRO_US(UTLB); LIBRETRO_US(ITLB); } if (version >= V11) LIBRETRO_US(sq_remap); { /* Which ITLB entry each LRU value replaces: a table this build * works out once (MMU_init), not state. One from the state could * point past the four entries. */ u32 lru_use[64]; LIBRETRO_USA(lru_use, 64); } LIBRETRO_US(NullDriveDiscType); LIBRETRO_USA(q_subchannel,96); { /* How much memory of each kind the machine has, and the masks for * it. These are not state: they are what the machine that is * running was set up with, and its memory is that large whatever * a state says. Taking them from the state had every access * masked for a size the memory may not have. A state made on a * machine with other main, video or sound memory is another * machine's, with all that came before this laid out otherwise, * and is not loaded. */ unsigned sizes[9]; LIBRETRO_USA(sizes, 9); if (sizes[3] != RAM_SIZE || sizes[4] != ARAM_SIZE || sizes[5] != VRAM_SIZE) { ra_unserialize_fail(); return false; } } if (version < V9) { LIBRETRO_US(dummy_int); // naomi_updates if (version < V4) { LIBRETRO_US(dummy_int); // RomPioOffset LIBRETRO_US(dummy_int); // DmaOffset LIBRETRO_US(dummy_int); // DmaCount } LIBRETRO_US(dummy_int); // BoardID } LIBRETRO_US(GSerialBuffer); LIBRETRO_US(BSerialBuffer); LIBRETRO_US(GBufPos); LIBRETRO_US(BBufPos); LIBRETRO_US(GState); LIBRETRO_US(BState); LIBRETRO_US(GOldClk); LIBRETRO_US(BOldClk); LIBRETRO_US(BControl); LIBRETRO_US(BCmd); LIBRETRO_US(BLastCmd); LIBRETRO_US(GControl); LIBRETRO_US(GCmd); LIBRETRO_US(GLastCmd); LIBRETRO_US(SerStep); LIBRETRO_US(SerStep2); LIBRETRO_USA(BSerial,69); LIBRETRO_USA(GSerial,69); LIBRETRO_US(reg_dimm_command); LIBRETRO_US(reg_dimm_offsetl); LIBRETRO_US(reg_dimm_parameterl); LIBRETRO_US(reg_dimm_parameterh); LIBRETRO_US(reg_dimm_status); LIBRETRO_SKIP(1); // NaomiDataRead if (version < V4) { LIBRETRO_US(dummy_int); // NAOMI_ROM_OFFSETH LIBRETRO_US(dummy_int); // NAOMI_ROM_OFFSETL LIBRETRO_US(dummy_int); // NAOMI_ROM_DATA LIBRETRO_US(dummy_int); // NAOMI_DMA_OFFSETH LIBRETRO_US(dummy_int); // NAOMI_DMA_OFFSETL LIBRETRO_US(dummy_int); // NAOMI_DMA_COUNT LIBRETRO_US(dummy_int); // NAOMI_BOARDID_WRITE LIBRETRO_US(dummy_int); // NAOMI_BOARDID_READ LIBRETRO_US(dummy_int); // NAOMI_COMM_OFFSET LIBRETRO_US(dummy_int); // NAOMI_COMM_DATA } if (version < V9) { LIBRETRO_US(dummy_int); // cycle_counter LIBRETRO_US(dummy_int); // idxnxx #if FEAT_SHREC != DYNAREC_NONE LIBRETRO_SKIP(sizeof(state_t)); // state LIBRETRO_US(dummy_int); // div_som_reg1 LIBRETRO_US(dummy_int); // div_som_reg2 LIBRETRO_US(dummy_int); // div_som_reg3 LIBRETRO_US(dummy_int); // LastAddr LIBRETRO_US(dummy_int); // LastAddr_min LIBRETRO_SKIP(1024); // block_hash #endif // RegisterRead, RegisterWrite for (int i = 0; i < sh4_reg_count; i++) { LIBRETRO_US(dummy_int); LIBRETRO_US(dummy_int); } LIBRETRO_US(dummy_int); // fallback_blocks LIBRETRO_US(dummy_int); // total_blocks LIBRETRO_US(dummy_int); // REMOVED_OPS if (version < V5) { LIBRETRO_US(dummy_int); // u16 kcode[4] LIBRETRO_US(dummy_int); LIBRETRO_US(dummy_int); // u8 rt[4] LIBRETRO_US(dummy_int); // u8 lt[4] LIBRETRO_US(dummy_int); // u32 vks[4] LIBRETRO_US(dummy_int); LIBRETRO_US(dummy_int); LIBRETRO_US(dummy_int); LIBRETRO_US(dummy_int); // s8 joyx[4] LIBRETRO_US(dummy_int); // s8 joyy[4] } } if (version >= V3) { LIBRETRO_US(settings.dreamcast.broadcast); LIBRETRO_US(settings.dreamcast.cable); LIBRETRO_US(settings.dreamcast.region); } if (version >= V4 && CurrentCartridge != NULL) { CurrentCartridge->Unserialize(data, total_size); } if (version >= V7) gd_hle_state.Unserialize(data, total_size); if (version >= V14) LIBRETRO_US(RealTimeClock); /* Before V18 a transfer's answers were in memory from its start, and * so they are in such a state: there is nothing waiting. */ maple_out_used = 0; if (version >= V18) { LIBRETRO_US(maple_out_used); LIBRETRO_USA(maple_out, MAPLE_OUT_WORDS); if (maple_out_used > MAPLE_OUT_WORDS) maple_out_used = 0; } // Before V19 a disc put in was ready at once: there is never a look under way { /* (on a machine with no drive there is no such event, its id is * -1, and this was written before the start of the list) */ int swap_start = -1; int swap_end = -1; if (version >= V19) { LIBRETRO_US(swap_start); LIBRETRO_US(swap_end); } if (gd_swap_schid >= 0) { if (version >= V19) sch_list[gd_swap_schid].start = swap_start; sch_list[gd_swap_schid].end = swap_end; } } if (version >= V20 && settings.System == DC_PLATFORM_NAOMI2 && elan_ram) { elan_state_t elan; LIBRETRO_US(SB_ISTNRM1); LIBRETRO_US(elan); LIBRETRO_USA(elan_ram, ELAN_RAM_SIZE); LIBRETRO_US(sch_list[elan_schid].start); LIBRETRO_US(sch_list[elan_schid].end); elan_set_state(&elan); } if (version >= V21) { bool ok = aica_unserialize_v21(data, total_size); ok = ok && gdrom_unserialize_v21(data, total_size); ok = ok && maple_unserialize_v21(data, total_size); ok = ok && naomi_unserialize_v21(data, total_size); #ifdef ENABLE_MODEM ok = ok && modem_unserialize_v21(data, total_size); #else if (settings.System == DC_PLATFORM_DREAMCAST) LIBRETRO_SKIP(MODEM_V21_SIZE); #endif ok = ok && reios_unserialize_v21(data, total_size); ok = ok && gdhle_unserialize_v21(data, total_size); ok = ok && nbios_unserialize_v21(data, total_size); ok = ok && pvr_unserialize_v21(data, total_size); if (!ok) ra_unserialize_fail(); } else { aica_state_before_v21(); gdrom_state_before_v21(); maple_state_before_v21(); naomi_state_before_v21(); #ifdef ENABLE_MODEM modem_state_before_v21(); #endif reios_state_before_v21(); gdhle_state_before_v21(); nbios_state_before_v21(); pvr_state_before_v21(); } if (version >= V22) { pvr_unserialize_v22(data, total_size); if (!mmu_unserialize_v22(data, total_size)) ra_unserialize_fail(); } else { pvr_state_before_v22(); mmu_state_before_v22(); } if (version >= V23) { if (!pvr_unserialize_v23(data, total_size) || !naomi_unserialize_v23(data, total_size) || !lr_unserialize_v23(data, total_size)) ra_unserialize_fail(); } else { pvr_state_before_v23(); naomi_state_before_v23(); lr_state_before_v23(); } if (version >= V24) { if (!net_unserialize_v24(data, total_size) || !vmu_beep_unserialize_v24(data, total_size) || !naomi_periph_unserialize_v24(data, total_size) || !systemsp_unserialize_v24(data, total_size)) ra_unserialize_fail(); } else { net_state_before_v24(); vmu_beep_state_before_v24(); naomi_periph_state_before_v24(); systemsp_state_before_v24(); } if (version >= V25) { if (!systemsp_unserialize_v25(data, total_size) || !maple_jvs_unserialize_v25(data, total_size) || !naomi_gd_unserialize_v25(data, total_size)) ra_unserialize_fail(); } else { systemsp_state_before_v25(); maple_jvs_state_before_v25(); naomi_gd_state_before_v25(); } if (version >= V26) { if (!naomi_serial_unserialize_v26(data, total_size)) ra_unserialize_fail(); } else naomi_serial_state_before_v26(); if (version >= V27) { if (!maple_jvs_unserialize_v27(data, total_size) || !midiffb_unserialize_v27(data, total_size) || !naomi_printer_unserialize_v27(data, total_size)) ra_unserialize_fail(); } else { maple_jvs_state_before_v27(); midiffb_state_before_v27(); naomi_printer_state_before_v27(); } if (version >= V28) { if (!pvr_unserialize_v28(data, total_size)) ra_unserialize_fail(); } else pvr_state_before_v28(); if (version >= V29) { if (!aica_sb_unserialize_v29(data, total_size)) ra_unserialize_fail(); } else aica_sb_state_before_v29(); if (version >= V30) { if (!naomi_cards_unserialize_v30(data, total_size)) ra_unserialize_fail(); } else naomi_cards_state_before_v30(); if (version >= V31) { if (!naomi_gd_unserialize_v31(data, total_size)) ra_unserialize_fail(); } else naomi_gd_state_before_v31(); sched_events_loaded(); /* A sound DMA is under way only while its end is to come: a start is * not taken while one is, and SB_ADST set with no end on its way would * have none taken again. */ if ((SB_ADST & 1) && !sh4_sched_is_scheduled(dma_sched_id)) SB_ADST = 0; /* The timers: what follows from their registers is worked out again, * for a state of any version, and their events are asked for anew. */ tmu_state_loaded(); /* Fail the whole load if any read was rejected for running past the end * of the input buffer. Callers (retro_unserialize) then bail cleanly * instead of continuing on partially-garbage state. */ return !ra_unserialize_failed() ; }