File: savestate_convert.c

package info (click to toggle)
mupen64plus-core 2.6.0-2
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 7,760 kB
  • sloc: ansic: 80,337; cpp: 1,404; asm: 1,096; makefile: 765; python: 619; sh: 325; awk: 9
file content (396 lines) | stat: -rw-r--r-- 11,211 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *   Mupen64plus - savestate_convert.c                                     *
 *   Mupen64Plus homepage: https://mupen64plus.org/                        *
 *   Copyright (C) 2008 Richard Goedeken                                   *
 *                                                                         *
 *   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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zlib.h>

/* savestate file header: magic number and version number */
const char *savestate_magic = "M64+SAVE";
const int savestate_newest_version = 0x00010000;  // 1.0

/* Data field lengths */

#define SIZE_REG_RDRAM       40
#define SIZE_REG_MIPS        36
#define SIZE_REG_PI          52
#define SIZE_REG_SP          52
#define SIZE_REG_RSP         8
#define SIZE_REG_SI          16
#define SIZE_REG_VI          60
#define SIZE_REG_RI          32
#define SIZE_REG_AI          40
#define SIZE_REG_DPC         48
#define SIZE_REG_DPS         16

#define SIZE_FLASHRAM_INFO   24
#define SIZE_TLB_ENTRY       52

#define SIZE_MAX_EVENTQUEUE  1024

/* Arrays and pointers for savestate data */

char rom_md5[32];

char rdram_register[SIZE_REG_RDRAM];
char mips_register[SIZE_REG_MIPS];
char pi_register[SIZE_REG_PI];
char sp_register[SIZE_REG_SP];
char rsp_register[SIZE_REG_RSP];
char si_register[SIZE_REG_SI];
char vi_register[SIZE_REG_VI];
char ri_register[SIZE_REG_RI];
char ai_register[SIZE_REG_AI];
char dpc_register[SIZE_REG_DPC];
char dps_register[SIZE_REG_DPS];

char *rdram;       /* 0x800000 bytes */
char SP_DMEM[0x1000];
char SP_IMEM[0x1000];
char PIF_RAM[0x40];

char flashram[SIZE_FLASHRAM_INFO];

char *tlb_LUT_r;   /* 0x400000 bytes */
char *tlb_LUT_w;   /* 0x400000 bytes */

char llbit[4];
char reg[32][8];
char reg_cop0[32][4];
char lo[8];
char hi[8];
char reg_cop1_fgr_64[32][8];
char FCR0[4];
char FCR31[4];
char tlb_e[32][SIZE_TLB_ENTRY];
char PCaddr[4];

char next_interrupt[4];
char next_vi[4];
char vi_field[4];

char eventqueue[SIZE_MAX_EVENTQUEUE];

/* savestate data parameters calculated from file contents */
int queuelength = 0;

/* Forward declarations for functions */
void printhelp(const char *progname);

int allocate_memory(void);
void free_memory(void);

int load_original_mupen64(const char *filename);
int save_newest(const char *filename);

/* Main Function - parse arguments, check version, load state file, overwrite state file with new one */
int main(int argc, char *argv[])
{
    FILE *pfTest;
    gzFile f;
    char *filename;
    char magictag[8];
    unsigned char inbuf[4];
    int (*load_function)(const char *) = NULL;
    int iVersion;

    /* start by parsing the command-line arguments */
    if (argc != 2 || strncmp(argv[1], "-h", 2) == 0 || strncmp(argv[1], "--help", 6) == 0)
    {
        printhelp(argv[0]);
        return 1;
    }
    filename = argv[1];
    pfTest = osal_file_open(filename, "rb");
    if (pfTest == NULL)
    {
        printf("Error: cannot open savestate file '%s' for reading.\n", filename);
        return 2;
    }
    fclose(pfTest);

    /* try to determine the version of this savestate file */
    f = gzopen(filename, "rb");
    if (f == NULL)
    {
        printf("Error: state file '%s' is corrupt\n", filename);
        return 3;
    }
    if (gzread(f, magictag, 8) != 8 || gzread(f, inbuf, 4) != 4)
    {
        printf("Error: state file '%s' is corrupt: end of savestate file while reading header.\n", filename);
        gzclose(f);
        return 4;
    }
    gzclose(f);
    iVersion =                   inbuf[0];
    iVersion = (iVersion << 8) | inbuf[1];
    iVersion = (iVersion << 8) | inbuf[2];
    iVersion = (iVersion << 8) | inbuf[3];

    /* determine which type of savestate file to load, based on savestate version */
    if (strncmp(magictag, savestate_magic, 8) != 0)
    {
        printf("Warning: old savestate file format.  This is presumed to be from the original Mupen64 or Mupen64Plus version 1.4 or earlier.\n");
        load_function = load_original_mupen64;
    }
    else if (iVersion == savestate_newest_version)
    {
        printf("This savestate file is already up to date (version %08x)\n", savestate_newest_version);
        return 0;
    }
    else
    {
        printf("This savestate file uses an unknown version (%08x)\n", iVersion);
        return 5;
    }

    /* allocate memory for savestate data */
    if (allocate_memory() != 0)
    {
        printf("Error: couldn't allocate memory for savestate data storage.\n");
        return 6;
    }

    /* load the savestate file */
    if (load_function(filename) != 0)
    {
        free_memory();
        return 7;
    }

    /* write new updated savestate file */
    if (save_newest(filename) != 0)
    {
        free_memory();
        return 8;
    }

    /* free the memory and return */
    printf("Savestate file '%s' successfully converted to latest version (%08x).\n", filename, savestate_newest_version);
    free_memory();
    return 0;
}

void printhelp(const char *progname)
{
    printf("%s - convert older Mupen64Plus savestate files to most recent version.\n\n", progname);
    printf("Usage: %s [-h] [--help] <savestatepath>\n\n", progname);
    printf("       -h, --help: display this message\n");
    printf("       <savestatepath>: full path to savestate file which will be overwritten with latest version.\n");
}

int allocate_memory(void)
{
    rdram = malloc(0x800000);
    if (rdram == NULL)
        return 1;

    tlb_LUT_r = malloc(0x400000);
    if (tlb_LUT_r == NULL)
    {
        free_memory();
        return 2;
    }

    tlb_LUT_w = malloc(0x400000);
    if (tlb_LUT_w == NULL)
    {
        free_memory();
        return 3;
    }

    return 0;
}

void free_memory(void)
{
    if (rdram != NULL)
    {
        free(rdram);
        rdram = NULL;
    }

    if (tlb_LUT_r != NULL)
    {
        free(tlb_LUT_r);
        tlb_LUT_r = NULL;
    }

    if (tlb_LUT_w != NULL)
    {
        free(tlb_LUT_w);
        tlb_LUT_w = NULL;
    }
}

/* State Loading Functions */
int load_original_mupen64(const char *filename)
{
    char buffer[4];
    int i;
    gzFile f;

    f = gzopen(filename, "rb");

    if (f == NULL)
    {
        printf("Error: savestate file '%s' is corrupt.\n", filename);
        return 1;
    }

    gzread(f, rom_md5, 32);

    gzread(f, rdram_register, SIZE_REG_RDRAM);
    gzread(f, mips_register, SIZE_REG_MIPS);
    gzread(f, pi_register, SIZE_REG_PI);
    gzread(f, sp_register, SIZE_REG_SP);
    gzread(f, rsp_register, SIZE_REG_RSP);
    gzread(f, si_register, SIZE_REG_SI);
    gzread(f, vi_register, SIZE_REG_VI);
    gzread(f, ri_register, SIZE_REG_RI);
    gzread(f, ai_register, SIZE_REG_AI);
    gzread(f, dpc_register, SIZE_REG_DPC);
    gzread(f, dps_register, SIZE_REG_DPS);

    gzread(f, rdram, 0x800000);
    gzread(f, SP_DMEM, 0x1000);
    gzread(f, SP_IMEM, 0x1000);
    gzread(f, PIF_RAM, 0x40);

    gzread(f, flashram, SIZE_FLASHRAM_INFO);

    memset(tlb_LUT_r, 0, 0x400000);
    memset(tlb_LUT_w, 0, 0x400000);
    gzread(f, tlb_LUT_r, 0x100000);
    gzread(f, tlb_LUT_w, 0x100000);

    gzread(f, llbit, 4);
    gzread(f, reg, 32*8);
    for (i = 0; i < 32; i++)
    {
        gzread(f, reg_cop0[i], 4);
        gzread(f, buffer, 4); /* for compatibility with older versions. */
    }
    gzread(f, lo, 8);
    gzread(f, hi, 8);
    gzread(f, reg_cop1_fgr_64[0], 32 * 8);
    gzread(f, FCR0, 4);
    gzread(f, FCR31, 4);
    gzread(f, tlb_e[0], 32 * SIZE_TLB_ENTRY);
    gzread(f, PCaddr, 4);

    gzread(f, next_interrupt, 4);
    gzread(f, next_vi, 4);
    gzread(f, vi_field, 4);

    queuelength = 0;
    while(queuelength < SIZE_MAX_EVENTQUEUE)
    {
        if (gzread(f, eventqueue + queuelength, 4) != 4)
        {
            printf("Error: savestate file '%s' is corrupt.\n", filename);
            return 2;
        }
        if (*((unsigned int*) &eventqueue[queuelength]) == 0xFFFFFFFF)
        {
            queuelength += 4;
            break;
        }
        gzread(f, eventqueue + queuelength + 4, 4);
        queuelength += 8;
    }

    if (queuelength >= SIZE_MAX_EVENTQUEUE)
    {
        printf("Error: savestate file '%s' has event queue larger than %i bytes.\n", filename, SIZE_MAX_EVENTQUEUE);
        return 3;
    }

    gzclose(f);
    return 0;
}

/* State Saving Functions */

int save_newest(const char *filename)
{
    unsigned char outbuf[4];
    gzFile f;

    f = gzopen(filename, "wb");

    /* write magic number */
    gzwrite(f, savestate_magic, 8);

    /* write savestate file version in big-endian */
    outbuf[0] = (savestate_newest_version >> 24) & 0xff;
    outbuf[1] = (savestate_newest_version >> 16) & 0xff;
    outbuf[2] = (savestate_newest_version >>  8) & 0xff;
    outbuf[3] = (savestate_newest_version >>  0) & 0xff;
    gzwrite(f, outbuf, 4);

    gzwrite(f, rom_md5, 32);

    gzwrite(f, rdram_register, SIZE_REG_RDRAM);
    gzwrite(f, mips_register, SIZE_REG_MIPS);
    gzwrite(f, pi_register, SIZE_REG_PI);
    gzwrite(f, sp_register, SIZE_REG_SP);
    gzwrite(f, rsp_register, SIZE_REG_RSP);
    gzwrite(f, si_register, SIZE_REG_SI);
    gzwrite(f, vi_register, SIZE_REG_VI);
    gzwrite(f, ri_register, SIZE_REG_RI);
    gzwrite(f, ai_register, SIZE_REG_AI);
    gzwrite(f, dpc_register, SIZE_REG_DPC);
    gzwrite(f, dps_register, SIZE_REG_DPS);

    gzwrite(f, rdram, 0x800000);
    gzwrite(f, SP_DMEM, 0x1000);
    gzwrite(f, SP_IMEM, 0x1000);
    gzwrite(f, PIF_RAM, 0x40);

    gzwrite(f, flashram, SIZE_FLASHRAM_INFO);

    gzwrite(f, tlb_LUT_r, 0x400000);
    gzwrite(f, tlb_LUT_w, 0x400000);

    gzwrite(f, llbit, 4);
    gzwrite(f, reg[0], 32*8);
    gzwrite(f, reg_cop0[0], 32*4);
    gzwrite(f, lo, 8);
    gzwrite(f, hi, 8);
    gzwrite(f, reg_cop1_fgr_64[0], 32*8);
    gzwrite(f, FCR0, 4);
    gzwrite(f, FCR31, 4);
    gzwrite(f, tlb_e[0], 32 * SIZE_TLB_ENTRY);
    gzwrite(f, PCaddr, 4);

    gzwrite(f, next_interrupt, 4);
    gzwrite(f, next_vi, 4);
    gzwrite(f, vi_field, 4);

    gzwrite(f, eventqueue, queuelength);

    gzclose(f);
    return 0;
}