X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=source%2Fmupen64plus-core%2Fsrc%2Fmain%2Fsavestates.c;fp=source%2Fmupen64plus-core%2Fsrc%2Fmain%2Fsavestates.c;h=ff03f99ce579e90902455a49487fd05448e11aba;hb=451ab91e3827a6384981b3300e2a7000d2eaba58;hp=0000000000000000000000000000000000000000;hpb=a2ab25365b5b0dddbee476d695d8a31151407581;p=mupen64plus-pandora.git diff --git a/source/mupen64plus-core/src/main/savestates.c b/source/mupen64plus-core/src/main/savestates.c new file mode 100644 index 0000000..ff03f99 --- /dev/null +++ b/source/mupen64plus-core/src/main/savestates.c @@ -0,0 +1,1534 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus - savestates.c * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Copyright (C) 2012 CasualJames * + * Copyright (C) 2009 Olejl Tillin9 * + * Copyright (C) 2008 Richard42 Tillin9 * + * Copyright (C) 2002 Hacktarux * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include +#include + +#define M64P_CORE_PROTOTYPES 1 +#include "api/m64p_types.h" +#include "api/callbacks.h" +#include "api/m64p_config.h" +#include "api/config.h" + +#include "savestates.h" +#include "main.h" +#include "rom.h" +#include "util.h" +#include "workqueue.h" + +#include "memory/memory.h" +#include "memory/flashram.h" +#include "memory/tlb.h" +#include "r4300/macros.h" +#include "r4300/r4300.h" +#include "r4300/interupt.h" +#include "osal/preproc.h" +#include "osd/osd.h" +#include "r4300/new_dynarec/new_dynarec.h" + +#ifdef LIBMINIZIP + #include + #include +#else + #include "main/zip/unzip.h" + #include "main/zip/zip.h" +#endif + +static const char* savestate_magic = "M64+SAVE"; +static const int savestate_latest_version = 0x00010000; /* 1.0 */ +static const unsigned char pj64_magic[4] = { 0xC8, 0xA6, 0xD8, 0x23 }; + +static savestates_job job = savestates_job_nothing; +static savestates_type type = savestates_type_unknown; +static char *fname = NULL; + +static unsigned int slot = 0; +static int autoinc_save_slot = 0; + +static SDL_mutex *savestates_lock; + +struct savestate_work { + char *filepath; + char *data; + size_t size; + struct work_struct work; +}; + +/* Returns the malloc'd full path of the currently selected savestate. */ +static char *savestates_generate_path(savestates_type type) +{ + if(fname != NULL) /* A specific path was given. */ + { + return strdup(fname); + } + else /* Use the selected savestate slot */ + { + char *filename; + switch (type) + { + case savestates_type_m64p: + filename = formatstr("%s.st%d", ROM_SETTINGS.goodname, slot); + break; + case savestates_type_pj64_zip: + filename = formatstr("%s.pj%d.zip", ROM_PARAMS.headername, slot); + break; + case savestates_type_pj64_unc: + filename = formatstr("%s.pj%d", ROM_PARAMS.headername, slot); + break; + default: + filename = NULL; + break; + } + + if (filename != NULL) + { + char *filepath = formatstr("%s%s", get_savestatepath(), filename); + free(filename); + return filepath; + } + else + return NULL; + } +} + +void savestates_select_slot(unsigned int s) +{ + if(s>9||s==slot) + return; + slot = s; + ConfigSetParameter(g_CoreConfig, "CurrentSaveSlot", M64TYPE_INT, &s); + StateChanged(M64CORE_SAVESTATE_SLOT, slot); + + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Selected state slot: %d", slot); +} + +/* Returns the currently selected save slot. */ +unsigned int savestates_get_slot(void) +{ + return slot; +} + +/* Sets save state slot autoincrement on or off. */ +void savestates_set_autoinc_slot(int b) +{ + autoinc_save_slot = b; +} + +void savestates_inc_slot(void) +{ + if(++slot>9) + slot = 0; + StateChanged(M64CORE_SAVESTATE_SLOT, slot); +} + +savestates_job savestates_get_job(void) +{ + return job; +} + +void savestates_set_job(savestates_job j, savestates_type t, const char *fn) +{ + if (fname != NULL) + { + free(fname); + fname = NULL; + } + + job = j; + type = t; + if (fn != NULL) + fname = strdup(fn); +} + +static void savestates_clear_job(void) +{ + savestates_set_job(savestates_job_nothing, savestates_type_unknown, NULL); +} + +#define GETARRAY(buff, type, count) \ + (to_little_endian_buffer(buff, sizeof(type),count), \ + buff += count*sizeof(type), \ + (type *)(buff-count*sizeof(type))) +#define COPYARRAY(dst, buff, type, count) \ + memcpy(dst, GETARRAY(buff, type, count), sizeof(type)*count) +#define GETDATA(buff, type) *GETARRAY(buff, type, 1) + +#define PUTARRAY(src, buff, type, count) \ + memcpy(buff, src, sizeof(type)*count); \ + to_little_endian_buffer(buff, sizeof(type), count); \ + buff += count*sizeof(type); + +#define PUTDATA(buff, type, value) \ + do { type x = value; PUTARRAY(&x, buff, type, 1); } while(0) + +static int savestates_load_m64p(char *filepath) +{ + unsigned char header[44]; + gzFile f; + int version; + int i; + + size_t savestateSize; + unsigned char *savestateData, *curr; + char queue[1024]; + + SDL_LockMutex(savestates_lock); + + f = gzopen(filepath, "rb"); + if(f==NULL) + { + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Could not open state file: %s", filepath); + SDL_UnlockMutex(savestates_lock); + return 0; + } + + /* Read and check Mupen64Plus magic number. */ + if (gzread(f, header, 44) != 44) + { + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Could not read header from state file %s", filepath); + gzclose(f); + SDL_UnlockMutex(savestates_lock); + return 0; + } + curr = header; + + if(strncmp((char *)curr, savestate_magic, 8)!=0) + { + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "State file: %s is not a valid Mupen64plus savestate.", filepath); + gzclose(f); + SDL_UnlockMutex(savestates_lock); + return 0; + } + curr += 8; + + version = *curr++; + version = (version << 8) | *curr++; + version = (version << 8) | *curr++; + version = (version << 8) | *curr++; + if(version != 0x00010000) + { + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "State version (%08x) isn't compatible. Please update Mupen64Plus.", version); + gzclose(f); + SDL_UnlockMutex(savestates_lock); + return 0; + } + + if(memcmp((char *)curr, ROM_SETTINGS.MD5, 32)) + { + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "State ROM MD5 does not match current ROM."); + gzclose(f); + SDL_UnlockMutex(savestates_lock); + return 0; + } + curr += 32; + + /* Read the rest of the savestate */ + savestateSize = 16788244; + savestateData = curr = (unsigned char *)malloc(savestateSize); + if (savestateData == NULL) + { + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Insufficient memory to load state."); + gzclose(f); + SDL_UnlockMutex(savestates_lock); + return 0; + } + if (gzread(f, savestateData, savestateSize) != savestateSize || + (gzread(f, queue, sizeof(queue)) % 4) != 0) + { + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Could not read Mupen64Plus savestate data from %s", filepath); + free(savestateData); + gzclose(f); + SDL_UnlockMutex(savestates_lock); + return 0; + } + + gzclose(f); + SDL_UnlockMutex(savestates_lock); + + // Parse savestate + rdram_register.rdram_config = GETDATA(curr, unsigned int); + rdram_register.rdram_device_id = GETDATA(curr, unsigned int); + rdram_register.rdram_delay = GETDATA(curr, unsigned int); + rdram_register.rdram_mode = GETDATA(curr, unsigned int); + rdram_register.rdram_ref_interval = GETDATA(curr, unsigned int); + rdram_register.rdram_ref_row = GETDATA(curr, unsigned int); + rdram_register.rdram_ras_interval = GETDATA(curr, unsigned int); + rdram_register.rdram_min_interval = GETDATA(curr, unsigned int); + rdram_register.rdram_addr_select = GETDATA(curr, unsigned int); + rdram_register.rdram_device_manuf = GETDATA(curr, unsigned int); + + MI_register.w_mi_init_mode_reg = GETDATA(curr, unsigned int); + MI_register.mi_init_mode_reg = GETDATA(curr, unsigned int); + curr += 4; // Duplicate MI init mode flags from old implementation + MI_register.mi_version_reg = GETDATA(curr, unsigned int); + MI_register.mi_intr_reg = GETDATA(curr, unsigned int); + MI_register.mi_intr_mask_reg = GETDATA(curr, unsigned int); + MI_register.w_mi_intr_mask_reg = GETDATA(curr, unsigned int); + curr += 8; // Duplicated MI intr flags and padding from old implementation + + pi_register.pi_dram_addr_reg = GETDATA(curr, unsigned int); + pi_register.pi_cart_addr_reg = GETDATA(curr, unsigned int); + pi_register.pi_rd_len_reg = GETDATA(curr, unsigned int); + pi_register.pi_wr_len_reg = GETDATA(curr, unsigned int); + pi_register.read_pi_status_reg = GETDATA(curr, unsigned int); + pi_register.pi_bsd_dom1_lat_reg = GETDATA(curr, unsigned int); + pi_register.pi_bsd_dom1_pwd_reg = GETDATA(curr, unsigned int); + pi_register.pi_bsd_dom1_pgs_reg = GETDATA(curr, unsigned int); + pi_register.pi_bsd_dom1_rls_reg = GETDATA(curr, unsigned int); + pi_register.pi_bsd_dom2_lat_reg = GETDATA(curr, unsigned int); + pi_register.pi_bsd_dom2_pwd_reg = GETDATA(curr, unsigned int); + pi_register.pi_bsd_dom2_pgs_reg = GETDATA(curr, unsigned int); + pi_register.pi_bsd_dom2_rls_reg = GETDATA(curr, unsigned int); + + sp_register.sp_mem_addr_reg = GETDATA(curr, unsigned int); + sp_register.sp_dram_addr_reg = GETDATA(curr, unsigned int); + sp_register.sp_rd_len_reg = GETDATA(curr, unsigned int); + sp_register.sp_wr_len_reg = GETDATA(curr, unsigned int); + sp_register.w_sp_status_reg = GETDATA(curr, unsigned int); + sp_register.sp_status_reg = GETDATA(curr, unsigned int); + curr += 16; // Duplicated SP flags and padding from old implementation + sp_register.sp_dma_full_reg = GETDATA(curr, unsigned int); + sp_register.sp_dma_busy_reg = GETDATA(curr, unsigned int); + sp_register.sp_semaphore_reg = GETDATA(curr, unsigned int); + + rsp_register.rsp_pc = GETDATA(curr, unsigned int); + rsp_register.rsp_ibist = GETDATA(curr, unsigned int); + + si_register.si_dram_addr = GETDATA(curr, unsigned int); + si_register.si_pif_addr_rd64b = GETDATA(curr, unsigned int); + si_register.si_pif_addr_wr64b = GETDATA(curr, unsigned int); + si_register.si_stat = GETDATA(curr, unsigned int); + + vi_register.vi_status = GETDATA(curr, unsigned int); + vi_register.vi_origin = GETDATA(curr, unsigned int); + vi_register.vi_width = GETDATA(curr, unsigned int); + vi_register.vi_v_intr = GETDATA(curr, unsigned int); + vi_register.vi_current = GETDATA(curr, unsigned int); + vi_register.vi_burst = GETDATA(curr, unsigned int); + vi_register.vi_v_sync = GETDATA(curr, unsigned int); + vi_register.vi_h_sync = GETDATA(curr, unsigned int); + vi_register.vi_leap = GETDATA(curr, unsigned int); + vi_register.vi_h_start = GETDATA(curr, unsigned int); + vi_register.vi_v_start = GETDATA(curr, unsigned int); + vi_register.vi_v_burst = GETDATA(curr, unsigned int); + vi_register.vi_x_scale = GETDATA(curr, unsigned int); + vi_register.vi_y_scale = GETDATA(curr, unsigned int); + vi_register.vi_delay = GETDATA(curr, unsigned int); + update_vi_status(vi_register.vi_status); + update_vi_width(vi_register.vi_width); + + ri_register.ri_mode = GETDATA(curr, unsigned int); + ri_register.ri_config = GETDATA(curr, unsigned int); + ri_register.ri_current_load = GETDATA(curr, unsigned int); + ri_register.ri_select = GETDATA(curr, unsigned int); + ri_register.ri_refresh = GETDATA(curr, unsigned int); + ri_register.ri_latency = GETDATA(curr, unsigned int); + ri_register.ri_error = GETDATA(curr, unsigned int); + ri_register.ri_werror = GETDATA(curr, unsigned int); + + ai_register.ai_dram_addr = GETDATA(curr, unsigned int); + ai_register.ai_len = GETDATA(curr, unsigned int); + ai_register.ai_control = GETDATA(curr, unsigned int); + ai_register.ai_status = GETDATA(curr, unsigned int); + ai_register.ai_dacrate = GETDATA(curr, unsigned int); + ai_register.ai_bitrate = GETDATA(curr, unsigned int); + ai_register.next_delay = GETDATA(curr, unsigned int); + ai_register.next_len = GETDATA(curr, unsigned int); + ai_register.current_delay = GETDATA(curr, unsigned int); + ai_register.current_len = GETDATA(curr, unsigned int); + update_ai_dacrate(ai_register.ai_dacrate); + + dpc_register.dpc_start = GETDATA(curr, unsigned int); + dpc_register.dpc_end = GETDATA(curr, unsigned int); + dpc_register.dpc_current = GETDATA(curr, unsigned int); + dpc_register.w_dpc_status = GETDATA(curr, unsigned int); + dpc_register.dpc_status = GETDATA(curr, unsigned int); + curr += 12; // Duplicated DPC flags and padding from old implementation + dpc_register.dpc_clock = GETDATA(curr, unsigned int); + dpc_register.dpc_bufbusy = GETDATA(curr, unsigned int); + dpc_register.dpc_pipebusy = GETDATA(curr, unsigned int); + dpc_register.dpc_tmem = GETDATA(curr, unsigned int); + + dps_register.dps_tbist = GETDATA(curr, unsigned int); + dps_register.dps_test_mode = GETDATA(curr, unsigned int); + dps_register.dps_buftest_addr = GETDATA(curr, unsigned int); + dps_register.dps_buftest_data = GETDATA(curr, unsigned int); + + COPYARRAY(rdram, curr, unsigned int, 0x800000/4); + COPYARRAY(SP_DMEM, curr, unsigned int, 0x1000/4); + COPYARRAY(SP_IMEM, curr, unsigned int, 0x1000/4); + COPYARRAY(PIF_RAM, curr, unsigned char, 0x40); + + flashram_info.use_flashram = GETDATA(curr, int); + flashram_info.mode = GETDATA(curr, int); + flashram_info.status = GETDATA(curr, unsigned long long); + flashram_info.erase_offset = GETDATA(curr, unsigned int); + flashram_info.write_pointer = GETDATA(curr, unsigned int); + + COPYARRAY(tlb_LUT_r, curr, unsigned int, 0x100000); + COPYARRAY(tlb_LUT_w, curr, unsigned int, 0x100000); + + llbit = GETDATA(curr, unsigned int); + COPYARRAY(reg, curr, long long int, 32); + COPYARRAY(reg_cop0, curr, unsigned int, 32); + set_fpr_pointers(Status); // Status is reg_cop0[12] + lo = GETDATA(curr, long long int); + hi = GETDATA(curr, long long int); + COPYARRAY(reg_cop1_fgr_64, curr, long long int, 32); + if ((Status & 0x04000000) == 0) // 32-bit FPR mode requires data shuffling because 64-bit layout is always stored in savestate file + shuffle_fpr_data(0x04000000, 0); + FCR0 = GETDATA(curr, int); + FCR31 = GETDATA(curr, int); + + for (i = 0; i < 32; i++) + { + tlb_e[i].mask = GETDATA(curr, short); + curr += 2; + tlb_e[i].vpn2 = GETDATA(curr, int); + tlb_e[i].g = GETDATA(curr, char); + tlb_e[i].asid = GETDATA(curr, unsigned char); + curr += 2; + tlb_e[i].pfn_even = GETDATA(curr, int); + tlb_e[i].c_even = GETDATA(curr, char); + tlb_e[i].d_even = GETDATA(curr, char); + tlb_e[i].v_even = GETDATA(curr, char); + curr++; + tlb_e[i].pfn_odd = GETDATA(curr, int); + tlb_e[i].c_odd = GETDATA(curr, char); + tlb_e[i].d_odd = GETDATA(curr, char); + tlb_e[i].v_odd = GETDATA(curr, char); + tlb_e[i].r = GETDATA(curr, char); + + tlb_e[i].start_even = GETDATA(curr, unsigned int); + tlb_e[i].end_even = GETDATA(curr, unsigned int); + tlb_e[i].phys_even = GETDATA(curr, unsigned int); + tlb_e[i].start_odd = GETDATA(curr, unsigned int); + tlb_e[i].end_odd = GETDATA(curr, unsigned int); + tlb_e[i].phys_odd = GETDATA(curr, unsigned int); + } + +#ifdef NEW_DYNAREC + if (r4300emu == CORE_DYNAREC) { + pcaddr = GETDATA(curr, unsigned int); + pending_exception = 1; + invalidate_all_pages(); + } else { + if(r4300emu != CORE_PURE_INTERPRETER) + { + for (i = 0; i < 0x100000; i++) + invalid_code[i] = 1; + } + generic_jump_to(GETDATA(curr, unsigned int)); // PC + } +#else + if(r4300emu != CORE_PURE_INTERPRETER) + { + for (i = 0; i < 0x100000; i++) + invalid_code[i] = 1; + } + generic_jump_to(GETDATA(curr, unsigned int)); // PC +#endif + + next_interupt = GETDATA(curr, unsigned int); + next_vi = GETDATA(curr, unsigned int); + vi_field = GETDATA(curr, unsigned int); + + // assert(savestateData+savestateSize == curr) + + to_little_endian_buffer(queue, 4, 256); + load_eventqueue_infos(queue); + +#ifdef NEW_DYNAREC + if (r4300emu == CORE_DYNAREC) + last_addr = pcaddr; + else + last_addr = PC->addr; +#else + last_addr = PC->addr; +#endif + + free(savestateData); + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "State loaded from: %s", namefrompath(filepath)); + return 1; +} + +static int savestates_load_pj64(char *filepath, void *handle, + int (*read_func)(void *, void *, size_t)) +{ + char buffer[1024]; + unsigned int vi_timer, SaveRDRAMSize; + int i; + + unsigned char header[8]; + unsigned char RomHeader[0x40]; + + size_t savestateSize; + unsigned char *savestateData, *curr; + + /* Read and check Project64 magic number. */ + if (!read_func(handle, header, 8)) + { + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Could not read header from Project64 savestate %s", filepath); + return 0; + } + + curr = header; + if (memcmp(curr, pj64_magic, 4) != 0) + { + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "State file: %s is not a valid Project64 savestate. Unrecognized file format.", filepath); + return 0; + } + curr += 4; + + SaveRDRAMSize = GETDATA(curr, unsigned int); + + /* Read the rest of the savestate into memory. */ + savestateSize = SaveRDRAMSize + 0x2754; + savestateData = curr = (unsigned char *)malloc(savestateSize); + if (savestateData == NULL) + { + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Insufficient memory to load state."); + return 0; + } + if (!read_func(handle, savestateData, savestateSize)) + { + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Could not read savestate data from Project64 savestate %s", filepath); + free(savestateData); + return 0; + } + + // check ROM header + COPYARRAY(RomHeader, curr, unsigned int, 0x40/4); + if(memcmp(RomHeader, rom, 0x40) != 0) + { + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "State ROM header does not match current ROM."); + free(savestateData); + return 0; + } + + // vi_timer + vi_timer = GETDATA(curr, unsigned int); + + // Program Counter + last_addr = GETDATA(curr, unsigned int); + + // GPR + COPYARRAY(reg, curr, long long int, 32); + + // FPR + COPYARRAY(reg_cop1_fgr_64, curr, long long int, 32); + + // CP0 + COPYARRAY(reg_cop0, curr, unsigned int, 32); + + set_fpr_pointers(Status); // Status is reg_cop0[12] + if ((Status & 0x04000000) == 0) // TODO not sure how pj64 handles this + shuffle_fpr_data(0x04000000, 0); + + // Initialze the interupts + vi_timer += reg_cop0[9]; // Add current Count + next_interupt = (Compare < vi_timer) ? Compare : vi_timer; + next_vi = vi_timer; + vi_field = 0; + *((unsigned int*)&buffer[0]) = VI_INT; + *((unsigned int*)&buffer[4]) = vi_timer; + *((unsigned int*)&buffer[8]) = COMPARE_INT; + *((unsigned int*)&buffer[12]) = Compare; + *((unsigned int*)&buffer[16]) = 0xFFFFFFFF; + + load_eventqueue_infos(buffer); + + // FPCR + FCR0 = GETDATA(curr, int); + curr += 30 * 4; // FCR1...FCR30 not supported + FCR31 = GETDATA(curr, int); + + // hi / lo + hi = GETDATA(curr, long long int); + lo = GETDATA(curr, long long int); + + // rdram register + rdram_register.rdram_config = GETDATA(curr, unsigned int); + rdram_register.rdram_device_id = GETDATA(curr, unsigned int); + rdram_register.rdram_delay = GETDATA(curr, unsigned int); + rdram_register.rdram_mode = GETDATA(curr, unsigned int); + rdram_register.rdram_ref_interval = GETDATA(curr, unsigned int); + rdram_register.rdram_ref_row = GETDATA(curr, unsigned int); + rdram_register.rdram_ras_interval = GETDATA(curr, unsigned int); + rdram_register.rdram_min_interval = GETDATA(curr, unsigned int); + rdram_register.rdram_addr_select = GETDATA(curr, unsigned int); + rdram_register.rdram_device_manuf = GETDATA(curr, unsigned int); + + // sp_register + sp_register.sp_mem_addr_reg = GETDATA(curr, unsigned int); + sp_register.sp_dram_addr_reg = GETDATA(curr, unsigned int); + sp_register.sp_rd_len_reg = GETDATA(curr, unsigned int); + sp_register.sp_wr_len_reg = GETDATA(curr, unsigned int); + sp_register.sp_status_reg = GETDATA(curr, unsigned int); + sp_register.sp_dma_full_reg = GETDATA(curr, unsigned int); + sp_register.sp_dma_busy_reg = GETDATA(curr, unsigned int); + sp_register.sp_semaphore_reg = GETDATA(curr, unsigned int); + rsp_register.rsp_pc = GETDATA(curr, unsigned int); + rsp_register.rsp_ibist = GETDATA(curr, unsigned int); + + make_w_sp_status_reg(); + + // dpc_register + dpc_register.dpc_start = GETDATA(curr, unsigned int); + dpc_register.dpc_end = GETDATA(curr, unsigned int); + dpc_register.dpc_current = GETDATA(curr, unsigned int); + dpc_register.dpc_status = GETDATA(curr, unsigned int); + dpc_register.dpc_clock = GETDATA(curr, unsigned int); + dpc_register.dpc_bufbusy = GETDATA(curr, unsigned int); + dpc_register.dpc_pipebusy = GETDATA(curr, unsigned int); + dpc_register.dpc_tmem = GETDATA(curr, unsigned int); + (void)GETDATA(curr, unsigned int); // Dummy read + (void)GETDATA(curr, unsigned int); // Dummy read + + make_w_dpc_status(); + + // mi_register + MI_register.mi_init_mode_reg = GETDATA(curr, unsigned int); + MI_register.mi_version_reg = GETDATA(curr, unsigned int); + MI_register.mi_intr_reg = GETDATA(curr, unsigned int); + MI_register.mi_intr_mask_reg = GETDATA(curr, unsigned int); + + make_w_mi_init_mode_reg(); + make_w_mi_intr_mask_reg(); + + // vi_register + vi_register.vi_status = GETDATA(curr, unsigned int); + vi_register.vi_origin = GETDATA(curr, unsigned int); + vi_register.vi_width = GETDATA(curr, unsigned int); + vi_register.vi_v_intr = GETDATA(curr, unsigned int); + vi_register.vi_current = GETDATA(curr, unsigned int); + vi_register.vi_burst = GETDATA(curr, unsigned int); + vi_register.vi_v_sync = GETDATA(curr, unsigned int); + vi_register.vi_h_sync = GETDATA(curr, unsigned int); + vi_register.vi_leap = GETDATA(curr, unsigned int); + vi_register.vi_h_start = GETDATA(curr, unsigned int); + vi_register.vi_v_start = GETDATA(curr, unsigned int); + vi_register.vi_v_burst = GETDATA(curr, unsigned int); + vi_register.vi_x_scale = GETDATA(curr, unsigned int); + vi_register.vi_y_scale = GETDATA(curr, unsigned int); + // TODO vi delay? + update_vi_status(vi_register.vi_status); + update_vi_width(vi_register.vi_width); + + // ai_register + ai_register.ai_dram_addr = GETDATA(curr, unsigned int); + ai_register.ai_len = GETDATA(curr, unsigned int); + ai_register.ai_control = GETDATA(curr, unsigned int); + ai_register.ai_status = GETDATA(curr, unsigned int); + ai_register.ai_dacrate = GETDATA(curr, unsigned int); + ai_register.ai_bitrate = GETDATA(curr, unsigned int); + update_ai_dacrate(ai_register.ai_dacrate); + + // pi_register + pi_register.pi_dram_addr_reg = GETDATA(curr, unsigned int); + pi_register.pi_cart_addr_reg = GETDATA(curr, unsigned int); + pi_register.pi_rd_len_reg = GETDATA(curr, unsigned int); + pi_register.pi_wr_len_reg = GETDATA(curr, unsigned int); + pi_register.read_pi_status_reg = GETDATA(curr, unsigned int); + pi_register.pi_bsd_dom1_lat_reg = GETDATA(curr, unsigned int); + pi_register.pi_bsd_dom1_pwd_reg = GETDATA(curr, unsigned int); + pi_register.pi_bsd_dom1_pgs_reg = GETDATA(curr, unsigned int); + pi_register.pi_bsd_dom1_rls_reg = GETDATA(curr, unsigned int); + pi_register.pi_bsd_dom2_lat_reg = GETDATA(curr, unsigned int); + pi_register.pi_bsd_dom2_pwd_reg = GETDATA(curr, unsigned int); + pi_register.pi_bsd_dom2_pgs_reg = GETDATA(curr, unsigned int); + pi_register.pi_bsd_dom2_rls_reg = GETDATA(curr, unsigned int); + read_func(handle, &pi_register, sizeof(PI_register)); + + // ri_register + ri_register.ri_mode = GETDATA(curr, unsigned int); + ri_register.ri_config = GETDATA(curr, unsigned int); + ri_register.ri_current_load = GETDATA(curr, unsigned int); + ri_register.ri_select = GETDATA(curr, unsigned int); + ri_register.ri_refresh = GETDATA(curr, unsigned int); + ri_register.ri_latency = GETDATA(curr, unsigned int); + ri_register.ri_error = GETDATA(curr, unsigned int); + ri_register.ri_werror = GETDATA(curr, unsigned int); + + // si_register + si_register.si_dram_addr = GETDATA(curr, unsigned int); + si_register.si_pif_addr_rd64b = GETDATA(curr, unsigned int); + si_register.si_pif_addr_wr64b = GETDATA(curr, unsigned int); + si_register.si_stat = GETDATA(curr, unsigned int); + + // tlb + memset(tlb_LUT_r, 0, 0x400000); + memset(tlb_LUT_w, 0, 0x400000); + for (i=0; i < 32; i++) + { + unsigned int MyPageMask, MyEntryHi, MyEntryLo0, MyEntryLo1; + + (void)GETDATA(curr, unsigned int); // Dummy read - EntryDefined + MyPageMask = GETDATA(curr, unsigned int); + MyEntryHi = GETDATA(curr, unsigned int); + MyEntryLo0 = GETDATA(curr, unsigned int); + MyEntryLo1 = GETDATA(curr, unsigned int); + + // This is copied from TLBWI instruction + tlb_e[i].g = (MyEntryLo0 & MyEntryLo1 & 1); + tlb_e[i].pfn_even = (MyEntryLo0 & 0x3FFFFFC0) >> 6; + tlb_e[i].pfn_odd = (MyEntryLo1 & 0x3FFFFFC0) >> 6; + tlb_e[i].c_even = (MyEntryLo0 & 0x38) >> 3; + tlb_e[i].c_odd = (MyEntryLo1 & 0x38) >> 3; + tlb_e[i].d_even = (MyEntryLo0 & 0x4) >> 2; + tlb_e[i].d_odd = (MyEntryLo1 & 0x4) >> 2; + tlb_e[i].v_even = (MyEntryLo0 & 0x2) >> 1; + tlb_e[i].v_odd = (MyEntryLo1 & 0x2) >> 1; + tlb_e[i].asid = (MyEntryHi & 0xFF); + tlb_e[i].vpn2 = (MyEntryHi & 0xFFFFE000) >> 13; + //tlb_e[i].r = (MyEntryHi & 0xC000000000000000LL) >> 62; + tlb_e[i].mask = (MyPageMask & 0x1FFE000) >> 13; + + tlb_e[i].start_even = tlb_e[i].vpn2 << 13; + tlb_e[i].end_even = tlb_e[i].start_even+ + (tlb_e[i].mask << 12) + 0xFFF; + tlb_e[i].phys_even = tlb_e[i].pfn_even << 12; + + tlb_e[i].start_odd = tlb_e[i].end_even+1; + tlb_e[i].end_odd = tlb_e[i].start_odd+ + (tlb_e[i].mask << 12) + 0xFFF; + tlb_e[i].phys_odd = tlb_e[i].pfn_odd << 12; + + tlb_map(&tlb_e[i]); + } + + // pif ram + COPYARRAY(PIF_RAM, curr, unsigned char, 0x40); + + // RDRAM + memset(rdram, 0, 0x800000); + COPYARRAY(rdram, curr, unsigned int, SaveRDRAMSize/4); + + // DMEM + COPYARRAY(SP_DMEM, curr, unsigned int, 0x1000/4); + + // IMEM + COPYARRAY(SP_IMEM, curr, unsigned int, 0x1000/4); + + // The following values should not matter because we don't have any AI interrupt + // ai_register.next_delay = 0; ai_register.next_len = 0; + // ai_register.current_delay = 0; ai_register.current_len = 0; + + // The following is not available in PJ64 savestate. Keep the values as is. + // dps_register.dps_tbist = 0; dps_register.dps_test_mode = 0; + // dps_register.dps_buftest_addr = 0; dps_register.dps_buftest_data = 0; llbit = 0; + + // No flashram info in pj64 savestate. + init_flashram(); + +#ifdef NEW_DYNAREC + if (r4300emu == CORE_DYNAREC) { + pcaddr = GETDATA(curr, unsigned int); + pending_exception = 1; + invalidate_all_pages(); + } else { + if(r4300emu != CORE_PURE_INTERPRETER) + { + for (i = 0; i < 0x100000; i++) + invalid_code[i] = 1; + } + generic_jump_to(last_addr); + } +#else + if(r4300emu != CORE_PURE_INTERPRETER) + { + for (i = 0; i < 0x100000; i++) + invalid_code[i] = 1; + } + generic_jump_to(last_addr); +#endif + + // assert(savestateData+savestateSize == curr) + + free(savestateData); + return 1; +} + +static int read_data_from_zip(void *zip, void *buffer, size_t length) +{ + return unzReadCurrentFile((unzFile)zip, buffer, (unsigned)length) == length; +} + +static int savestates_load_pj64_zip(char *filepath) +{ + char szFileName[256], szExtraField[256], szComment[256]; + unzFile zipstatefile = NULL; + unz_file_info fileinfo; + int ret = 0; + + /* Open the .zip file. */ + zipstatefile = unzOpen(filepath); + if (zipstatefile == NULL || + unzGoToFirstFile(zipstatefile) != UNZ_OK || + unzGetCurrentFileInfo(zipstatefile, &fileinfo, szFileName, 255, szExtraField, 255, szComment, 255) != UNZ_OK || + unzOpenCurrentFile(zipstatefile) != UNZ_OK) + { + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Zip error. Could not open state file: %s", filepath); + goto clean_and_exit; + } + + if (!savestates_load_pj64(filepath, zipstatefile, read_data_from_zip)) + goto clean_and_exit; + + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "State loaded from: %s", namefrompath(filepath)); + ret = 1; + + clean_and_exit: + if (zipstatefile != NULL) + unzClose(zipstatefile); + return ret; +} + +static int read_data_from_file(void *file, void *buffer, size_t length) +{ + return fread(buffer, 1, length, file) == length; +} + +static int savestates_load_pj64_unc(char *filepath) +{ + FILE *f; + + /* Open the file. */ + f = fopen(filepath, "rb"); + if (f == NULL) + { + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Could not open state file: %s", filepath); + return 0; + } + + if (!savestates_load_pj64(filepath, f, read_data_from_file)) + { + fclose(f); + return 0; + } + + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "State loaded from: %s", namefrompath(filepath)); + fclose(f); + return 1; +} + +static savestates_type savestates_detect_type(char *filepath) +{ + unsigned char magic[4]; + FILE *f = fopen(filepath, "rb"); + if (f == NULL) + { + DebugMessage(M64MSG_STATUS, "Could not open state file %s\n", filepath); + return savestates_type_unknown; + } + + if (fread(magic, 1, 4, f) != 4) + { + fclose(f); + DebugMessage(M64MSG_STATUS, "Could not read from state file %s\n", filepath); + return savestates_type_unknown; + } + + fclose(f); + + if (magic[0] == 0x1f && magic[1] == 0x8b) // GZIP header + return savestates_type_m64p; + else if (memcmp(magic, "PK\x03\x04", 4) == 0) // ZIP header + return savestates_type_pj64_zip; + else if (memcmp(magic, pj64_magic, 4) == 0) // PJ64 header + return savestates_type_pj64_unc; + else + { + DebugMessage(M64MSG_STATUS, "Unknown state file type %s\n", filepath); + return savestates_type_unknown; + } +} + +int savestates_load(void) +{ + FILE *fPtr = NULL; + char *filepath = NULL; + int ret = 0; + + if (fname == NULL) // For slots, autodetect the savestate type + { + // try M64P type first + type = savestates_type_m64p; + filepath = savestates_generate_path(type); + fPtr = fopen(filepath, "rb"); // can I open this? + if (fPtr == NULL) + { + free(filepath); + // try PJ64 zipped type second + type = savestates_type_pj64_zip; + filepath = savestates_generate_path(type); + fPtr = fopen(filepath, "rb"); // can I open this? + if (fPtr == NULL) + { + free(filepath); + // finally, try PJ64 uncompressed + type = savestates_type_pj64_unc; + filepath = savestates_generate_path(type); + fPtr = fopen(filepath, "rb"); // can I open this? + if (fPtr == NULL) + { + free(filepath); + filepath = NULL; + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "No Mupen64Plus/PJ64 state file found for slot %i", slot); + type = savestates_type_unknown; + } + } + } + } + else // filename of state file to load was set explicitly in 'fname' + { + // detect type if unknown + if (type == savestates_type_unknown) + { + type = savestates_detect_type(fname); + } + filepath = savestates_generate_path(type); + if (filepath != NULL) + fPtr = fopen(filepath, "rb"); // can I open this? + if (fPtr == NULL) + { + if (filepath != NULL) + free(filepath); + filepath = NULL; + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Failed to open savestate file %s", filepath); + } + } + if (fPtr != NULL) + fclose(fPtr); + + if (filepath != NULL) + { + switch (type) + { + case savestates_type_m64p: ret = savestates_load_m64p(filepath); break; + case savestates_type_pj64_zip: ret = savestates_load_pj64_zip(filepath); break; + case savestates_type_pj64_unc: ret = savestates_load_pj64_unc(filepath); break; + default: ret = 0; break; + } + free(filepath); + filepath = NULL; + } + + // deliver callback to indicate completion of state loading operation + StateChanged(M64CORE_STATE_LOADCOMPLETE, ret); + + savestates_clear_job(); + + return ret; +} + +static void savestates_save_m64p_work(struct work_struct *work) +{ + gzFile f; + struct savestate_work *save = container_of(work, struct savestate_work, work); + + SDL_LockMutex(savestates_lock); + + // Write the state to a GZIP file + f = gzopen(save->filepath, "wb"); + + if (f==NULL) + { + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Could not open state file: %s", save->filepath); + free(save->data); + return; + } + + if (gzwrite(f, save->data, save->size) != save->size) + { + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Could not write data to state file: %s", save->filepath); + gzclose(f); + free(save->data); + return; + } + + gzclose(f); + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Saved state to: %s", namefrompath(save->filepath)); + free(save->data); + free(save->filepath); + free(save); + + SDL_UnlockMutex(savestates_lock); +} + +static int savestates_save_m64p(char *filepath) +{ + unsigned char outbuf[4]; + int i; + + char queue[1024]; + int queuelength; + + struct savestate_work *save; + char *curr; + + save = malloc(sizeof(*save)); + if (!save) { + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Insufficient memory to save state."); + return 0; + } + + save->filepath = strdup(filepath); + + if(autoinc_save_slot) + savestates_inc_slot(); + + queuelength = save_eventqueue_infos(queue); + + // Allocate memory for the save state data + save->size = 16788288 + queuelength; + save->data = curr = malloc(save->size); + if (save->data == NULL) + { + free(save->filepath); + free(save); + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Insufficient memory to save state."); + return 0; + } + + // Write the save state data to memory + PUTARRAY(savestate_magic, curr, unsigned char, 8); + + outbuf[0] = (savestate_latest_version >> 24) & 0xff; + outbuf[1] = (savestate_latest_version >> 16) & 0xff; + outbuf[2] = (savestate_latest_version >> 8) & 0xff; + outbuf[3] = (savestate_latest_version >> 0) & 0xff; + PUTARRAY(outbuf, curr, unsigned char, 4); + + PUTARRAY(ROM_SETTINGS.MD5, curr, char, 32); + + PUTDATA(curr, unsigned int, rdram_register.rdram_config); + PUTDATA(curr, unsigned int, rdram_register.rdram_device_id); + PUTDATA(curr, unsigned int, rdram_register.rdram_delay); + PUTDATA(curr, unsigned int, rdram_register.rdram_mode); + PUTDATA(curr, unsigned int, rdram_register.rdram_ref_interval); + PUTDATA(curr, unsigned int, rdram_register.rdram_ref_row); + PUTDATA(curr, unsigned int, rdram_register.rdram_ras_interval); + PUTDATA(curr, unsigned int, rdram_register.rdram_min_interval); + PUTDATA(curr, unsigned int, rdram_register.rdram_addr_select); + PUTDATA(curr, unsigned int, rdram_register.rdram_device_manuf); + + PUTDATA(curr, unsigned int, MI_register.w_mi_init_mode_reg); + PUTDATA(curr, unsigned int, MI_register.mi_init_mode_reg); + PUTDATA(curr, unsigned char, MI_register.mi_init_mode_reg & 0x7F); + PUTDATA(curr, unsigned char, (MI_register.mi_init_mode_reg & 0x80) != 0); + PUTDATA(curr, unsigned char, (MI_register.mi_init_mode_reg & 0x100) != 0); + PUTDATA(curr, unsigned char, (MI_register.mi_init_mode_reg & 0x200) != 0); + PUTDATA(curr, unsigned int, MI_register.mi_version_reg); + PUTDATA(curr, unsigned int, MI_register.mi_intr_reg); + PUTDATA(curr, unsigned int, MI_register.mi_intr_mask_reg); + PUTDATA(curr, unsigned int, MI_register.w_mi_intr_mask_reg); + PUTDATA(curr, unsigned char, (MI_register.mi_intr_mask_reg & 0x1) != 0); + PUTDATA(curr, unsigned char, (MI_register.mi_intr_mask_reg & 0x2) != 0); + PUTDATA(curr, unsigned char, (MI_register.mi_intr_mask_reg & 0x4) != 0); + PUTDATA(curr, unsigned char, (MI_register.mi_intr_mask_reg & 0x8) != 0); + PUTDATA(curr, unsigned char, (MI_register.mi_intr_mask_reg & 0x10) != 0); + PUTDATA(curr, unsigned char, (MI_register.mi_intr_mask_reg & 0x20) != 0); + PUTDATA(curr, unsigned short, 0); // Padding from old implementation + + PUTDATA(curr, unsigned int, pi_register.pi_dram_addr_reg); + PUTDATA(curr, unsigned int, pi_register.pi_cart_addr_reg); + PUTDATA(curr, unsigned int, pi_register.pi_rd_len_reg); + PUTDATA(curr, unsigned int, pi_register.pi_wr_len_reg); + PUTDATA(curr, unsigned int, pi_register.read_pi_status_reg); + PUTDATA(curr, unsigned int, pi_register.pi_bsd_dom1_lat_reg); + PUTDATA(curr, unsigned int, pi_register.pi_bsd_dom1_pwd_reg); + PUTDATA(curr, unsigned int, pi_register.pi_bsd_dom1_pgs_reg); + PUTDATA(curr, unsigned int, pi_register.pi_bsd_dom1_rls_reg); + PUTDATA(curr, unsigned int, pi_register.pi_bsd_dom2_lat_reg); + PUTDATA(curr, unsigned int, pi_register.pi_bsd_dom2_pwd_reg); + PUTDATA(curr, unsigned int, pi_register.pi_bsd_dom2_pgs_reg); + PUTDATA(curr, unsigned int, pi_register.pi_bsd_dom2_rls_reg); + + PUTDATA(curr, unsigned int, sp_register.sp_mem_addr_reg); + PUTDATA(curr, unsigned int, sp_register.sp_dram_addr_reg); + PUTDATA(curr, unsigned int, sp_register.sp_rd_len_reg); + PUTDATA(curr, unsigned int, sp_register.sp_wr_len_reg); + PUTDATA(curr, unsigned int, sp_register.w_sp_status_reg); + PUTDATA(curr, unsigned int, sp_register.sp_status_reg); + PUTDATA(curr, unsigned char, (sp_register.sp_status_reg & 0x1) != 0); + PUTDATA(curr, unsigned char, (sp_register.sp_status_reg & 0x2) != 0); + PUTDATA(curr, unsigned char, (sp_register.sp_status_reg & 0x4) != 0); + PUTDATA(curr, unsigned char, (sp_register.sp_status_reg & 0x8) != 0); + PUTDATA(curr, unsigned char, (sp_register.sp_status_reg & 0x10) != 0); + PUTDATA(curr, unsigned char, (sp_register.sp_status_reg & 0x20) != 0); + PUTDATA(curr, unsigned char, (sp_register.sp_status_reg & 0x40) != 0); + PUTDATA(curr, unsigned char, (sp_register.sp_status_reg & 0x80) != 0); + PUTDATA(curr, unsigned char, (sp_register.sp_status_reg & 0x100) != 0); + PUTDATA(curr, unsigned char, (sp_register.sp_status_reg & 0x200) != 0); + PUTDATA(curr, unsigned char, (sp_register.sp_status_reg & 0x400) != 0); + PUTDATA(curr, unsigned char, (sp_register.sp_status_reg & 0x800) != 0); + PUTDATA(curr, unsigned char, (sp_register.sp_status_reg & 0x1000) != 0); + PUTDATA(curr, unsigned char, (sp_register.sp_status_reg & 0x2000) != 0); + PUTDATA(curr, unsigned char, (sp_register.sp_status_reg & 0x4000) != 0); + PUTDATA(curr, unsigned char, 0); + PUTDATA(curr, unsigned int, sp_register.sp_dma_full_reg); + PUTDATA(curr, unsigned int, sp_register.sp_dma_busy_reg); + PUTDATA(curr, unsigned int, sp_register.sp_semaphore_reg); + + PUTDATA(curr, unsigned int, rsp_register.rsp_pc); + PUTDATA(curr, unsigned int, rsp_register.rsp_ibist); + + PUTDATA(curr, unsigned int, si_register.si_dram_addr); + PUTDATA(curr, unsigned int, si_register.si_pif_addr_rd64b); + PUTDATA(curr, unsigned int, si_register.si_pif_addr_wr64b); + PUTDATA(curr, unsigned int, si_register.si_stat); + + PUTDATA(curr, unsigned int, vi_register.vi_status); + PUTDATA(curr, unsigned int, vi_register.vi_origin); + PUTDATA(curr, unsigned int, vi_register.vi_width); + PUTDATA(curr, unsigned int, vi_register.vi_v_intr); + PUTDATA(curr, unsigned int, vi_register.vi_current); + PUTDATA(curr, unsigned int, vi_register.vi_burst); + PUTDATA(curr, unsigned int, vi_register.vi_v_sync); + PUTDATA(curr, unsigned int, vi_register.vi_h_sync); + PUTDATA(curr, unsigned int, vi_register.vi_leap); + PUTDATA(curr, unsigned int, vi_register.vi_h_start); + PUTDATA(curr, unsigned int, vi_register.vi_v_start); + PUTDATA(curr, unsigned int, vi_register.vi_v_burst); + PUTDATA(curr, unsigned int, vi_register.vi_x_scale); + PUTDATA(curr, unsigned int, vi_register.vi_y_scale); + PUTDATA(curr, unsigned int, vi_register.vi_delay); + + PUTDATA(curr, unsigned int, ri_register.ri_mode); + PUTDATA(curr, unsigned int, ri_register.ri_config); + PUTDATA(curr, unsigned int, ri_register.ri_current_load); + PUTDATA(curr, unsigned int, ri_register.ri_select); + PUTDATA(curr, unsigned int, ri_register.ri_refresh); + PUTDATA(curr, unsigned int, ri_register.ri_latency); + PUTDATA(curr, unsigned int, ri_register.ri_error); + PUTDATA(curr, unsigned int, ri_register.ri_werror); + + PUTDATA(curr, unsigned int, ai_register.ai_dram_addr); + PUTDATA(curr, unsigned int, ai_register.ai_len); + PUTDATA(curr, unsigned int, ai_register.ai_control); + PUTDATA(curr, unsigned int, ai_register.ai_status); + PUTDATA(curr, unsigned int, ai_register.ai_dacrate); + PUTDATA(curr, unsigned int, ai_register.ai_bitrate); + PUTDATA(curr, unsigned int, ai_register.next_delay); + PUTDATA(curr, unsigned int, ai_register.next_len); + PUTDATA(curr, unsigned int, ai_register.current_delay); + PUTDATA(curr, unsigned int, ai_register.current_len); + + PUTDATA(curr, unsigned int, dpc_register.dpc_start); + PUTDATA(curr, unsigned int, dpc_register.dpc_end); + PUTDATA(curr, unsigned int, dpc_register.dpc_current); + PUTDATA(curr, unsigned int, dpc_register.w_dpc_status); + PUTDATA(curr, unsigned int, dpc_register.dpc_status); + PUTDATA(curr, unsigned char, (dpc_register.dpc_status & 0x1) != 0); + PUTDATA(curr, unsigned char, (dpc_register.dpc_status & 0x2) != 0); + PUTDATA(curr, unsigned char, (dpc_register.dpc_status & 0x4) != 0); + PUTDATA(curr, unsigned char, (dpc_register.dpc_status & 0x8) != 0); + PUTDATA(curr, unsigned char, (dpc_register.dpc_status & 0x10) != 0); + PUTDATA(curr, unsigned char, (dpc_register.dpc_status & 0x20) != 0); + PUTDATA(curr, unsigned char, (dpc_register.dpc_status & 0x40) != 0); + PUTDATA(curr, unsigned char, (dpc_register.dpc_status & 0x80) != 0); + PUTDATA(curr, unsigned char, (dpc_register.dpc_status & 0x100) != 0); + PUTDATA(curr, unsigned char, (dpc_register.dpc_status & 0x200) != 0); + PUTDATA(curr, unsigned char, (dpc_register.dpc_status & 0x400) != 0); + PUTDATA(curr, unsigned char, 0); + PUTDATA(curr, unsigned int, dpc_register.dpc_clock); + PUTDATA(curr, unsigned int, dpc_register.dpc_bufbusy); + PUTDATA(curr, unsigned int, dpc_register.dpc_pipebusy); + PUTDATA(curr, unsigned int, dpc_register.dpc_tmem); + + PUTDATA(curr, unsigned int, dps_register.dps_tbist); + PUTDATA(curr, unsigned int, dps_register.dps_test_mode); + PUTDATA(curr, unsigned int, dps_register.dps_buftest_addr); + PUTDATA(curr, unsigned int, dps_register.dps_buftest_data); + + PUTARRAY(rdram, curr, unsigned int, 0x800000/4); + PUTARRAY(SP_DMEM, curr, unsigned int, 0x1000/4); + PUTARRAY(SP_IMEM, curr, unsigned int, 0x1000/4); + PUTARRAY(PIF_RAM, curr, unsigned char, 0x40); + + PUTDATA(curr, int, flashram_info.use_flashram); + PUTDATA(curr, int, flashram_info.mode); + PUTDATA(curr, unsigned long long, flashram_info.status); + PUTDATA(curr, unsigned int, flashram_info.erase_offset); + PUTDATA(curr, unsigned int, flashram_info.write_pointer); + + PUTARRAY(tlb_LUT_r, curr, unsigned int, 0x100000); + PUTARRAY(tlb_LUT_w, curr, unsigned int, 0x100000); + + PUTDATA(curr, unsigned int, llbit); + PUTARRAY(reg, curr, long long int, 32); + PUTARRAY(reg_cop0, curr, unsigned int, 32); + PUTDATA(curr, long long int, lo); + PUTDATA(curr, long long int, hi); + + if ((Status & 0x04000000) == 0) // FR bit == 0 means 32-bit (MIPS I) FGR mode + shuffle_fpr_data(0, 0x04000000); // shuffle data into 64-bit register format for storage + PUTARRAY(reg_cop1_fgr_64, curr, long long int, 32); + if ((Status & 0x04000000) == 0) + shuffle_fpr_data(0x04000000, 0); // put it back in 32-bit mode + + PUTDATA(curr, int, FCR0); + PUTDATA(curr, int, FCR31); + for (i = 0; i < 32; i++) + { + PUTDATA(curr, short, tlb_e[i].mask); + PUTDATA(curr, short, 0); + PUTDATA(curr, int, tlb_e[i].vpn2); + PUTDATA(curr, char, tlb_e[i].g); + PUTDATA(curr, unsigned char, tlb_e[i].asid); + PUTDATA(curr, short, 0); + PUTDATA(curr, int, tlb_e[i].pfn_even); + PUTDATA(curr, char, tlb_e[i].c_even); + PUTDATA(curr, char, tlb_e[i].d_even); + PUTDATA(curr, char, tlb_e[i].v_even); + PUTDATA(curr, char, 0); + PUTDATA(curr, int, tlb_e[i].pfn_odd); + PUTDATA(curr, char, tlb_e[i].c_odd); + PUTDATA(curr, char, tlb_e[i].d_odd); + PUTDATA(curr, char, tlb_e[i].v_odd); + PUTDATA(curr, char, tlb_e[i].r); + + PUTDATA(curr, unsigned int, tlb_e[i].start_even); + PUTDATA(curr, unsigned int, tlb_e[i].end_even); + PUTDATA(curr, unsigned int, tlb_e[i].phys_even); + PUTDATA(curr, unsigned int, tlb_e[i].start_odd); + PUTDATA(curr, unsigned int, tlb_e[i].end_odd); + PUTDATA(curr, unsigned int, tlb_e[i].phys_odd); + } +#ifdef NEW_DYNAREC + if (r4300emu == CORE_DYNAREC) + PUTDATA(curr, unsigned int, pcaddr); + else + PUTDATA(curr, unsigned int, PC->addr); +#else + PUTDATA(curr, unsigned int, PC->addr); +#endif + + PUTDATA(curr, unsigned int, next_interupt); + PUTDATA(curr, unsigned int, next_vi); + PUTDATA(curr, unsigned int, vi_field); + + to_little_endian_buffer(queue, 4, queuelength/4); + PUTARRAY(queue, curr, char, queuelength); + + // assert(curr == save->data + save->size) + + init_work(&save->work, savestates_save_m64p_work); + queue_work(&save->work); + + return 1; +} + +static int savestates_save_pj64(char *filepath, void *handle, + int (*write_func)(void *, const void *, size_t)) +{ + unsigned int i; + unsigned int SaveRDRAMSize = 0x800000; + + size_t savestateSize; + unsigned char *savestateData, *curr; + + // Allocate memory for the save state data + savestateSize = 8 + SaveRDRAMSize + 0x2754; + savestateData = curr = (unsigned char *)malloc(savestateSize); + if (savestateData == NULL) + { + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Insufficient memory to save state."); + return 0; + } + + // Write the save state data in memory + PUTARRAY(pj64_magic, curr, unsigned char, 4); + PUTDATA(curr, unsigned int, SaveRDRAMSize); + PUTARRAY(rom, curr, unsigned int, 0x40/4); + PUTDATA(curr, unsigned int, get_event(VI_INT) - reg_cop0[9]); // vi_timer +#ifdef NEW_DYNAREC + if (r4300emu == CORE_DYNAREC) + PUTDATA(curr, unsigned int, pcaddr); + else + PUTDATA(curr, unsigned int, PC->addr); +#else + PUTDATA(curr, unsigned int, PC->addr); +#endif + PUTARRAY(reg, curr, long long int, 32); + if ((Status & 0x04000000) == 0) // TODO not sure how pj64 handles this + shuffle_fpr_data(0x04000000, 0); + PUTARRAY(reg_cop1_fgr_64, curr, long long int, 32); + if ((Status & 0x04000000) == 0) // TODO not sure how pj64 handles this + shuffle_fpr_data(0x04000000, 0); + PUTARRAY(reg_cop0, curr, unsigned int, 32); + PUTDATA(curr, int, FCR0); + for (i = 0; i < 30; i++) + PUTDATA(curr, int, 0); // FCR1-30 not implemented + PUTDATA(curr, int, FCR31); + PUTDATA(curr, long long int, hi); + PUTDATA(curr, long long int, lo); + + PUTDATA(curr, unsigned int, rdram_register.rdram_config); + PUTDATA(curr, unsigned int, rdram_register.rdram_device_id); + PUTDATA(curr, unsigned int, rdram_register.rdram_delay); + PUTDATA(curr, unsigned int, rdram_register.rdram_mode); + PUTDATA(curr, unsigned int, rdram_register.rdram_ref_interval); + PUTDATA(curr, unsigned int, rdram_register.rdram_ref_row); + PUTDATA(curr, unsigned int, rdram_register.rdram_ras_interval); + PUTDATA(curr, unsigned int, rdram_register.rdram_min_interval); + PUTDATA(curr, unsigned int, rdram_register.rdram_addr_select); + PUTDATA(curr, unsigned int, rdram_register.rdram_device_manuf); + + PUTDATA(curr, unsigned int, sp_register.sp_mem_addr_reg); + PUTDATA(curr, unsigned int, sp_register.sp_dram_addr_reg); + PUTDATA(curr, unsigned int, sp_register.sp_rd_len_reg); + PUTDATA(curr, unsigned int, sp_register.sp_wr_len_reg); + PUTDATA(curr, unsigned int, sp_register.sp_status_reg); + PUTDATA(curr, unsigned int, sp_register.sp_dma_full_reg); + PUTDATA(curr, unsigned int, sp_register.sp_dma_busy_reg); + PUTDATA(curr, unsigned int, sp_register.sp_semaphore_reg); + + PUTDATA(curr, unsigned int, rsp_register.rsp_pc); + PUTDATA(curr, unsigned int, rsp_register.rsp_ibist); + + PUTDATA(curr, unsigned int, dpc_register.dpc_start); + PUTDATA(curr, unsigned int, dpc_register.dpc_end); + PUTDATA(curr, unsigned int, dpc_register.dpc_current); + PUTDATA(curr, unsigned int, dpc_register.dpc_status); + PUTDATA(curr, unsigned int, dpc_register.dpc_clock); + PUTDATA(curr, unsigned int, dpc_register.dpc_bufbusy); + PUTDATA(curr, unsigned int, dpc_register.dpc_pipebusy); + PUTDATA(curr, unsigned int, dpc_register.dpc_tmem); + PUTDATA(curr, unsigned int, 0); // ? + PUTDATA(curr, unsigned int, 0); // ? + + PUTDATA(curr, unsigned int, MI_register.mi_init_mode_reg); //TODO Secial handling in pj64 + PUTDATA(curr, unsigned int, MI_register.mi_version_reg); + PUTDATA(curr, unsigned int, MI_register.mi_intr_reg); + PUTDATA(curr, unsigned int, MI_register.mi_intr_mask_reg); + + PUTDATA(curr, unsigned int, vi_register.vi_status); + PUTDATA(curr, unsigned int, vi_register.vi_origin); + PUTDATA(curr, unsigned int, vi_register.vi_width); + PUTDATA(curr, unsigned int, vi_register.vi_v_intr); + PUTDATA(curr, unsigned int, vi_register.vi_current); + PUTDATA(curr, unsigned int, vi_register.vi_burst); + PUTDATA(curr, unsigned int, vi_register.vi_v_sync); + PUTDATA(curr, unsigned int, vi_register.vi_h_sync); + PUTDATA(curr, unsigned int, vi_register.vi_leap); + PUTDATA(curr, unsigned int, vi_register.vi_h_start); + PUTDATA(curr, unsigned int, vi_register.vi_v_start); + PUTDATA(curr, unsigned int, vi_register.vi_v_burst); + PUTDATA(curr, unsigned int, vi_register.vi_x_scale); + PUTDATA(curr, unsigned int, vi_register.vi_y_scale); + + PUTDATA(curr, unsigned int, ai_register.ai_dram_addr); + PUTDATA(curr, unsigned int, ai_register.ai_len); + PUTDATA(curr, unsigned int, ai_register.ai_control); + PUTDATA(curr, unsigned int, ai_register.ai_status); + PUTDATA(curr, unsigned int, ai_register.ai_dacrate); + PUTDATA(curr, unsigned int, ai_register.ai_bitrate); + + PUTDATA(curr, unsigned int, pi_register.pi_dram_addr_reg); + PUTDATA(curr, unsigned int, pi_register.pi_cart_addr_reg); + PUTDATA(curr, unsigned int, pi_register.pi_rd_len_reg); + PUTDATA(curr, unsigned int, pi_register.pi_wr_len_reg); + PUTDATA(curr, unsigned int, pi_register.read_pi_status_reg); + PUTDATA(curr, unsigned int, pi_register.pi_bsd_dom1_lat_reg); + PUTDATA(curr, unsigned int, pi_register.pi_bsd_dom1_pwd_reg); + PUTDATA(curr, unsigned int, pi_register.pi_bsd_dom1_pgs_reg); + PUTDATA(curr, unsigned int, pi_register.pi_bsd_dom1_rls_reg); + PUTDATA(curr, unsigned int, pi_register.pi_bsd_dom2_lat_reg); + PUTDATA(curr, unsigned int, pi_register.pi_bsd_dom2_pwd_reg); + PUTDATA(curr, unsigned int, pi_register.pi_bsd_dom2_pgs_reg); + PUTDATA(curr, unsigned int, pi_register.pi_bsd_dom2_rls_reg); + + PUTDATA(curr, unsigned int, ri_register.ri_mode); + PUTDATA(curr, unsigned int, ri_register.ri_config); + PUTDATA(curr, unsigned int, ri_register.ri_current_load); + PUTDATA(curr, unsigned int, ri_register.ri_select); + PUTDATA(curr, unsigned int, ri_register.ri_refresh); + PUTDATA(curr, unsigned int, ri_register.ri_latency); + PUTDATA(curr, unsigned int, ri_register.ri_error); + PUTDATA(curr, unsigned int, ri_register.ri_werror); + + PUTDATA(curr, unsigned int, si_register.si_dram_addr); + PUTDATA(curr, unsigned int, si_register.si_pif_addr_rd64b); + PUTDATA(curr, unsigned int, si_register.si_pif_addr_wr64b); + PUTDATA(curr, unsigned int, si_register.si_stat); + + for (i=0; i < 32;i++) + { + // From TLBR + unsigned int EntryDefined, MyPageMask, MyEntryHi, MyEntryLo0, MyEntryLo1; + EntryDefined = tlb_e[i].v_even || tlb_e[i].v_odd; + MyPageMask = tlb_e[i].mask << 13; + MyEntryHi = ((tlb_e[i].vpn2 << 13) | tlb_e[i].asid); + MyEntryLo0 = (tlb_e[i].pfn_even << 6) | (tlb_e[i].c_even << 3) + | (tlb_e[i].d_even << 2) | (tlb_e[i].v_even << 1) + | tlb_e[i].g; + MyEntryLo1 = (tlb_e[i].pfn_odd << 6) | (tlb_e[i].c_odd << 3) + | (tlb_e[i].d_odd << 2) | (tlb_e[i].v_odd << 1) + | tlb_e[i].g; + + PUTDATA(curr, unsigned int, EntryDefined); + PUTDATA(curr, unsigned int, MyPageMask); + PUTDATA(curr, unsigned int, MyEntryHi); + PUTDATA(curr, unsigned int, MyEntryLo0); + PUTDATA(curr, unsigned int, MyEntryLo1); + } + + PUTARRAY(PIF_RAM, curr, unsigned char, 0x40); + + PUTARRAY(rdram, curr, unsigned int, SaveRDRAMSize/4); + PUTARRAY(SP_DMEM, curr, unsigned int, 0x1000/4); + PUTARRAY(SP_IMEM, curr, unsigned int, 0x1000/4); + + // Write the save state data to the output + if (!write_func(handle, savestateData, savestateSize)) + { + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Couldn't write data to Project64 state file %s.", filepath); + free(savestateData); + return 0; + } + + // assert(savestateData+savestateSize == curr) + free(savestateData); + return 1; +} + +static int write_data_to_zip(void *zip, const void *buffer, size_t length) +{ + return zipWriteInFileInZip((zipFile)zip, buffer, (unsigned)length) == ZIP_OK; +} + +static int savestates_save_pj64_zip(char *filepath) +{ + int retval; + zipFile zipfile = NULL; + + zipfile = zipOpen(filepath, APPEND_STATUS_CREATE); + if(zipfile == NULL) + { + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Could not create PJ64 state file: %s", filepath); + goto clean_and_exit; + } + + retval = zipOpenNewFileInZip(zipfile, namefrompath(filepath), NULL, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION); + if(retval != ZIP_OK) + { + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Zip error. Could not create state file: %s", filepath); + goto clean_and_exit; + } + + if (!savestates_save_pj64(filepath, zipfile, write_data_to_zip)) + goto clean_and_exit; + + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Saved state to: %s", namefrompath(filepath)); + + clean_and_exit: + if (zipfile != NULL) + { + zipCloseFileInZip(zipfile); // This may fail, but we don't care + zipClose(zipfile, ""); + } + return 1; +} + +static int write_data_to_file(void *file, const void *buffer, size_t length) +{ + return fwrite(buffer, 1, length, (FILE *)file) == length; +} + +static int savestates_save_pj64_unc(char *filepath) +{ + FILE *f; + + f = fopen(filepath, "wb"); + if (f == NULL) + { + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Could not create PJ64 state file: %s", filepath); + return 0; + } + + if (!savestates_save_pj64(filepath, f, write_data_to_file)) + { + fclose(f); + return 0; + } + + main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "Saved state to: %s", namefrompath(filepath)); + fclose(f); + return 1; +} + +int savestates_save(void) +{ + char *filepath; + int ret = 0; + + /* Can only save PJ64 savestates on VI / COMPARE interrupt. + Otherwise try again in a little while. */ + if ((type == savestates_type_pj64_zip || + type == savestates_type_pj64_unc) && + get_next_event_type() > COMPARE_INT) + return 0; + + if (fname != NULL && type == savestates_type_unknown) + type = savestates_type_m64p; + else if (fname == NULL) // Always save slots in M64P format + type = savestates_type_m64p; + + filepath = savestates_generate_path(type); + if (filepath != NULL) + { + switch (type) + { + case savestates_type_m64p: ret = savestates_save_m64p(filepath); break; + case savestates_type_pj64_zip: ret = savestates_save_pj64_zip(filepath); break; + case savestates_type_pj64_unc: ret = savestates_save_pj64_unc(filepath); break; + default: ret = 0; break; + } + free(filepath); + } + + // deliver callback to indicate completion of state saving operation + StateChanged(M64CORE_STATE_SAVECOMPLETE, ret); + + savestates_clear_job(); + return ret; +} + +void savestates_init(void) +{ + savestates_lock = SDL_CreateMutex(); + if (!savestates_lock) { + DebugMessage(M64MSG_ERROR, "Could not create savestates list lock"); + return; + } +} + +void savestates_deinit(void) +{ + SDL_DestroyMutex(savestates_lock); + savestates_clear_job(); +}