int MDFNSS_LoadFP(gzFile fp) { uint8 header[32]; StateMem st; memset(&st, 0, sizeof(StateMem)); if(gzread(fp, header, 32) != 32) { return(0); } st.len = MDFN_de32lsb(header + 16 + 4); if(st.len < 32) return(0); if(!(st.data = (uint8 *)malloc(st.len))) return(0); memcpy(st.data, header, 32); if(gzread(fp, st.data + 32, st.len - 32) != ((int32)st.len - 32)) { free(st.data); return(0); } if(!MDFNSS_LoadSM(&st, 1, 0)) { free(st.data); return(0); } free(st.data); return(1); }
bool retro_unserialize(const void *data, size_t size) { StateMem st; memset(&st, 0, sizeof(st)); st.data = (uint8_t*)data; st.len = size; return MDFNSS_LoadSM(&st, 0, 1); }
static void RecvState(const uint32 clen) { StateMem sm; std::vector<uint8> cbuf; std::vector<uint8> buf; memset(&sm, 0, sizeof(StateMem)); if(clen < 4) { throw MDFN_Error(0, _("Compressed save state data is too small: %u"), clen); } if(clen > 8 * 1024 * 1024) // Compressed length sanity check - 8 MiB max. { throw MDFN_Error(0, _("Compressed save state data is too large: %u"), clen); } cbuf.resize(clen); MDFND_RecvData(&cbuf[0], clen); uLongf len = MDFN_de32lsb(&cbuf[0]); if(len > 12 * 1024 * 1024) // Uncompressed length sanity check - 12 MiB max. { throw MDFN_Error(0, _("Uncompressed save state data is too large: %llu"), (unsigned long long)len); } buf.resize(len); uncompress((Bytef *)&buf[0], &len, (Bytef *)&cbuf[0] + 4, clen - 4); sm.data = &buf[0]; sm.len = len; if(!MDFNSS_LoadSM(&sm, 0, 0)) { throw MDFN_Error(0, _("Error during save state loading.")); } if(MDFNMOV_IsRecording()) MDFNMOV_RecordState(); }
int MDFN_StateEvil(int rewind) { if(!EvilEnabled) return(0); if(rewind) { int32 next_bcspos = bcspos; bool NeedDataFlush = FALSE; bcspos--; if(bcspos < 0) bcspos += SRW_NUM; if(!bcs[bcspos].data) bcspos = (bcspos + 1) % SRW_NUM; else NeedDataFlush = TRUE; if(bcs[bcspos].compressed_len) { uint8 *tmp_buf; lzo_uint dst_len = bcs[bcspos].uncompressed_len; tmp_buf = (uint8 *)malloc(bcs[bcspos].uncompressed_len); if(SRWCompressor == SRW_COMPRESSOR_QUICKLZ) { //static char workmem[QLZ_SCRATCH_DECOMPRESS]; dst_len = qlz_decompress((char*)bcs[bcspos].data, tmp_buf); //, workmem); } else if(SRWCompressor == SRW_COMPRESSOR_MINILZO) lzo1x_decompress(bcs[bcspos].data, bcs[bcspos].compressed_len, tmp_buf, &dst_len, NULL); else if(SRWCompressor == SRW_COMPRESSOR_BLZ) { dst_len = blz_unpack(bcs[bcspos].data, tmp_buf); } for(uint32 x = 0; x < bcs[bcspos].uncompressed_len && x < bcs[next_bcspos].uncompressed_len; x++) tmp_buf[x] ^= bcs[next_bcspos].data[x]; free(bcs[bcspos].data); bcs[bcspos].data = tmp_buf; bcs[bcspos].compressed_len = 0; } if(NeedDataFlush) { if(bcs[next_bcspos].MovieLove.data) { free(bcs[next_bcspos].MovieLove.data); bcs[next_bcspos].MovieLove.data = NULL; } free(bcs[next_bcspos].data); bcs[next_bcspos].data = NULL; bcs[next_bcspos].compressed_len = 0; bcs[next_bcspos].uncompressed_len = 0; } if(bcs[bcspos].uncompressed_len) { StateMem sm; sm.data = bcs[bcspos].data; sm.loc = 0; sm.initial_malloc = 0; sm.malloced = sm.len = bcs[bcspos].uncompressed_len; MDFNSS_LoadSM(&sm, 0, 1); // free(MDFNMOV_GrabRewindJoy().data); return(1); } } else { StateMem sm; int32 prev_bcspos = bcspos; bcspos = (bcspos + 1) % SRW_NUM; // if(MDFNMOV_IsRecording()) { if(bcs[bcspos].data && bcs[bcspos].MovieLove.data) { //printf("Force: %d\n", bcspos); // MDFNMOV_ForceRecord(&bcs[bcspos].MovieLove); free(bcs[bcspos].MovieLove.data); bcs[bcspos].MovieLove.data = NULL; } } if(bcs[bcspos].data) { free(bcs[bcspos].data); bcs[bcspos].data = NULL; } if(bcs[bcspos].MovieLove.data) { free(bcs[bcspos].MovieLove.data); bcs[bcspos].MovieLove.data = NULL; } memset(&sm, 0, sizeof(sm)); // MDFNSS_SaveSM(&sm, 0, 1); bcs[bcspos].data = sm.data; bcs[bcspos].compressed_len = 0; bcs[bcspos].uncompressed_len = sm.len; // Compress the previous save state. if(bcs[prev_bcspos].data) { for(uint32 x = 0; x < bcs[prev_bcspos].uncompressed_len && x < sm.len; x++) bcs[prev_bcspos].data[x] ^= sm.data[x]; if(SRWCompressor == SRW_COMPRESSOR_QUICKLZ) { //static char workmem[QLZ_SCRATCH_COMPRESS]; uint8 *tmp_buf = (uint8 *)malloc(bcs[prev_bcspos].uncompressed_len + 36000); uint32 dst_len = bcs[prev_bcspos].uncompressed_len + 36000; dst_len = qlz_compress(bcs[prev_bcspos].data, (char*)tmp_buf, bcs[prev_bcspos].uncompressed_len); //, workmem); free(bcs[prev_bcspos].data); bcs[prev_bcspos].data = (uint8 *)realloc(tmp_buf, dst_len); bcs[prev_bcspos].compressed_len = dst_len; } else if(SRWCompressor == SRW_COMPRESSOR_MINILZO) { uint8 workmem[LZO1X_1_MEM_COMPRESS]; uint8 * tmp_buf = (uint8 *)malloc((size_t)(1.10 * bcs[prev_bcspos].uncompressed_len)); lzo_uint dst_len = (lzo_uint)(1.10 * bcs[prev_bcspos].uncompressed_len); lzo1x_1_compress(bcs[prev_bcspos].data, bcs[prev_bcspos].uncompressed_len, tmp_buf, &dst_len, workmem); free(bcs[prev_bcspos].data); bcs[prev_bcspos].data = (uint8 *)realloc(tmp_buf, dst_len); bcs[prev_bcspos].compressed_len = dst_len; } else if(SRWCompressor == SRW_COMPRESSOR_BLZ) { blz_pack_t workmem; uint8 * tmp_buf = (uint8 *)malloc((size_t)(bcs[prev_bcspos].uncompressed_len + blz_pack_extra)); uint32 dst_len = bcs[prev_bcspos].uncompressed_len + blz_pack_extra; dst_len = blz_pack(bcs[prev_bcspos].data, bcs[prev_bcspos].uncompressed_len, tmp_buf, &workmem); free(bcs[prev_bcspos].data); bcs[prev_bcspos].data = (uint8 *)realloc(tmp_buf, dst_len); bcs[prev_bcspos].compressed_len = dst_len; } } // if(MDFNMOV_IsRecording()) // bcs[bcspos].MovieLove = MDFNMOV_GrabRewindJoy(); } return(0); }
// Donuts are a tasty treat and delicious with powdered sugar. void MDFNMOV_AddJoy(void *donutdata, uint32 donutlen) { gzFile fp; if(!current) return; /* Not playback nor recording. */ if(current < 0) /* Playback */ { int t; fp = slots[-1 - current]; while((t = gzgetc(fp)) >= 0 && t) { if(t == MDFNNPCMD_LOADSTATE) { uint32 len; StateMem sm; len = gzgetc(fp); len |= gzgetc(fp) << 8; len |= gzgetc(fp) << 16; len |= gzgetc(fp) << 24; if(len >= 5 * 1024 * 1024) // A sanity limit of 5MiB { StopPlayback(); return; } memset(&sm, 0, sizeof(StateMem)); sm.len = len; sm.data = (uint8 *)malloc(len); if(gzread(fp, sm.data, len) != len) { StopPlayback(); return; } if(!MDFNSS_LoadSM(&sm, 0, 0)) { StopPlayback(); return; } } else MDFN_DoSimpleCommand(t); } if(t < 0) { StopPlayback(); return; } if(gzread(fp, donutdata, donutlen) != donutlen) { StopPlayback(); return; } } else /* Recording */ { if(MDFN_StateEvilIsRunning()) { smem_putc(&RewindBuffer, 0); smem_write(&RewindBuffer, donutdata, donutlen); } else { fp = slots[current - 1]; gzputc(fp, 0); gzwrite(fp, donutdata, donutlen); } } }
bool MDFNI_LoadState(const char *fname, const char *suffix) noexcept { bool ret = true; try { if(!MDFNGameInfo->StateAction) { throw MDFN_Error(0, _("Module \"%s\" doesn't support save states."), MDFNGameInfo->shortname); } /* For network play and movies, be load the state locally, and then save the state to a temporary buffer, and send or record that. This ensures that if an older state is loaded that is missing some information expected in newer save states, desynchronization won't occur(at least not from this ;)). */ { GZFileStream st(fname ? std::string(fname) : MDFN_MakeFName(MDFNMKF_STATE,CurrentState,suffix), GZFileStream::MODE::READ); uint8 header[32]; uint32 st_len; st.read(header, 32); st_len = MDFN_de32lsb(header + 16 + 4) & 0x7FFFFFFF; if(st_len < 32) throw MDFN_Error(0, _("Save state header length field is bad.")); MemoryStream sm(st_len, -1); memcpy(sm.map(), header, 32); st.read(sm.map() + 32, st_len - 32); MDFNSS_LoadSM(&sm, false); } if(MDFNnetplay) { NetplaySendState(); } if(MDFNMOV_IsRecording()) MDFNMOV_RecordState(); MDFND_SetStateStatus(NULL); if(!fname && !suffix) { SaveStateStatus[CurrentState] = true; MDFN_DispMessage(_("State %d loaded."), CurrentState); } } catch(std::exception &e) { if(!fname && !suffix) MDFN_DispMessage(_("State %d load error: %s"), CurrentState, e.what()); else MDFN_PrintError("%s", e.what()); if(MDFNnetplay) MDFND_NetplayText(e.what(), false); ret = false; } return(ret); }