Path: blob/master/libmupen64plus/mupen64plus-core/tools/savestate_convert.c
2 views
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1* Mupen64plus - savestate_convert.c *2* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *3* Copyright (C) 2008 Richard Goedeken *4* *5* This program is free software; you can redistribute it and/or modify *6* it under the terms of the GNU General Public License as published by *7* the Free Software Foundation; either version 2 of the License, or *8* (at your option) any later version. *9* *10* This program is distributed in the hope that it will be useful, *11* but WITHOUT ANY WARRANTY; without even the implied warranty of *12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *13* GNU General Public License for more details. *14* *15* You should have received a copy of the GNU General Public License *16* along with this program; if not, write to the *17* Free Software Foundation, Inc., *18* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *19* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */2021#include <zlib.h>22#include <stdio.h>23#include <stdlib.h>24#include <string.h>2526/* savestate file header: magic number and version number */27const char *savestate_magic = "M64+SAVE";28const int savestate_newest_version = 0x00010000; // 1.02930/* Data field lengths */3132#define SIZE_REG_RDRAM 4033#define SIZE_REG_MIPS 3634#define SIZE_REG_PI 5235#define SIZE_REG_SP 5236#define SIZE_REG_RSP 837#define SIZE_REG_SI 1638#define SIZE_REG_VI 6039#define SIZE_REG_RI 3240#define SIZE_REG_AI 4041#define SIZE_REG_DPC 4842#define SIZE_REG_DPS 164344#define SIZE_FLASHRAM_INFO 2445#define SIZE_TLB_ENTRY 524647#define SIZE_MAX_EVENTQUEUE 10244849/* Arrays and pointers for savestate data */5051char rom_md5[32];5253char rdram_register[SIZE_REG_RDRAM];54char mips_register[SIZE_REG_MIPS];55char pi_register[SIZE_REG_PI];56char sp_register[SIZE_REG_SP];57char rsp_register[SIZE_REG_RSP];58char si_register[SIZE_REG_SI];59char vi_register[SIZE_REG_VI];60char ri_register[SIZE_REG_RI];61char ai_register[SIZE_REG_AI];62char dpc_register[SIZE_REG_DPC];63char dps_register[SIZE_REG_DPS];6465char *rdram; /* 0x800000 bytes */66char SP_DMEM[0x1000];67char SP_IMEM[0x1000];68char PIF_RAM[0x40];6970char flashram[SIZE_FLASHRAM_INFO];7172char *tlb_LUT_r; /* 0x400000 bytes */73char *tlb_LUT_w; /* 0x400000 bytes */7475char llbit[4];76char reg[32][8];77char reg_cop0[32][4];78char lo[8];79char hi[8];80char reg_cop1_fgr_64[32][8];81char FCR0[4];82char FCR31[4];83char tlb_e[32][SIZE_TLB_ENTRY];84char PCaddr[4];8586char next_interupt[4];87char next_vi[4];88char vi_field[4];8990char eventqueue[SIZE_MAX_EVENTQUEUE];9192/* savestate data parameters calculated from file contents */93int queuelength = 0;9495/* Forward declarations for functions */96void printhelp(const char *progname);9798int allocate_memory(void);99void free_memory(void);100101int load_original_mupen64(const char *filename);102int save_newest(const char *filename);103104/* Main Function - parse arguments, check version, load state file, overwrite state file with new one */105int main(int argc, char *argv[])106{107FILE *pfTest;108gzFile f;109char *filename;110char magictag[8];111unsigned char inbuf[4];112int (*load_function)(const char *) = NULL;113int iVersion;114115/* start by parsing the command-line arguments */116if (argc != 2 || strncmp(argv[1], "-h", 2) == 0 || strncmp(argv[1], "--help", 6) == 0)117{118printhelp(argv[0]);119return 1;120}121filename = argv[1];122pfTest = fopen(filename, "rb");123if (pfTest == NULL)124{125printf("Error: cannot open savestate file '%s' for reading.\n", filename);126return 2;127}128fclose(pfTest);129130/* try to determine the version of this savestate file */131f = gzopen(filename, "rb");132if (f == NULL)133{134printf("Error: state file '%s' is corrupt\n", filename);135return 3;136}137if (gzread(f, magictag, 8) != 8 || gzread(f, inbuf, 4) != 4)138{139printf("Error: state file '%s' is corrupt: end of savestate file while reading header.\n", filename);140gzclose(f);141return 4;142}143gzclose(f);144iVersion = inbuf[0];145iVersion = (iVersion << 8) | inbuf[1];146iVersion = (iVersion << 8) | inbuf[2];147iVersion = (iVersion << 8) | inbuf[3];148149/* determine which type of savestate file to load, based on savestate version */150if (strncmp(magictag, savestate_magic, 8) != 0)151{152printf("Warning: old savestate file format. This is presumed to be from the original Mupen64 or Mupen64Plus version 1.4 or earlier.\n");153load_function = load_original_mupen64;154}155else if (iVersion == savestate_newest_version)156{157printf("This savestate file is already up to date (version %08x)\n", savestate_newest_version);158return 0;159}160else161{162printf("This savestate file uses an unknown version (%08x)\n", iVersion);163return 5;164}165166/* allocate memory for savestate data */167if (allocate_memory() != 0)168{169printf("Error: couldn't allocate memory for savestate data storage.\n");170return 6;171}172173/* load the savestate file */174if (load_function(filename) != 0)175{176free_memory();177return 7;178}179180/* write new updated savestate file */181if (save_newest(filename) != 0)182{183free_memory();184return 8;185}186187/* free the memory and return */188printf("Savestate file '%s' successfully converted to latest version (%08x).\n", filename, savestate_newest_version);189free_memory();190return 0;191}192193void printhelp(const char *progname)194{195printf("%s - convert older Mupen64Plus savestate files to most recent version.\n\n", progname);196printf("Usage: %s [-h] [--help] <savestatepath>\n\n", progname);197printf(" -h, --help: display this message\n");198printf(" <savestatepath>: full path to savestate file which will be overwritten with latest version.\n");199}200201int allocate_memory(void)202{203rdram = malloc(0x800000);204if (rdram == NULL)205return 1;206207tlb_LUT_r = malloc(0x400000);208if (tlb_LUT_r == NULL)209{210free_memory();211return 2;212}213214tlb_LUT_w = malloc(0x400000);215if (tlb_LUT_w == NULL)216{217free_memory();218return 3;219}220221return 0;222}223224void free_memory(void)225{226if (rdram != NULL)227{228free(rdram);229rdram = NULL;230}231232if (tlb_LUT_r != NULL)233{234free(tlb_LUT_r);235tlb_LUT_r = NULL;236}237238if (tlb_LUT_w != NULL)239{240free(tlb_LUT_w);241tlb_LUT_w = NULL;242}243}244245/* State Loading Functions */246int load_original_mupen64(const char *filename)247{248char buffer[4];249int i;250gzFile f;251252f = gzopen(filename, "rb");253254if (f == NULL)255{256printf("Error: savestate file '%s' is corrupt.\n", filename);257return 1;258}259260gzread(f, rom_md5, 32);261262gzread(f, rdram_register, SIZE_REG_RDRAM);263gzread(f, mips_register, SIZE_REG_MIPS);264gzread(f, pi_register, SIZE_REG_PI);265gzread(f, sp_register, SIZE_REG_SP);266gzread(f, rsp_register, SIZE_REG_RSP);267gzread(f, si_register, SIZE_REG_SI);268gzread(f, vi_register, SIZE_REG_VI);269gzread(f, ri_register, SIZE_REG_RI);270gzread(f, ai_register, SIZE_REG_AI);271gzread(f, dpc_register, SIZE_REG_DPC);272gzread(f, dps_register, SIZE_REG_DPS);273274gzread(f, rdram, 0x800000);275gzread(f, SP_DMEM, 0x1000);276gzread(f, SP_IMEM, 0x1000);277gzread(f, PIF_RAM, 0x40);278279gzread(f, flashram, SIZE_FLASHRAM_INFO);280281memset(tlb_LUT_r, 0, 0x400000);282memset(tlb_LUT_w, 0, 0x400000);283gzread(f, tlb_LUT_r, 0x100000);284gzread(f, tlb_LUT_w, 0x100000);285286gzread(f, llbit, 4);287gzread(f, reg, 32*8);288for (i = 0; i < 32; i++)289{290gzread(f, reg_cop0[i], 4);291gzread(f, buffer, 4); /* for compatibility with older versions. */292}293gzread(f, lo, 8);294gzread(f, hi, 8);295gzread(f, reg_cop1_fgr_64[0], 32 * 8);296gzread(f, FCR0, 4);297gzread(f, FCR31, 4);298gzread(f, tlb_e[0], 32 * SIZE_TLB_ENTRY);299gzread(f, PCaddr, 4);300301gzread(f, next_interupt, 4);302gzread(f, next_vi, 4);303gzread(f, vi_field, 4);304305queuelength = 0;306while(queuelength < SIZE_MAX_EVENTQUEUE)307{308if (gzread(f, eventqueue + queuelength, 4) != 4)309{310printf("Error: savestate file '%s' is corrupt.\n", filename);311return 2;312}313if (*((unsigned int*) &eventqueue[queuelength]) == 0xFFFFFFFF)314{315queuelength += 4;316break;317}318gzread(f, eventqueue + queuelength + 4, 4);319queuelength += 8;320}321322if (queuelength >= SIZE_MAX_EVENTQUEUE)323{324printf("Error: savestate file '%s' has event queue larger than %i bytes.\n", filename, SIZE_MAX_EVENTQUEUE);325return 3;326}327328gzclose(f);329return 0;330}331332/* State Saving Functions */333334int save_newest(const char *filename)335{336unsigned char outbuf[4];337gzFile f;338339f = gzopen(filename, "wb");340341/* write magic number */342gzwrite(f, savestate_magic, 8);343344/* write savestate file version in big-endian */345outbuf[0] = (savestate_newest_version >> 24) & 0xff;346outbuf[1] = (savestate_newest_version >> 16) & 0xff;347outbuf[2] = (savestate_newest_version >> 8) & 0xff;348outbuf[3] = (savestate_newest_version >> 0) & 0xff;349gzwrite(f, outbuf, 4);350351gzwrite(f, rom_md5, 32);352353gzwrite(f, rdram_register, SIZE_REG_RDRAM);354gzwrite(f, mips_register, SIZE_REG_MIPS);355gzwrite(f, pi_register, SIZE_REG_PI);356gzwrite(f, sp_register, SIZE_REG_SP);357gzwrite(f, rsp_register, SIZE_REG_RSP);358gzwrite(f, si_register, SIZE_REG_SI);359gzwrite(f, vi_register, SIZE_REG_VI);360gzwrite(f, ri_register, SIZE_REG_RI);361gzwrite(f, ai_register, SIZE_REG_AI);362gzwrite(f, dpc_register, SIZE_REG_DPC);363gzwrite(f, dps_register, SIZE_REG_DPS);364365gzwrite(f, rdram, 0x800000);366gzwrite(f, SP_DMEM, 0x1000);367gzwrite(f, SP_IMEM, 0x1000);368gzwrite(f, PIF_RAM, 0x40);369370gzwrite(f, flashram, SIZE_FLASHRAM_INFO);371372gzwrite(f, tlb_LUT_r, 0x400000);373gzwrite(f, tlb_LUT_w, 0x400000);374375gzwrite(f, llbit, 4);376gzwrite(f, reg[0], 32*8);377gzwrite(f, reg_cop0[0], 32*4);378gzwrite(f, lo, 8);379gzwrite(f, hi, 8);380gzwrite(f, reg_cop1_fgr_64[0], 32*8);381gzwrite(f, FCR0, 4);382gzwrite(f, FCR31, 4);383gzwrite(f, tlb_e[0], 32 * SIZE_TLB_ENTRY);384gzwrite(f, PCaddr, 4);385386gzwrite(f, next_interupt, 4);387gzwrite(f, next_vi, 4);388gzwrite(f, vi_field, 4);389390gzwrite(f, eventqueue, queuelength);391392gzclose(f);393return 0;394}395396397398