misc: avoid assertion failure when state save fails
[pcsx_rearmed.git] / libpcsxcore / misc.c
1 /***************************************************************************
2  *   Copyright (C) 2007 Ryan Schultz, PCSX-df Team, PCSX team              *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   51 Franklin Street, Fifth Floor, Boston, MA 02111-1307 USA.           *
18  ***************************************************************************/
19
20 /*
21 * Miscellaneous functions, including savestates and CD-ROM loading.
22 */
23
24 #include <stddef.h>
25 #include <errno.h>
26 #include <assert.h>
27 #include "misc.h"
28 #include "cdrom.h"
29 #include "mdec.h"
30 #include "gpu.h"
31 #include "ppf.h"
32 #include "psxbios.h"
33 #include "database.h"
34 #include <zlib.h>
35
36 char CdromId[10] = "";
37 char CdromLabel[33] = "";
38 int  CdromFrontendId; // for frontend use
39
40 // PSX Executable types
41 #define PSX_EXE     1
42 #define CPE_EXE     2
43 #define COFF_EXE    3
44 #define INVALID_EXE 4
45
46 #define ISODCL(from, to) (to - from + 1)
47
48 struct iso_directory_record {
49         char length                     [ISODCL (1, 1)]; /* 711 */
50         char ext_attr_length            [ISODCL (2, 2)]; /* 711 */
51         char extent                     [ISODCL (3, 10)]; /* 733 */
52         char size                       [ISODCL (11, 18)]; /* 733 */
53         char date                       [ISODCL (19, 25)]; /* 7 by 711 */
54         char flags                      [ISODCL (26, 26)];
55         char file_unit_size             [ISODCL (27, 27)]; /* 711 */
56         char interleave                 [ISODCL (28, 28)]; /* 711 */
57         char volume_sequence_number     [ISODCL (29, 32)]; /* 723 */
58         unsigned char name_len          [ISODCL (33, 33)]; /* 711 */
59         char name                       [1];
60 };
61
62 static void mmssdd( char *b, char *p )
63 {
64         int m, s, d;
65         unsigned char *ub = (void *)b;
66         int block = (ub[3] << 24) | (ub[2] << 16) | (ub[1] << 8) | ub[0];
67
68         block += 150;
69         m = block / 4500;                       // minutes
70         block = block - m * 4500;       // minutes rest
71         s = block / 75;                         // seconds
72         d = block - s * 75;                     // seconds rest
73
74         m = ((m / 10) << 4) | m % 10;
75         s = ((s / 10) << 4) | s % 10;
76         d = ((d / 10) << 4) | d % 10;
77
78         p[0] = m;
79         p[1] = s;
80         p[2] = d;
81 }
82
83 #define incTime() \
84         time[0] = btoi(time[0]); time[1] = btoi(time[1]); time[2] = btoi(time[2]); \
85         time[2]++; \
86         if(time[2] == 75) { \
87                 time[2] = 0; \
88                 time[1]++; \
89                 if (time[1] == 60) { \
90                         time[1] = 0; \
91                         time[0]++; \
92                 } \
93         } \
94         time[0] = itob(time[0]); time[1] = itob(time[1]); time[2] = itob(time[2]);
95
96 #define READTRACK() \
97         if (!CDR_readTrack(time)) return -1; \
98         buf = (void *)CDR_getBuffer(); \
99         if (buf == NULL) return -1; \
100         else CheckPPFCache((u8 *)buf, time[0], time[1], time[2]);
101
102 #define READDIR(_dir) \
103         READTRACK(); \
104         memcpy(_dir, buf + 12, 2048); \
105  \
106         incTime(); \
107         READTRACK(); \
108         memcpy(_dir + 2048, buf + 12, 2048);
109
110 int GetCdromFile(u8 *mdir, u8 *time, char *filename) {
111         struct iso_directory_record *dir;
112         int retval = -1;
113         u8 ddir[4096];
114         u8 *buf;
115         int i;
116
117         // only try to scan if a filename is given
118         if (filename == INVALID_PTR || !strlen(filename)) return -1;
119
120         i = 0;
121         while (i < 4096) {
122                 dir = (struct iso_directory_record*) &mdir[i];
123                 if (dir->length[0] == 0) {
124                         return -1;
125                 }
126                 i += (u8)dir->length[0];
127
128                 if (dir->flags[0] & 0x2) { // it's a dir
129                         if (!strnicmp((char *)&dir->name[0], filename, dir->name_len[0])) {
130                                 if (filename[dir->name_len[0]] != '\\') continue;
131
132                                 filename += dir->name_len[0] + 1;
133
134                                 mmssdd(dir->extent, (char *)time);
135                                 READDIR(ddir);
136                                 i = 0;
137                                 mdir = ddir;
138                         }
139                 } else {
140                         if (!strnicmp((char *)&dir->name[0], filename, strlen(filename))) {
141                                 mmssdd(dir->extent, (char *)time);
142                                 retval = 0;
143                                 break;
144                         }
145                 }
146         }
147         return retval;
148 }
149
150 static void SetBootRegs(u32 pc, u32 gp, u32 sp)
151 {
152         //printf("%s %08x %08x %08x\n", __func__, pc, gp, sp);
153         psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
154
155         psxRegs.pc = pc;
156         psxRegs.GPR.n.gp = gp;
157         psxRegs.GPR.n.sp = sp ? sp : 0x801fff00;
158         psxRegs.GPR.n.fp = psxRegs.GPR.n.sp;
159
160         psxRegs.GPR.n.t0 = psxRegs.GPR.n.sp; // mimic A(43)
161         psxRegs.GPR.n.t3 = pc;
162
163         psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
164 }
165
166 int BiosBootBypass() {
167         struct CdrStat stat = { 0, 0, };
168         assert(psxRegs.pc == 0x80030000);
169
170         // no bypass if the lid is open
171         CDR__getStatus(&stat);
172         if (stat.Status & 0x10)
173                 return 0;
174
175         // skip BIOS logos and region check
176         psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
177         psxRegs.pc = psxRegs.GPR.n.ra;
178         return 1;
179 }
180
181 static void getFromCnf(char *buf, const char *key, u32 *val)
182 {
183         buf = strstr(buf, key);
184         if (buf)
185                 buf = strchr(buf, '=');
186         if (buf) {
187                 unsigned long v;
188                 errno = 0;
189                 v = strtoul(buf + 1, NULL, 16);
190                 if (errno == 0)
191                         *val = v;
192         }
193 }
194
195 int LoadCdrom() {
196         union {
197                 EXE_HEADER h;
198                 u32 d[sizeof(EXE_HEADER) / sizeof(u32)];
199         } tmpHead;
200         struct iso_directory_record *dir;
201         u8 time[4], *buf;
202         u8 mdir[4096];
203         char exename[256];
204         u32 cnf_tcb = 4;
205         u32 cnf_event = 16;
206         u32 cnf_stack = 0;
207         u32 t_addr;
208         u32 t_size;
209         u32 sp = 0;
210         int i, ret;
211
212         if (!Config.HLE) {
213                 if (psxRegs.pc != 0x80030000) // BiosBootBypass'ed or custom BIOS?
214                         return 0;
215                 if (Config.SlowBoot)
216                         return 0;
217         }
218
219         time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
220
221         READTRACK();
222
223         // skip head and sub, and go to the root directory record
224         dir = (struct iso_directory_record*) &buf[12+156];
225
226         mmssdd(dir->extent, (char*)time);
227
228         READDIR(mdir);
229
230         // Load SYSTEM.CNF and scan for the main executable
231         if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
232                 // if SYSTEM.CNF is missing, start an existing PSX.EXE
233                 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
234                 strcpy(exename, "PSX.EXE;1");
235
236                 READTRACK();
237         }
238         else {
239                 // read the SYSTEM.CNF
240                 READTRACK();
241                 buf[1023] = 0;
242
243                 ret = sscanf((char *)buf + 12, "BOOT = cdrom:\\%255s", exename);
244                 if (ret < 1 || GetCdromFile(mdir, time, exename) == -1) {
245                         ret = sscanf((char *)buf + 12, "BOOT = cdrom:%255s", exename);
246                         if (ret < 1 || GetCdromFile(mdir, time, exename) == -1) {
247                                 char *ptr = strstr((char *)buf + 12, "cdrom:");
248                                 if (ptr != NULL) {
249                                         ptr += 6;
250                                         while (*ptr == '\\' || *ptr == '/') ptr++;
251                                         strncpy(exename, ptr, 255);
252                                         exename[255] = '\0';
253                                         ptr = exename;
254                                         while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
255                                         *ptr = '\0';
256                                         if (GetCdromFile(mdir, time, exename) == -1)
257                                                 return -1;
258                                 } else
259                                         return -1;
260                         }
261                 }
262                 getFromCnf((char *)buf + 12, "TCB", &cnf_tcb);
263                 getFromCnf((char *)buf + 12, "EVENT", &cnf_event);
264                 getFromCnf((char *)buf + 12, "STACK", &cnf_stack);
265                 if (Config.HLE)
266                         psxBiosCnfLoaded(cnf_tcb, cnf_event, cnf_stack);
267
268                 // Read the EXE-Header
269                 READTRACK();
270         }
271
272         memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
273         for (i = 2; i < sizeof(tmpHead.d) / sizeof(tmpHead.d[0]); i++)
274                 tmpHead.d[i] = SWAP32(tmpHead.d[i]);
275
276         SysPrintf("manual booting '%s' pc=%x\n", exename, tmpHead.h.pc0);
277         sp = tmpHead.h.s_addr;
278         if (cnf_stack)
279                 sp = cnf_stack;
280         SetBootRegs(tmpHead.h.pc0, tmpHead.h.gp0, sp);
281
282         // Read the rest of the main executable
283         for (t_addr = tmpHead.h.t_addr, t_size = tmpHead.h.t_size; t_size & ~2047; ) {
284                 void *ptr = (void *)PSXM(t_addr);
285
286                 incTime();
287                 READTRACK();
288
289                 if (ptr != INVALID_PTR) memcpy(ptr, buf+12, 2048);
290
291                 t_addr += 2048;
292                 t_size -= 2048;
293         }
294
295         psxCpu->Clear(tmpHead.h.t_addr, tmpHead.h.t_size / 4);
296         //psxCpu->Reset();
297
298         if (Config.HLE)
299                 psxBiosCheckExe(tmpHead.h.t_addr, tmpHead.h.t_size, 0);
300
301         return 0;
302 }
303
304 int LoadCdromFile(const char *filename, EXE_HEADER *head, u8 *time_bcd_out) {
305         struct iso_directory_record *dir;
306         u8 time[4],*buf;
307         u8 mdir[4096];
308         char exename[256];
309         const char *p1, *p2;
310         u32 size, addr;
311         void *mem;
312
313         if (filename == INVALID_PTR)
314                 return -1;
315
316         p1 = filename;
317         if ((p2 = strchr(p1, ':')))
318                 p1 = p2 + 1;
319         while (*p1 == '\\')
320                 p1++;
321         snprintf(exename, sizeof(exename), "%s", p1);
322
323         time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
324
325         READTRACK();
326
327         // skip head and sub, and go to the root directory record
328         dir = (struct iso_directory_record *)&buf[12 + 156];
329
330         mmssdd(dir->extent, (char*)time);
331
332         READDIR(mdir);
333
334         if (GetCdromFile(mdir, time, exename) == -1) return -1;
335
336         READTRACK();
337         incTime();
338
339         memcpy(head, buf + 12, sizeof(EXE_HEADER));
340         size = SWAP32(head->t_size);
341         addr = SWAP32(head->t_addr);
342
343         psxCpu->Clear(addr, size / 4);
344         //psxCpu->Reset();
345
346         while (size & ~2047) {
347                 READTRACK();
348                 incTime();
349
350                 mem = PSXM(addr);
351                 if (mem != INVALID_PTR)
352                         memcpy(mem, buf + 12, 2048);
353
354                 size -= 2048;
355                 addr += 2048;
356         }
357         if (time_bcd_out)
358                 memcpy(time_bcd_out, time, 3);
359
360         return 0;
361 }
362
363 int CheckCdrom() {
364         struct iso_directory_record *dir;
365         unsigned char time[4];
366         char *buf;
367         unsigned char mdir[4096];
368         char exename[256];
369         int i, len, c;
370
371         FreePPFCache();
372
373         time[0] = itob(0);
374         time[1] = itob(2);
375         time[2] = itob(0x10);
376
377         READTRACK();
378
379         memset(CdromLabel, 0, sizeof(CdromLabel));
380         memset(CdromId, 0, sizeof(CdromId));
381         memset(exename, 0, sizeof(exename));
382
383         strncpy(CdromLabel, buf + 52, 32);
384
385         // skip head and sub, and go to the root directory record
386         dir = (struct iso_directory_record *)&buf[12 + 156];
387
388         mmssdd(dir->extent, (char *)time);
389
390         READDIR(mdir);
391
392         if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
393                 READTRACK();
394
395                 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
396                 if (GetCdromFile(mdir, time, exename) == -1) {
397                         sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
398                         if (GetCdromFile(mdir, time, exename) == -1) {
399                                 char *ptr = strstr(buf + 12, "cdrom:");                 // possibly the executable is in some subdir
400                                 if (ptr != NULL) {
401                                         ptr += 6;
402                                         while (*ptr == '\\' || *ptr == '/') ptr++;
403                                         strncpy(exename, ptr, 255);
404                                         exename[255] = '\0';
405                                         ptr = exename;
406                                         while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
407                                         *ptr = '\0';
408                                         if (GetCdromFile(mdir, time, exename) == -1)
409                                                 return -1;              // main executable not found
410                                 } else
411                                         return -1;
412                         }
413                 }
414                 /* Workaround for Wild Arms EU/US which has non-standard string causing incorrect region detection */
415                 if (exename[0] == 'E' && exename[1] == 'X' && exename[2] == 'E' && exename[3] == '\\') {
416                         size_t offset = 4;
417                         size_t i, len = strlen(exename) - offset;
418                         for (i = 0; i < len; i++)
419                                 exename[i] = exename[i + offset];
420                         exename[i] = '\0';
421                 }
422         } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
423                 strcpy(exename, "PSX.EXE;1");
424                 strcpy(CdromId, "SLUS99999");
425         } else
426                 return -1;              // SYSTEM.CNF and PSX.EXE not found
427
428         if (CdromId[0] == '\0') {
429                 len = strlen(exename);
430                 c = 0;
431                 for (i = 0; i < len; ++i) {
432                         if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
433                                 break;
434                         if (isalnum(exename[i]))
435                                 CdromId[c++] = exename[i];
436                 }
437         }
438
439         if (CdromId[0] == '\0')
440                 strcpy(CdromId, "SLUS99999");
441
442         if (Config.PsxAuto) { // autodetect system (pal or ntsc)
443                 if (
444                         /* Make sure Wild Arms SCUS-94608 is not detected as a PAL game. */
445                         ((CdromId[0] == 's' || CdromId[0] == 'S') && (CdromId[2] == 'e' || CdromId[2] == 'E')) ||
446                         !strncmp(CdromId, "DTLS3035", 8) ||
447                         !strncmp(CdromId, "PBPX95001", 9) || // according to redump.org, these PAL
448                         !strncmp(CdromId, "PBPX95007", 9) || // discs have a non-standard ID;
449                         !strncmp(CdromId, "PBPX95008", 9))   // add more serials if they are discovered.
450                         Config.PsxType = PSX_TYPE_PAL; // pal
451                 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
452         }
453
454         if (CdromLabel[0] == ' ') {
455                 strncpy(CdromLabel, CdromId, 9);
456         }
457         SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
458         SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
459         SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
460         
461         Apply_Hacks_Cdrom();
462
463         BuildPPFCache();
464
465         return 0;
466 }
467
468 static int PSXGetFileType(FILE *f) {
469         unsigned long current;
470         u8 mybuf[2048];
471         EXE_HEADER *exe_hdr;
472         FILHDR *coff_hdr;
473
474         current = ftell(f);
475         fseek(f, 0L, SEEK_SET);
476         if (fread(&mybuf, 1, sizeof(mybuf), f) != sizeof(mybuf))
477                 goto io_fail;
478         
479         fseek(f, current, SEEK_SET);
480
481         exe_hdr = (EXE_HEADER *)mybuf;
482         if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
483                 return PSX_EXE;
484
485         if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
486                 return CPE_EXE;
487
488         coff_hdr = (FILHDR *)mybuf;
489         if (SWAPu16(coff_hdr->f_magic) == 0x0162)
490                 return COFF_EXE;
491
492         return INVALID_EXE;
493
494 io_fail:
495 #ifndef NDEBUG
496         SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
497 #endif
498         return INVALID_EXE;
499 }
500
501 // temporary pandora workaround..
502 // FIXME: remove
503 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
504 {
505         void *tmp;
506         size_t ret = 0;
507
508         tmp = malloc(size * nmemb);
509         if (tmp) {
510                 ret = fread(tmp, size, nmemb, stream);
511                 memcpy(ptr, tmp, size * nmemb);
512                 free(tmp);
513         }
514         return ret;
515 }
516
517 int Load(const char *ExePath) {
518         FILE *tmpFile;
519         EXE_HEADER tmpHead;
520         int type;
521         int retval = 0;
522         u8 opcode;
523         u32 section_address, section_size;
524         void *mem;
525
526         strcpy(CdromId, "SLUS99999");
527         strcpy(CdromLabel, "SLUS_999.99");
528
529         tmpFile = fopen(ExePath, "rb");
530         if (tmpFile == NULL) {
531                 SysPrintf(_("Error opening file: %s.\n"), ExePath);
532                 retval = -1;
533         } else {
534                 type = PSXGetFileType(tmpFile);
535                 switch (type) {
536                         case PSX_EXE:
537                                 if (fread(&tmpHead, 1, sizeof(EXE_HEADER), tmpFile) != sizeof(EXE_HEADER))
538                                         goto fail_io;
539                                 section_address = SWAP32(tmpHead.t_addr);
540                                 section_size = SWAP32(tmpHead.t_size);
541                                 mem = PSXM(section_address);
542                                 if (mem != INVALID_PTR) {
543                                         fseek(tmpFile, 0x800, SEEK_SET);
544                                         fread_to_ram(mem, section_size, 1, tmpFile);
545                                         psxCpu->Clear(section_address, section_size / 4);
546                                 }
547                                 SetBootRegs(SWAP32(tmpHead.pc0), SWAP32(tmpHead.gp0),
548                                         SWAP32(tmpHead.s_addr));
549                                 retval = 0;
550                                 break;
551                         case CPE_EXE:
552                                 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
553                                 do {
554                                         if (fread(&opcode, 1, sizeof(opcode), tmpFile) != sizeof(opcode))
555                                                 goto fail_io;
556                                         switch (opcode) {
557                                                 case 1: /* Section loading */
558                                                         if (fread(&section_address, 1, sizeof(section_address), tmpFile) != sizeof(section_address))
559                                                                 goto fail_io;
560                                                         if (fread(&section_size, 1, sizeof(section_size), tmpFile) != sizeof(section_size))
561                                                                 goto fail_io;
562                                                         section_address = SWAPu32(section_address);
563                                                         section_size = SWAPu32(section_size);
564 #ifdef EMU_LOG
565                                                         EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
566 #endif
567                                                         mem = PSXM(section_address);
568                                                         if (mem != INVALID_PTR) {
569                                                                 fread_to_ram(mem, section_size, 1, tmpFile);
570                                                                 psxCpu->Clear(section_address, section_size / 4);
571                                                         }
572                                                         break;
573                                                 case 3: /* register loading (PC only?) */
574                                                         fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
575                                                         if (fread(&psxRegs.pc, 1, sizeof(psxRegs.pc), tmpFile) != sizeof(psxRegs.pc))
576                                                                 goto fail_io;
577                                                         psxRegs.pc = SWAPu32(psxRegs.pc);
578                                                         break;
579                                                 case 0: /* End of file */
580                                                         break;
581                                                 default:
582                                                         SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
583                                                         retval = -1;
584                                                         break;
585                                         }
586                                 } while (opcode != 0 && retval == 0);
587                                 break;
588                         case COFF_EXE:
589                                 SysPrintf(_("COFF files not supported.\n"));
590                                 retval = -1;
591                                 break;
592                         case INVALID_EXE:
593                                 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
594                                 SysPrintf(_("(did you forget -cdfile ?)\n"));
595                                 retval = -1;
596                                 break;
597                 }
598         }
599
600         if (retval != 0) {
601                 CdromId[0] = '\0';
602                 CdromLabel[0] = '\0';
603         }
604
605         if (tmpFile)
606                 fclose(tmpFile);
607         return retval;
608
609 fail_io:
610 #ifndef NDEBUG
611         SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
612 #endif
613         fclose(tmpFile);
614         return -1;
615 }
616
617 // STATES
618
619 static void *zlib_open(const char *name, const char *mode)
620 {
621         return gzopen(name, mode);
622 }
623
624 static int zlib_read(void *file, void *buf, u32 len)
625 {
626         return gzread(file, buf, len);
627 }
628
629 static int zlib_write(void *file, const void *buf, u32 len)
630 {
631         return gzwrite(file, buf, len);
632 }
633
634 static long zlib_seek(void *file, long offs, int whence)
635 {
636         return gzseek(file, offs, whence);
637 }
638
639 static void zlib_close(void *file)
640 {
641         gzclose(file);
642 }
643
644 struct PcsxSaveFuncs SaveFuncs = {
645         zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
646 };
647
648 static const char PcsxHeader[32] = "STv4 PCSX v" PCSX_VERSION;
649
650 // Savestate Versioning!
651 // If you make changes to the savestate version, please increment the value below.
652 static const u32 SaveVersion = 0x8b410006;
653
654 #define MISC_MAGIC 0x4353494d
655 struct misc_save_data {
656         u32 magic;
657         u32 gteBusyCycle;
658         u32 muldivBusyCycle;
659         u32 biuReg;
660         u32 biosBranchCheck;
661         u32 gpuIdleAfter;
662         u32 gpuSr;
663         u32 frame_counter;
664         int CdromFrontendId;
665 };
666
667 int SaveState(const char *file) {
668         struct misc_save_data *misc = (void *)(psxH + 0xf000);
669         void *f;
670         GPUFreeze_t *gpufP = NULL;
671         SPUFreezeHdr_t spufH;
672         SPUFreeze_t *spufP = NULL;
673         unsigned char *pMem = NULL;
674         int result = -1;
675         int Size;
676
677         assert(!psxRegs.branching);
678         assert(!psxRegs.cpuInRecursion);
679         assert(!misc->magic);
680
681         f = SaveFuncs.open(file, "wb");
682         if (f == NULL) return -1;
683
684         misc->magic = MISC_MAGIC;
685         misc->gteBusyCycle = psxRegs.gteBusyCycle;
686         misc->muldivBusyCycle = psxRegs.muldivBusyCycle;
687         misc->biuReg = psxRegs.biuReg;
688         misc->biosBranchCheck = psxRegs.biosBranchCheck;
689         misc->gpuIdleAfter = psxRegs.gpuIdleAfter;
690         misc->gpuSr = HW_GPU_STATUS;
691         misc->frame_counter = frame_counter;
692         misc->CdromFrontendId = CdromFrontendId;
693
694         psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
695
696         SaveFuncs.write(f, (void *)PcsxHeader, 32);
697         SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
698         SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
699
700         pMem = (unsigned char *)malloc(128 * 96 * 3);
701         if (pMem == NULL) goto cleanup;
702         GPU_getScreenPic(pMem);
703         SaveFuncs.write(f, pMem, 128 * 96 * 3);
704         free(pMem);
705
706         if (Config.HLE)
707                 psxBiosFreeze(1);
708
709         SaveFuncs.write(f, psxM, 0x00200000);
710         SaveFuncs.write(f, psxR, 0x00080000);
711         SaveFuncs.write(f, psxH, 0x00010000);
712         // only partial save of psxRegisters to maintain savestate compat
713         SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
714
715         // gpu
716         gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
717         if (gpufP == NULL) goto cleanup;
718         gpufP->ulFreezeVersion = 1;
719         GPU_freeze(1, gpufP);
720         SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
721         free(gpufP); gpufP = NULL;
722
723         // spu
724         SPU_freeze(2, (SPUFreeze_t *)&spufH, psxRegs.cycle);
725         Size = spufH.Size; SaveFuncs.write(f, &Size, 4);
726         spufP = (SPUFreeze_t *) malloc(Size);
727         if (spufP == NULL) goto cleanup;
728         SPU_freeze(1, spufP, psxRegs.cycle);
729         SaveFuncs.write(f, spufP, Size);
730         free(spufP); spufP = NULL;
731
732         sioFreeze(f, 1);
733         cdrFreeze(f, 1);
734         psxHwFreeze(f, 1);
735         psxRcntFreeze(f, 1);
736         mdecFreeze(f, 1);
737         new_dyna_freeze(f, 1);
738         padFreeze(f, 1);
739
740         result = 0;
741 cleanup:
742         memset(misc, 0, sizeof(*misc));
743         SaveFuncs.close(f);
744         return result;
745 }
746
747 int LoadState(const char *file) {
748         struct misc_save_data *misc = (void *)(psxH + 0xf000);
749         u32 biosBranchCheckOld = psxRegs.biosBranchCheck;
750         void *f;
751         GPUFreeze_t *gpufP = NULL;
752         SPUFreeze_t *spufP = NULL;
753         int Size;
754         char header[32];
755         u32 version;
756         boolean hle;
757         int result = -1;
758
759         f = SaveFuncs.open(file, "rb");
760         if (f == NULL) return -1;
761
762         SaveFuncs.read(f, header, sizeof(header));
763         SaveFuncs.read(f, &version, sizeof(u32));
764         SaveFuncs.read(f, &hle, sizeof(boolean));
765
766         if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
767                 SysPrintf("incompatible savestate version %x\n", version);
768                 goto cleanup;
769         }
770         Config.HLE = hle;
771
772         if (Config.HLE)
773                 psxBiosInit();
774
775         SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
776         SaveFuncs.read(f, psxM, 0x00200000);
777         SaveFuncs.read(f, psxR, 0x00080000);
778         SaveFuncs.read(f, psxH, 0x00010000);
779         SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
780         psxRegs.gteBusyCycle = psxRegs.cycle;
781         psxRegs.biosBranchCheck = ~0;
782         psxRegs.gpuIdleAfter = psxRegs.cycle - 1;
783         HW_GPU_STATUS &= SWAP32(~PSXGPU_nBUSY);
784         if (misc->magic == MISC_MAGIC) {
785                 psxRegs.gteBusyCycle = misc->gteBusyCycle;
786                 psxRegs.muldivBusyCycle = misc->muldivBusyCycle;
787                 psxRegs.biuReg = misc->biuReg;
788                 psxRegs.biosBranchCheck = misc->biosBranchCheck;
789                 psxRegs.gpuIdleAfter = misc->gpuIdleAfter;
790                 HW_GPU_STATUS = misc->gpuSr;
791                 frame_counter = misc->frame_counter;
792                 CdromFrontendId = misc->CdromFrontendId;
793         }
794
795         psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
796
797         if (Config.HLE)
798                 psxBiosFreeze(0);
799
800         // gpu
801         gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
802         if (gpufP == NULL) goto cleanup;
803         SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
804         GPU_freeze(0, gpufP);
805         free(gpufP);
806         gpuSyncPluginSR();
807
808         // spu
809         SaveFuncs.read(f, &Size, 4);
810         spufP = (SPUFreeze_t *)malloc(Size);
811         if (spufP == NULL) goto cleanup;
812         SaveFuncs.read(f, spufP, Size);
813         SPU_freeze(0, spufP, psxRegs.cycle);
814         free(spufP);
815
816         sioFreeze(f, 0);
817         cdrFreeze(f, 0);
818         psxHwFreeze(f, 0);
819         psxRcntFreeze(f, 0);
820         mdecFreeze(f, 0);
821         new_dyna_freeze(f, 0);
822         padFreeze(f, 0);
823
824         events_restore();
825         if (Config.HLE)
826                 psxBiosCheckExe(biosBranchCheckOld, 0x60, 1);
827
828         result = 0;
829 cleanup:
830         memset(misc, 0, sizeof(*misc));
831         SaveFuncs.close(f);
832         return result;
833 }
834
835 int CheckState(const char *file) {
836         void *f;
837         char header[32];
838         u32 version;
839         boolean hle;
840
841         f = SaveFuncs.open(file, "rb");
842         if (f == NULL) return -1;
843
844         SaveFuncs.read(f, header, sizeof(header));
845         SaveFuncs.read(f, &version, sizeof(u32));
846         SaveFuncs.read(f, &hle, sizeof(boolean));
847
848         SaveFuncs.close(f);
849
850         if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
851                 return -1;
852
853         return 0;
854 }
855
856 // NET Function Helpers
857
858 int SendPcsxInfo() {
859         if (NET_recvData == NULL || NET_sendData == NULL)
860                 return 0;
861
862         boolean Sio_old = 0;
863         boolean SpuIrq_old = 0;
864         boolean RCntFix_old = 0;
865         NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
866         NET_sendData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
867         NET_sendData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
868         NET_sendData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
869         NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
870         NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
871
872         return 0;
873 }
874
875 int RecvPcsxInfo() {
876         int tmp;
877
878         if (NET_recvData == NULL || NET_sendData == NULL)
879                 return 0;
880
881         boolean Sio_old = 0;
882         boolean SpuIrq_old = 0;
883         boolean RCntFix_old = 0;
884         NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
885         NET_recvData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
886         NET_recvData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
887         NET_recvData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
888         NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
889
890         tmp = Config.Cpu;
891         NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
892         if (tmp != Config.Cpu) {
893                 psxCpu->Shutdown();
894 #ifndef DRC_DISABLE
895                 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
896                 else psxCpu = &psxRec;
897 #else
898                 psxCpu = &psxInt;
899 #endif
900                 if (psxCpu->Init() == -1) {
901                         SysClose(); return -1;
902                 }
903                 psxCpu->Reset();
904                 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
905         }
906
907         return 0;
908 }
909
910 // remove the leading and trailing spaces in a string
911 void trim(char *str) {
912         int pos = 0;
913         char *dest = str;
914
915         // skip leading blanks
916         while (str[pos] <= ' ' && str[pos] > 0)
917                 pos++;
918
919         while (str[pos]) {
920                 *(dest++) = str[pos];
921                 pos++;
922         }
923
924         *(dest--) = '\0'; // store the null
925
926         // remove trailing blanks
927         while (dest >= str && *dest <= ' ' && *dest > 0)
928                 *(dest--) = '\0';
929 }
930
931 // lookup table for crc calculation
932 static unsigned short crctab[256] = {
933         0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
934         0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
935         0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
936         0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
937         0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
938         0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
939         0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
940         0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
941         0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
942         0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
943         0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
944         0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
945         0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
946         0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
947         0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
948         0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
949         0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
950         0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
951         0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
952         0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
953         0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
954         0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
955         0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
956         0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
957         0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
958         0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
959         0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
960         0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
961         0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
962 };
963
964 u16 calcCrc(u8 *d, int len) {
965         u16 crc = 0;
966         int i;
967
968         for (i = 0; i < len; i++) {
969                 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);
970         }
971
972         return ~crc;
973 }