--- generator/main/state.c 2020/03/04 04:46:43 1.1 +++ generator/main/state.c 2020/03/04 04:47:21 1.1.1.4 @@ -18,66 +18,29 @@ #include "cpuz80.h" #include "vdp.h" #include "gensound.h" -#include "gensoundi.h" #include "fm.h" +typedef struct _t_statelist { + struct _t_statelist *next; + char *mod; + char *name; + uint8 instance; + uint32 bytes; + uint32 size; + uint8 *data; +} t_statelist; + +FILE *state_outputfile; /* the file handle to place data blocks */ +uint8 state_transfermode; /* 0 = save, 1 = load */ +uint8 state_major; /* major version */ +uint8 state_minor; /* minor version */ +t_statelist *state_statelist; /* loaded state */ + /* * NB: * states are only loaded/saved at the end of the frame - * all local values are stored in network / 68k format (big endian) */ -struct savestate { - /* must ensure everything is aligned as expected */ - char s_majorver[1]; - char s_minorver[1]; - char reserved1[2]; /* align */ - char s_ram[0x10000]; - char s_vram[0x10000]; - char s_z80ram[0x2000]; - char s_cram[0x80]; - char s_vsram[0x50]; - char s_68k_regs[4*16]; - char s_68k_pc[4]; - char s_68k_sp[4]; - char s_68k_sr[2]; - char s_68k_stop[1]; - char s_68k_pending[1]; - char s_z80_af[2]; - char s_z80_bc[2]; - char s_z80_de[2]; - char s_z80_hl[2]; - char s_z80_afprime[2]; - char s_z80_bcprime[2]; - char s_z80_deprime[2]; - char s_z80_hlprime[2]; - char s_z80_ix[2]; - char s_z80_iy[2]; - char s_z80_sp[2]; - char s_z80_pc[2]; - char s_z80_i[1]; - char s_z80_r[1]; - char s_z80_active[1]; - char s_z80_halted[1]; /* was reserved in 0.31 */ - char s_z80_bank[4]; - char s_fmregs1[256]; - char s_fmregs2[256]; - char s_fmaddr1[1]; - char s_fmaddr2[1]; - char s_vdp_regs[25]; - char s_z80_im[1]; /* was reserved in 0.31 */ - char s_vdp_pal[1]; - char s_vdp_overseas[1]; - char s_vdp_ctrlflag[1]; - char s_vdp_code[1]; - char s_vdp_first[2]; /* possibly only top two bits required here */ - char s_vdp_second[2]; /* used for unterminated ctrl writes */ - char s_vdp_dmabytes[4]; - char s_vdp_address[2]; - char s_z80_iff1[1]; /* was reserved in 0.31 */ - char s_z80_iff2[1]; /* was reserved in 0.31 */ -}; - /*** state_date - return the modification date or 0 for non-existant ***/ time_t state_date(const int slot) @@ -97,226 +60,408 @@ time_t state_date(const int slot) int state_load(const int slot) { char filename[256]; - struct savestate *ssp; - char *ss; - struct stat statbuf; - FILE *f, *a; - int i; snprintf(filename, sizeof(filename), "%s.gt%d", gen_leafname, slot); - if (stat(filename, &statbuf) != 0) - return -1; + return state_loadfile(filename); +} - if ((ss = malloc(statbuf.st_size)) == NULL) { - LOG_CRITICAL(("Failed to allocate memory whilst loading '%s'", - filename)); - return -1; +/*** state_save - save to the given slot ***/ + +int state_save(const int slot) +{ + char filename[256]; + + snprintf(filename, sizeof(filename), "%s.gt%d", gen_leafname, slot); + return state_savefile(filename); +} + +void state_transfer8(const char *mod, const char *name, uint8 instance, + uint8 *data, uint32 size) +{ + t_statelist *l; + uint8 buf[4]; + uint32 i; + + if (state_transfermode == 0) { + /* save */ + fwrite(mod, strlen(mod)+1, 1, state_outputfile); + fwrite(name, strlen(name)+1, 1, state_outputfile); + buf[0] = instance; + buf[1] = 1; /* bytes per object */ + fwrite(buf, 2, 1, state_outputfile); + buf[0] = (size >> 24) & 0xff; + buf[1] = (size >> 16) & 0xff; + buf[2] = (size >> 8) & 0xff; + buf[3] = size & 0xff; + fwrite(buf, 4, 1, state_outputfile); + fwrite(data, size, 1, state_outputfile); + } else { + /* load */ + for (l = state_statelist; l; l = l->next) { + if (!strcasecmp(l->mod, mod) && !strcasecmp(l->name, name) && + l->instance == instance && l->size == size && l->bytes == 1) { + for (i = 0; i < size; i++) + data[i] = l->data[i]; + LOG_VERBOSE(("Loaded %s %s (%d)", mod, name, instance)); + break; + } + } + if (l == NULL) { + LOG_CRITICAL(("bad %s/%s\n", mod, name)); + memset(data, 0, size); + } } - if ((f = fopen(filename, "rb")) == NULL) { - LOG_CRITICAL(("Failed to open '%s': %s", filename, strerror(errno))); - return -1; +} + +void state_transfer16(const char *mod, const char *name, uint8 instance, + uint16 *data, uint32 size) +{ + t_statelist *l; + uint8 buf[4]; + uint32 i; + + if (state_transfermode == 0) { + /* save */ + fwrite(mod, strlen(mod)+1, 1, state_outputfile); + fwrite(name, strlen(name)+1, 1, state_outputfile); + buf[0] = instance; + buf[1] = 2; /* bytes per object */ + fwrite(buf, 2, 1, state_outputfile); + buf[0] = (size >> 24) & 0xff; + buf[1] = (size >> 16) & 0xff; + buf[2] = (size >> 8) & 0xff; + buf[3] = size & 0xff; + fwrite(buf, 4, 1, state_outputfile); + for (i = 0; i < size; i++) { + buf[0] = (data[i] >> 8) & 0xff; + buf[1] = data[i] & 0xff; + fwrite(buf, 2, 1, state_outputfile); + } + } else { + /* load */ + for (l = state_statelist; l; l = l->next) { + if (!strcasecmp(l->mod, mod) && !strcasecmp(l->name, name) && + l->instance == instance && l->size == size && l->bytes == 2) { + for (i = 0; i < size; i++) + data[i] = ((((uint8 *)l->data)[(i << 1)] << 8) | + (((uint8 *)l->data)[(i << 1) + 1])); + LOG_VERBOSE(("Loaded %s %s (%d)", mod, name, instance)); + break; + } + } + if (l == NULL) { + LOG_CRITICAL(("bad %s/%s\n", mod, name)); + memset(data, 0, size * 2); + } } - if (fread(ss, statbuf.st_size, 1, f) != 1) { - if (feof(f)) { - LOG_CRITICAL(("EOF whilst reading GTR file '%s'", filename)); - return -1; +} + +void state_transfer32(const char *mod, const char *name, uint8 instance, + uint32 *data, uint32 size) +{ + t_statelist *l; + uint8 buf[4]; + uint32 i; + + if (state_transfermode == 0) { + /* save */ + fwrite(mod, strlen(mod)+1, 1, state_outputfile); + fwrite(name, strlen(name)+1, 1, state_outputfile); + buf[0] = instance; + buf[1] = 4; /* bytes per object */ + fwrite(buf, 2, 1, state_outputfile); + buf[0] = (size >> 24) & 0xff; + buf[1] = (size >> 16) & 0xff; + buf[2] = (size >> 8) & 0xff; + buf[3] = size & 0xff; + fwrite(buf, 4, 1, state_outputfile); + for (i = 0; i < size; i++) { + buf[0] = (data[i] >> 24) & 0xff; + buf[1] = (data[i] >> 16) & 0xff; + buf[2] = (data[i] >> 8) & 0xff; + buf[3] = data[i] & 0xff; + fwrite(buf, 4, 1, state_outputfile); + } + } else { + /* load */ + for (l = state_statelist; l; l = l->next) { + if (!strcasecmp(l->mod, mod) && !strcasecmp(l->name, name) && + l->instance == instance && l->size == size && l->bytes == 4) { + for (i = 0; i < size; i++) + data[i] = ((((uint8 *)l->data)[(i << 2)] << 24) | + (((uint8 *)l->data)[(i << 2) + 1] << 16) | + (((uint8 *)l->data)[(i << 2) + 2] << 8) | + (((uint8 *)l->data)[(i << 2) + 3])); + LOG_VERBOSE(("Loaded %s %s (%d)", mod, name, instance)); + break; + } + } + if (l == NULL) { + LOG_CRITICAL(("bad %s/%s\n", mod, name)); + memset(data, 0, size * 4); } - LOG_CRITICAL(("Error whilst reading GTR file '%s': %s", filename, - strerror(errno))); - return -1; } - fclose(f); +} - ssp = (struct savestate *)ss; +/*** state_dotransfer - do transfer of data, either save or load ***/ - if (ssp->s_majorver[0] != 1) { - LOG_CRITICAL(("GTR file '%s' is version %d, and we're version 1", - filename, ssp->s_majorver)); - return -1; - } - if (statbuf.st_size != sizeof(struct savestate)) { - LOG_CRITICAL(("GTR file '%s' is corrupt - it is not %d bytes long", - filename, sizeof(struct savestate))); - return -1; - } +static void state_dotransfer(unsigned int mode) +{ + uint8 i8, i8b; + uint16 i16; - gen_reset(); - memcpy(cpu68k_ram, ssp->s_ram, sizeof(ssp->s_ram)); - memcpy(vdp_vram, ssp->s_vram, sizeof(ssp->s_vram)); - memcpy(cpuz80_ram, ssp->s_z80ram, sizeof(ssp->s_z80ram)); - memcpy(vdp_cram, ssp->s_cram, sizeof(ssp->s_cram)); - memcpy(vdp_vsram, ssp->s_vsram, sizeof(ssp->s_vsram)); - for (i = 0; i < 16; i++) - regs.regs[i] = LOCENDIAN32(((uint32 *)ssp->s_68k_regs)[i]); - regs.pc = LOCENDIAN32(*(uint32 *)ssp->s_68k_pc); - regs.sp = LOCENDIAN32(*(uint32 *)ssp->s_68k_sp); - regs.sr.sr_int = LOCENDIAN16(*(uint16 *)ssp->s_68k_sr); - regs.stop = ssp->s_68k_stop[0]; - regs.pending = ssp->s_68k_pending[0]; + (void)i8b; + state_transfermode = mode; /* 0 = save, 1 = load */ + state_transfer8("ver", "major", 0, &state_major, 1); + state_transfer8("ver" ,"minor", 0, &state_minor, 1); + state_transfer8("vdp", "vram", 0, vdp_vram, LEN_VRAM); + state_transfer8("vdp", "cram", 0, vdp_cram, LEN_CRAM); + state_transfer8("vdp", "vsram", 0, vdp_vsram, LEN_VSRAM); + state_transfer8("vdp", "regs", 0, vdp_reg, 25); + state_transfer8("vdp", "pal", 0, &vdp_pal, 1); + state_transfer8("vdp", "overseas", 0, &vdp_overseas, 1); + state_transfer8("vdp", "ctrlflag", 0, &vdp_ctrlflag, 1); + /* this cast is probably very bad */ + state_transfer8("vdp", "code", 0, (uint8 *)&vdp_code, 1); + state_transfer16("vdp", "first", 0, &vdp_first, 1); + state_transfer16("vdp", "second", 0, &vdp_second, 1); + state_transfer32("vdp", "dmabytes", 0, &vdp_dmabytes, 1); + state_transfer16("vdp", "address", 0, &vdp_address, 1); + state_transfer8("68k", "ram", 0, cpu68k_ram, 0x10000); + state_transfer32("68k", "regs", 0, regs.regs, 16); + state_transfer32("68k", "pc", 0, ®s.pc, 1); + state_transfer32("68k", "sp", 0, ®s.sp, 1); + state_transfer16("68k", "sr", 0, ®s.sr.sr_int, 1); + state_transfer16("68k", "stop", 0, ®s.stop, 1); + state_transfer16("68k", "pending", 0, ®s.pending, 1); + state_transfer8("z80", "ram", 0, cpuz80_ram, LEN_SRAM); + state_transfer8("z80", "active", 0, &cpuz80_active, 1); + state_transfer8("z80", "resetting", 0, &cpuz80_resetting, 1); + state_transfer32("z80", "bank", 0, &cpuz80_bank, 1); #ifdef RAZE - z80_set_reg(Z80_REG_AF, LOCENDIAN16(*(uint16 *)ssp->s_z80_af)); - z80_set_reg(Z80_REG_BC, LOCENDIAN16(*(uint16 *)ssp->s_z80_bc)); - z80_set_reg(Z80_REG_DE, LOCENDIAN16(*(uint16 *)ssp->s_z80_de)); - z80_set_reg(Z80_REG_HL, LOCENDIAN16(*(uint16 *)ssp->s_z80_hl)); - z80_set_reg(Z80_REG_AF2, LOCENDIAN16(*(uint16 *)ssp->s_z80_afprime)); - z80_set_reg(Z80_REG_BC2, LOCENDIAN16(*(uint16 *)ssp->s_z80_bcprime)); - z80_set_reg(Z80_REG_DE2, LOCENDIAN16(*(uint16 *)ssp->s_z80_deprime)); - z80_set_reg(Z80_REG_HL2, LOCENDIAN16(*(uint16 *)ssp->s_z80_hlprime)); - z80_set_reg(Z80_REG_IX, LOCENDIAN16(*(uint16 *)ssp->s_z80_ix)); - z80_set_reg(Z80_REG_IY, LOCENDIAN16(*(uint16 *)ssp->s_z80_iy)); - z80_set_reg(Z80_REG_SP, LOCENDIAN16(*(uint16 *)ssp->s_z80_sp)); - z80_set_reg(Z80_REG_PC, LOCENDIAN16(*(uint16 *)ssp->s_z80_pc)); - z80_set_reg(Z80_REG_IFF1, ssp->s_z80_iff1[0]); - z80_set_reg(Z80_REG_IFF2, ssp->s_z80_iff2[0]); - z80_set_reg(Z80_REG_IR, ssp->s_z80_i[0]<<8 | ssp->s_z80_r[0]); - z80_set_reg(Z80_REG_IM, ssp->s_z80_im[0]); - z80_set_reg(Z80_REG_Halted, ssp->s_z80_halted[0]); - /* FIX: - Z80_REG_IRQVector, 0x00 to 0xff - Z80_REG_IRQLine, boolean - 1 or 0 - */ + if (state_transfermode == 0) { + /* save */ + i16 = z80_get_reg(Z80_REG_AF); state_transfer16("z80", "af", 0, &i16, 1); + i16 = z80_get_reg(Z80_REG_BC); state_transfer16("z80", "bc", 0, &i16, 1); + i16 = z80_get_reg(Z80_REG_DE); state_transfer16("z80", "de", 0, &i16, 1); + i16 = z80_get_reg(Z80_REG_HL); state_transfer16("z80", "hl", 0, &i16, 1); + i16 = z80_get_reg(Z80_REG_AF2); state_transfer16("z80", "af2", 0, &i16, 1); + i16 = z80_get_reg(Z80_REG_BC2); state_transfer16("z80", "bc2", 0, &i16, 1); + i16 = z80_get_reg(Z80_REG_DE2); state_transfer16("z80", "de2", 0, &i16, 1); + i16 = z80_get_reg(Z80_REG_HL2); state_transfer16("z80", "hl2", 0, &i16, 1); + i16 = z80_get_reg(Z80_REG_IX); state_transfer16("z80", "ix", 0, &i16, 1); + i16 = z80_get_reg(Z80_REG_IY); state_transfer16("z80", "iy", 0, &i16, 1); + i16 = z80_get_reg(Z80_REG_SP); state_transfer16("z80", "sp", 0, &i16, 1); + i16 = z80_get_reg(Z80_REG_PC); state_transfer16("z80", "pc", 0, &i16, 1); + i8 = (z80_get_reg(Z80_REG_IR) >> 8) & 0xff; + state_transfer8("z80", "i", 0, &i8, 1); + i8 = z80_get_reg(Z80_REG_IR) & 0xff; + state_transfer8("z80", "r", 0, &i8, 1); + i8 = z80_get_reg(Z80_REG_IFF1); state_transfer8("z80", "iff1", 0, &i8, 1); + i8 = z80_get_reg(Z80_REG_IFF2); state_transfer8("z80", "iff2", 0, &i8, 1); + i8 = z80_get_reg(Z80_REG_IM); state_transfer8("z80", "im", 0, &i8, 1); + i8 = z80_get_reg(Z80_REG_Halted); + state_transfer8("z80", "halted", 0, &i8, 1); + } else { + /* load */ + state_transfer16("z80", "af", 0, &i16, 1); z80_set_reg(Z80_REG_AF, i16); + state_transfer16("z80", "bc", 0, &i16, 1); z80_set_reg(Z80_REG_BC, i16); + state_transfer16("z80", "de", 0, &i16, 1); z80_set_reg(Z80_REG_DE, i16); + state_transfer16("z80", "hl", 0, &i16, 1); z80_set_reg(Z80_REG_HL, i16); + state_transfer16("z80", "af2", 0, &i16, 1); z80_set_reg(Z80_REG_AF2, i16); + state_transfer16("z80", "bc2", 0, &i16, 1); z80_set_reg(Z80_REG_BC2, i16); + state_transfer16("z80", "de2", 0, &i16, 1); z80_set_reg(Z80_REG_DE2, i16); + state_transfer16("z80", "hl2", 0, &i16, 1); z80_set_reg(Z80_REG_HL2, i16); + state_transfer16("z80", "ix", 0, &i16, 1); z80_set_reg(Z80_REG_IX, i16); + state_transfer16("z80", "iy", 0, &i16, 1); z80_set_reg(Z80_REG_IY, i16); + state_transfer16("z80", "sp", 0, &i16, 1); z80_set_reg(Z80_REG_SP, i16); + state_transfer16("z80", "pc", 0, &i16, 1); z80_set_reg(Z80_REG_PC, i16); + state_transfer8("z80", "i", 0, &i8, 1); + state_transfer8("z80", "r", 0, &i8b, 1); + z80_set_reg(Z80_REG_IR, ((uint16)i8 << 8) | i8b); + state_transfer8("z80", "iff1", 0, &i8, 1); z80_set_reg(Z80_REG_IFF1, i8); + state_transfer8("z80", "iff2", 0, &i8, 1); z80_set_reg(Z80_REG_IFF2, i8); + state_transfer8("z80", "im", 0, &i8, 1); z80_set_reg(Z80_REG_IM, i8); + state_transfer8("z80", "halted", 0, &i8, 1); + z80_set_reg(Z80_REG_Halted, i8); + } #else - cpuz80_z80.z80af = LOCENDIAN16(*(uint16 *)ssp->s_z80_af); - cpuz80_z80.z80bc = LOCENDIAN16(*(uint16 *)ssp->s_z80_bc); - cpuz80_z80.z80de = LOCENDIAN16(*(uint16 *)ssp->s_z80_de); - cpuz80_z80.z80hl = LOCENDIAN16(*(uint16 *)ssp->s_z80_hl); - cpuz80_z80.z80afprime = LOCENDIAN16(*(uint16 *)ssp->s_z80_afprime); - cpuz80_z80.z80bcprime = LOCENDIAN16(*(uint16 *)ssp->s_z80_bcprime); - cpuz80_z80.z80deprime = LOCENDIAN16(*(uint16 *)ssp->s_z80_deprime); - cpuz80_z80.z80hlprime = LOCENDIAN16(*(uint16 *)ssp->s_z80_hlprime); - cpuz80_z80.z80ix = LOCENDIAN16(*(uint16 *)ssp->s_z80_ix); - cpuz80_z80.z80iy = LOCENDIAN16(*(uint16 *)ssp->s_z80_iy); - cpuz80_z80.z80sp = LOCENDIAN16(*(uint16 *)ssp->s_z80_sp); - cpuz80_z80.z80pc = LOCENDIAN16(*(uint16 *)ssp->s_z80_pc); - cpuz80_z80.z80i = ssp->s_z80_i[0]; - cpuz80_z80.z80r = ssp->s_z80_r[0]; - cpuz80_z80.z80inInterrupt = ssp->s_z80_iff1[0]; - cpuz80_z80.z80interruptState = ssp->s_z80_iff2[0]; - cpuz80_z80.z80interruptMode = ssp->s_z80_im[0]; - cpuz80_z80.z80halted = ssp->s_z80_halted[0]; - /* FIX: - c -> z80intAddr = z80intAddr; - c -> z80nmiAddr = z80nmiAddr; - */ + state_transfer16("z80", "af", 0, &cpuz80_z80.z80af, 1); + state_transfer16("z80", "bc", 0, &cpuz80_z80.z80bc, 1); + state_transfer16("z80", "de", 0, &cpuz80_z80.z80de, 1); + state_transfer16("z80", "hl", 0, &cpuz80_z80.z80hl, 1); + state_transfer16("z80", "af2", 0, &cpuz80_z80.z80afprime, 1); + state_transfer16("z80", "bc2", 0, &cpuz80_z80.z80bcprime, 1); + state_transfer16("z80", "de2", 0, &cpuz80_z80.z80deprime, 1); + state_transfer16("z80", "hl2", 0, &cpuz80_z80.z80hlprime, 1); + state_transfer16("z80", "ix", 0, &cpuz80_z80.z80ix, 1); + state_transfer16("z80", "iy", 0, &cpuz80_z80.z80iy, 1); + state_transfer16("z80", "sp", 0, &cpuz80_z80.z80sp, 1); + state_transfer16("z80", "pc", 0, &cpuz80_z80.z80pc, 1); + state_transfer8("z80", "i", 0, &cpuz80_z80.z80i, 1); + state_transfer8("z80", "r", 0, &cpuz80_z80.z80r, 1); + if (state_transfermode == 0) { + /* save */ + i8 = cpuz80_z80.z80inInterrupt; + state_transfer8("z80", "iff1", 0, &i8, 1); + i8 = cpuz80_z80.z80interruptState; + state_transfer8("z80", "iff2", 0, &i8, 1); + i8 = cpuz80_z80.z80interruptMode; + state_transfer8("z80", "im", 0, &i8, 1); + i8 = cpuz80_z80.z80halted; + state_transfer8("z80", "halted", 0, &i8, 1); + } else { + /* load */ + state_transfer8("z80", "iff1", 0, &i8, 1); + cpuz80_z80.z80inInterrupt = i8; + state_transfer8("z80", "iff2", 0, &i8, 1); + cpuz80_z80.z80interruptState = i8; + state_transfer8("z80", "im", 0, &i8, 1); + cpuz80_z80.z80interruptMode = i8; + state_transfer8("z80", "halted", 0, &i8, 1); + cpuz80_z80.z80halted = i8; + } #endif - cpuz80_bank = LOCENDIAN32(*(uint32 *)ssp->s_z80_bank); + YM2612_save_state(); + /* XXX: FIX ME! + Z80_REG_IRQVector, 0x00 to 0xff + Z80_REG_IRQLine, boolean - 1 or 0 + */ + + /* XXX: FIX ME! + c -> z80intAddr = z80intAddr; + c -> z80nmiAddr = z80nmiAddr; + */ +} - memcpy(vdp_reg, ssp->s_vdp_regs, sizeof(ssp->s_vdp_regs)); - vdp_pal = ssp->s_vdp_pal[0]; - vdp_overseas = ssp->s_vdp_overseas[0]; - vdp_ctrlflag = ssp->s_vdp_ctrlflag[0]; - vdp_code = ssp->s_vdp_code[0]; - vdp_first = LOCENDIAN16(*(uint16 *)ssp->s_vdp_first); - vdp_second = LOCENDIAN16(*(uint16 *)ssp->s_vdp_second); - vdp_dmabytes = LOCENDIAN32(*(uint32 *)ssp->s_vdp_dmabytes); - vdp_address = LOCENDIAN16(*(uint16 *)ssp->s_vdp_address); - - /* reset some other run-time stuff that isn't important enough to save */ - vdp_setupvideo(); - vdp_dmabusy = vdp_dmabytes > 0 ? 1 : 0; +/*** state_savefile - save to the given filename */ - for (i = 0; i < 256; i++) { - soundi_ym2612store(0, i); - soundi_ym2612store(1, ssp->s_fmregs1[i]); - soundi_ym2612store(2, i); - soundi_ym2612store(3, ssp->s_fmregs2[i]); +int state_savefile(const char *filename) +{ + if ((state_outputfile = fopen(filename, "wb")) == NULL) { + LOG_CRITICAL(("Failed to open '%s' for writing: %s", + filename, strerror(errno))); + return -1; } - soundi_ym2612store(0, ssp->s_fmaddr1[0]); - soundi_ym2612store(1, ssp->s_fmaddr2[0]); - cpuz80_updatecontext(); + fprintf(state_outputfile, "Generator " VERSION " saved state\n"); + state_major = 2; + state_minor = 0; + state_dotransfer(0); /* save */ + fclose(state_outputfile); return 0; } -/*** state_save - save to the given slot ***/ +/*** state_loadfile - load the given filename ***/ -int state_save(const int slot) +int state_loadfile(const char *filename) { - char filename[256]; - struct savestate ss; - struct savestate *ssp = &ss; /* I want a pointer like the load routines */ + char *blk; + uint8 *p, *e; struct stat statbuf; - FILE *f, *a; - int i; + FILE *f; + t_statelist *ent; - snprintf(filename, sizeof(filename), "%s.gt%d", gen_leafname, slot); - if ((f = fopen(filename, "wb")) == NULL) { - LOG_CRITICAL(("Failed to open '%s' for writing: %s", - filename, strerror(errno))); + if (stat(filename, &statbuf) != 0) { + errno = ENOENT; return -1; } - memset(ssp, 0, sizeof(ss)); - ssp->s_majorver[0] = 1; - ssp->s_minorver[0] = 2; - memcpy(ssp->s_ram, cpu68k_ram, sizeof(ssp->s_ram)); - memcpy(ssp->s_vram, vdp_vram, sizeof(ssp->s_vram)); - memcpy(ssp->s_z80ram, cpuz80_ram, sizeof(ssp->s_z80ram)); - memcpy(ssp->s_cram, vdp_cram, sizeof(ssp->s_cram)); - memcpy(ssp->s_vsram, vdp_vsram, sizeof(ssp->s_vsram)); - for (i = 0; i < 16; i++) - ((uint32 *)ssp->s_68k_regs)[i] = LOCENDIAN32(regs.regs[i]); - *(uint32 *)ssp->s_68k_pc = LOCENDIAN32(regs.pc); - *(uint32 *)ssp->s_68k_sp = LOCENDIAN32(regs.sp); - *(uint16 *)ssp->s_68k_sr = LOCENDIAN16(regs.sr.sr_int); - ssp->s_68k_stop[0] = regs.stop; - ssp->s_68k_pending[0] = regs.pending; -#ifdef RAZE - *(uint16 *)ssp->s_z80_af = LOCENDIAN16(z80_get_reg(Z80_REG_AF)); - *(uint16 *)ssp->s_z80_bc = LOCENDIAN16(z80_get_reg(Z80_REG_BC)); - *(uint16 *)ssp->s_z80_de = LOCENDIAN16(z80_get_reg(Z80_REG_DE)); - *(uint16 *)ssp->s_z80_hl = LOCENDIAN16(z80_get_reg(Z80_REG_HL)); - *(uint16 *)ssp->s_z80_afprime = LOCENDIAN16(z80_get_reg(Z80_REG_AF2)); - *(uint16 *)ssp->s_z80_bcprime = LOCENDIAN16(z80_get_reg(Z80_REG_BC2)); - *(uint16 *)ssp->s_z80_deprime = LOCENDIAN16(z80_get_reg(Z80_REG_DE2)); - *(uint16 *)ssp->s_z80_hlprime = LOCENDIAN16(z80_get_reg(Z80_REG_HL2)); - *(uint16 *)ssp->s_z80_ix = LOCENDIAN16(z80_get_reg(Z80_REG_IX)); - *(uint16 *)ssp->s_z80_iy = LOCENDIAN16(z80_get_reg(Z80_REG_IY)); - *(uint16 *)ssp->s_z80_sp = LOCENDIAN16(z80_get_reg(Z80_REG_SP)); - *(uint16 *)ssp->s_z80_pc = LOCENDIAN16(z80_get_reg(Z80_REG_PC)); - ssp->s_z80_i[0] = (z80_get_reg(Z80_REG_IR) >> 8) & 0xff; - ssp->s_z80_r[0] = z80_get_reg(Z80_REG_IR) & 0xff; - ssp->s_z80_iff1[0] = z80_get_reg(Z80_REG_IFF1); - ssp->s_z80_iff2[0] = z80_get_reg(Z80_REG_IFF2); - ssp->s_z80_im[0] = z80_get_reg(Z80_REG_IM); -#else - *(uint16 *)ssp->s_z80_af = LOCENDIAN16(cpuz80_z80.z80af); - *(uint16 *)ssp->s_z80_bc = LOCENDIAN16(cpuz80_z80.z80bc); - *(uint16 *)ssp->s_z80_de = LOCENDIAN16(cpuz80_z80.z80de); - *(uint16 *)ssp->s_z80_hl = LOCENDIAN16(cpuz80_z80.z80hl); - *(uint16 *)ssp->s_z80_afprime = LOCENDIAN16(cpuz80_z80.z80afprime); - *(uint16 *)ssp->s_z80_bcprime = LOCENDIAN16(cpuz80_z80.z80bcprime); - *(uint16 *)ssp->s_z80_deprime = LOCENDIAN16(cpuz80_z80.z80deprime); - *(uint16 *)ssp->s_z80_hlprime = LOCENDIAN16(cpuz80_z80.z80hlprime); - *(uint16 *)ssp->s_z80_ix = LOCENDIAN16(cpuz80_z80.z80ix); - *(uint16 *)ssp->s_z80_iy = LOCENDIAN16(cpuz80_z80.z80iy); - *(uint16 *)ssp->s_z80_sp = LOCENDIAN16(cpuz80_z80.z80sp); - *(uint16 *)ssp->s_z80_pc = LOCENDIAN16(cpuz80_z80.z80pc); - ssp->s_z80_i[0] = cpuz80_z80.z80i; - ssp->s_z80_r[0] = cpuz80_z80.z80r; - ssp->s_z80_iff1[0] = cpuz80_z80.z80inInterrupt; - ssp->s_z80_iff2[0] = cpuz80_z80.z80interruptState; - ssp->s_z80_im[0] = cpuz80_z80.z80interruptMode; - ssp->s_z80_halted[0] = cpuz80_z80.z80halted; -#endif - *(uint32 *)ssp->s_z80_bank = LOCENDIAN32(cpuz80_bank); - memcpy(ssp->s_vdp_regs, vdp_reg, sizeof(ssp->s_vdp_regs)); - ssp->s_vdp_pal[0] = vdp_pal; - ssp->s_vdp_overseas[0] = vdp_overseas; - ssp->s_vdp_ctrlflag[0] = vdp_ctrlflag; - ssp->s_vdp_code[0] = vdp_code; - *(uint16 *)ssp->s_vdp_first = LOCENDIAN16(vdp_first); - *(uint16 *)ssp->s_vdp_second = LOCENDIAN16(vdp_second); - *(uint32 *)ssp->s_vdp_dmabytes = LOCENDIAN32(vdp_dmabytes); - *(uint16 *)ssp->s_vdp_address = LOCENDIAN16(vdp_address); - memcpy(ssp->s_fmregs1, soundi_regs1, sizeof(ssp->s_fmregs1)); - memcpy(ssp->s_fmregs2, soundi_regs2, sizeof(ssp->s_fmregs2)); - ssp->s_fmaddr1[0] = soundi_address1; - ssp->s_fmaddr2[0] = soundi_address2; - if (fwrite(ssp, sizeof(ss), 1, f) != 1) { - LOG_CRITICAL(("Error whilst writing GTR file '%s': %s", filename, - strerror(errno))); + + if ((blk = malloc(statbuf.st_size)) == NULL) { + LOG_CRITICAL(("Failed to allocate memory whilst loading '%s'", filename)); + return -1; + } + if ((f = fopen(filename, "rb")) == NULL) { + LOG_CRITICAL(("Failed to open '%s': %s", filename, strerror(errno))); + free(blk); + return -1; + } + if (fread(blk, statbuf.st_size, 1, f) != 1) { + if (feof(f)) { + LOG_CRITICAL(("EOF whilst reading save state file '%s'", filename)); + free(blk); + return -1; + } + LOG_CRITICAL(("Error whilst reading save state file '%s': %s", filename, + strerror(errno))); + free(blk); return -1; } fclose(f); + + p = blk; + e = blk + statbuf.st_size; + + /* skip first line comment */ + while (p < e && *p++ != '\n') ; + if (p >= e) + goto OVERRUN; + + /* loop around blocks creating structure */ + state_statelist = NULL; + for (;;) { + /* mod(1+), name(1+), instance(1), bytes(1), size(4), data(0+) */ + if (e == p) + /* EOF */ + break; + if ((ent = malloc(sizeof(t_statelist))) == NULL) + ui_err("out of memory"); + if ((e-p) < 8) + goto OVERRUN; + ent->mod = p; + while (p < e && *p++) ; + if ((e-p) < 7) + goto OVERRUN; + ent->name = p; + while (p < e && *p++) ; + if ((e-p) < 6) + goto OVERRUN; + ent->instance = p[0]; + ent->bytes = p[1]; + ent->size = (p[2] << 24) | (p[3] << 16) | (p[4] << 8) | p[5]; + if ((e-p) < (int)(ent->bytes * ent->size)) + goto OVERRUN; + p+= 6; + ent->data = p; + p+= ent->bytes * ent->size; + ent->next = state_statelist; + state_statelist = ent; + } + + /* reset */ + gen_reset(); + + state_dotransfer(1); /* load into place */ + + /* free memory */ + free(blk); + while (state_statelist) { + ent = state_statelist; + state_statelist = state_statelist->next; + free(ent); + } + + if (state_major != 2) { + LOG_CRITICAL(("Save state file '%s' is version %d, and we're version 2", + filename, state_major)); + errno = EINVAL; + return -1; + } + + /* reset some other run-time stuff that isn't important enough to save */ + vdp_setupvideo(); + vdp_dmabusy = vdp_dmabytes > 0 ? 1 : 0; + cpuz80_updatecontext(); return 0; +OVERRUN: + LOG_CRITICAL(("Invalid state file '%s': overrun encountered", filename)); + errno = EINVAL; + free(blk); + while (state_statelist) { + ent = state_statelist; + state_statelist = state_statelist->next; + free(ent); + } + return -1; } +