cdrom: allow resetting with lid open
[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) {
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
338         memcpy(head, buf + 12, sizeof(EXE_HEADER));
339         size = SWAP32(head->t_size);
340         addr = SWAP32(head->t_addr);
341
342         psxCpu->Clear(addr, size / 4);
343         //psxCpu->Reset();
344
345         while (size & ~2047) {
346                 incTime();
347                 READTRACK();
348
349                 mem = PSXM(addr);
350                 if (mem != INVALID_PTR)
351                         memcpy(mem, buf + 12, 2048);
352
353                 size -= 2048;
354                 addr += 2048;
355         }
356
357         return 0;
358 }
359
360 int CheckCdrom() {
361         struct iso_directory_record *dir;
362         unsigned char time[4];
363         char *buf;
364         unsigned char mdir[4096];
365         char exename[256];
366         int i, len, c;
367
368         FreePPFCache();
369
370         time[0] = itob(0);
371         time[1] = itob(2);
372         time[2] = itob(0x10);
373
374         READTRACK();
375
376         memset(CdromLabel, 0, sizeof(CdromLabel));
377         memset(CdromId, 0, sizeof(CdromId));
378         memset(exename, 0, sizeof(exename));
379
380         strncpy(CdromLabel, buf + 52, 32);
381
382         // skip head and sub, and go to the root directory record
383         dir = (struct iso_directory_record *)&buf[12 + 156];
384
385         mmssdd(dir->extent, (char *)time);
386
387         READDIR(mdir);
388
389         if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
390                 READTRACK();
391
392                 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
393                 if (GetCdromFile(mdir, time, exename) == -1) {
394                         sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
395                         if (GetCdromFile(mdir, time, exename) == -1) {
396                                 char *ptr = strstr(buf + 12, "cdrom:");                 // possibly the executable is in some subdir
397                                 if (ptr != NULL) {
398                                         ptr += 6;
399                                         while (*ptr == '\\' || *ptr == '/') ptr++;
400                                         strncpy(exename, ptr, 255);
401                                         exename[255] = '\0';
402                                         ptr = exename;
403                                         while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
404                                         *ptr = '\0';
405                                         if (GetCdromFile(mdir, time, exename) == -1)
406                                                 return -1;              // main executable not found
407                                 } else
408                                         return -1;
409                         }
410                 }
411                 /* Workaround for Wild Arms EU/US which has non-standard string causing incorrect region detection */
412                 if (exename[0] == 'E' && exename[1] == 'X' && exename[2] == 'E' && exename[3] == '\\') {
413                         size_t offset = 4;
414                         size_t i, len = strlen(exename) - offset;
415                         for (i = 0; i < len; i++)
416                                 exename[i] = exename[i + offset];
417                         exename[i] = '\0';
418                 }
419         } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
420                 strcpy(exename, "PSX.EXE;1");
421                 strcpy(CdromId, "SLUS99999");
422         } else
423                 return -1;              // SYSTEM.CNF and PSX.EXE not found
424
425         if (CdromId[0] == '\0') {
426                 len = strlen(exename);
427                 c = 0;
428                 for (i = 0; i < len; ++i) {
429                         if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
430                                 break;
431                         if (isalnum(exename[i]))
432                                 CdromId[c++] = exename[i];
433                 }
434         }
435
436         if (CdromId[0] == '\0')
437                 strcpy(CdromId, "SLUS99999");
438
439         if (Config.PsxAuto) { // autodetect system (pal or ntsc)
440                 if (
441                         /* Make sure Wild Arms SCUS-94608 is not detected as a PAL game. */
442                         ((CdromId[0] == 's' || CdromId[0] == 'S') && (CdromId[2] == 'e' || CdromId[2] == 'E')) ||
443                         !strncmp(CdromId, "DTLS3035", 8) ||
444                         !strncmp(CdromId, "PBPX95001", 9) || // according to redump.org, these PAL
445                         !strncmp(CdromId, "PBPX95007", 9) || // discs have a non-standard ID;
446                         !strncmp(CdromId, "PBPX95008", 9))   // add more serials if they are discovered.
447                         Config.PsxType = PSX_TYPE_PAL; // pal
448                 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
449         }
450
451         if (CdromLabel[0] == ' ') {
452                 strncpy(CdromLabel, CdromId, 9);
453         }
454         SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
455         SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
456         SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
457         
458         Apply_Hacks_Cdrom();
459
460         BuildPPFCache();
461
462         return 0;
463 }
464
465 static int PSXGetFileType(FILE *f) {
466         unsigned long current;
467         u8 mybuf[2048];
468         EXE_HEADER *exe_hdr;
469         FILHDR *coff_hdr;
470
471         current = ftell(f);
472         fseek(f, 0L, SEEK_SET);
473         if (fread(&mybuf, 1, sizeof(mybuf), f) != sizeof(mybuf))
474                 goto io_fail;
475         
476         fseek(f, current, SEEK_SET);
477
478         exe_hdr = (EXE_HEADER *)mybuf;
479         if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
480                 return PSX_EXE;
481
482         if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
483                 return CPE_EXE;
484
485         coff_hdr = (FILHDR *)mybuf;
486         if (SWAPu16(coff_hdr->f_magic) == 0x0162)
487                 return COFF_EXE;
488
489         return INVALID_EXE;
490
491 io_fail:
492 #ifndef NDEBUG
493         SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
494 #endif
495         return INVALID_EXE;
496 }
497
498 // temporary pandora workaround..
499 // FIXME: remove
500 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
501 {
502         void *tmp;
503         size_t ret = 0;
504
505         tmp = malloc(size * nmemb);
506         if (tmp) {
507                 ret = fread(tmp, size, nmemb, stream);
508                 memcpy(ptr, tmp, size * nmemb);
509                 free(tmp);
510         }
511         return ret;
512 }
513
514 int Load(const char *ExePath) {
515         FILE *tmpFile;
516         EXE_HEADER tmpHead;
517         int type;
518         int retval = 0;
519         u8 opcode;
520         u32 section_address, section_size;
521         void *mem;
522
523         strcpy(CdromId, "SLUS99999");
524         strcpy(CdromLabel, "SLUS_999.99");
525
526         tmpFile = fopen(ExePath, "rb");
527         if (tmpFile == NULL) {
528                 SysPrintf(_("Error opening file: %s.\n"), ExePath);
529                 retval = -1;
530         } else {
531                 type = PSXGetFileType(tmpFile);
532                 switch (type) {
533                         case PSX_EXE:
534                                 if (fread(&tmpHead, 1, sizeof(EXE_HEADER), tmpFile) != sizeof(EXE_HEADER))
535                                         goto fail_io;
536                                 section_address = SWAP32(tmpHead.t_addr);
537                                 section_size = SWAP32(tmpHead.t_size);
538                                 mem = PSXM(section_address);
539                                 if (mem != INVALID_PTR) {
540                                         fseek(tmpFile, 0x800, SEEK_SET);
541                                         fread_to_ram(mem, section_size, 1, tmpFile);
542                                         psxCpu->Clear(section_address, section_size / 4);
543                                 }
544                                 SetBootRegs(SWAP32(tmpHead.pc0), SWAP32(tmpHead.gp0),
545                                         SWAP32(tmpHead.s_addr));
546                                 retval = 0;
547                                 break;
548                         case CPE_EXE:
549                                 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
550                                 do {
551                                         if (fread(&opcode, 1, sizeof(opcode), tmpFile) != sizeof(opcode))
552                                                 goto fail_io;
553                                         switch (opcode) {
554                                                 case 1: /* Section loading */
555                                                         if (fread(&section_address, 1, sizeof(section_address), tmpFile) != sizeof(section_address))
556                                                                 goto fail_io;
557                                                         if (fread(&section_size, 1, sizeof(section_size), tmpFile) != sizeof(section_size))
558                                                                 goto fail_io;
559                                                         section_address = SWAPu32(section_address);
560                                                         section_size = SWAPu32(section_size);
561 #ifdef EMU_LOG
562                                                         EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
563 #endif
564                                                         mem = PSXM(section_address);
565                                                         if (mem != INVALID_PTR) {
566                                                                 fread_to_ram(mem, section_size, 1, tmpFile);
567                                                                 psxCpu->Clear(section_address, section_size / 4);
568                                                         }
569                                                         break;
570                                                 case 3: /* register loading (PC only?) */
571                                                         fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
572                                                         if (fread(&psxRegs.pc, 1, sizeof(psxRegs.pc), tmpFile) != sizeof(psxRegs.pc))
573                                                                 goto fail_io;
574                                                         psxRegs.pc = SWAPu32(psxRegs.pc);
575                                                         break;
576                                                 case 0: /* End of file */
577                                                         break;
578                                                 default:
579                                                         SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
580                                                         retval = -1;
581                                                         break;
582                                         }
583                                 } while (opcode != 0 && retval == 0);
584                                 break;
585                         case COFF_EXE:
586                                 SysPrintf(_("COFF files not supported.\n"));
587                                 retval = -1;
588                                 break;
589                         case INVALID_EXE:
590                                 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
591                                 SysPrintf(_("(did you forget -cdfile ?)\n"));
592                                 retval = -1;
593                                 break;
594                 }
595         }
596
597         if (retval != 0) {
598                 CdromId[0] = '\0';
599                 CdromLabel[0] = '\0';
600         }
601
602         if (tmpFile)
603                 fclose(tmpFile);
604         return retval;
605
606 fail_io:
607 #ifndef NDEBUG
608         SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
609 #endif
610         fclose(tmpFile);
611         return -1;
612 }
613
614 // STATES
615
616 static void *zlib_open(const char *name, const char *mode)
617 {
618         return gzopen(name, mode);
619 }
620
621 static int zlib_read(void *file, void *buf, u32 len)
622 {
623         return gzread(file, buf, len);
624 }
625
626 static int zlib_write(void *file, const void *buf, u32 len)
627 {
628         return gzwrite(file, buf, len);
629 }
630
631 static long zlib_seek(void *file, long offs, int whence)
632 {
633         return gzseek(file, offs, whence);
634 }
635
636 static void zlib_close(void *file)
637 {
638         gzclose(file);
639 }
640
641 struct PcsxSaveFuncs SaveFuncs = {
642         zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
643 };
644
645 static const char PcsxHeader[32] = "STv4 PCSX v" PCSX_VERSION;
646
647 // Savestate Versioning!
648 // If you make changes to the savestate version, please increment the value below.
649 static const u32 SaveVersion = 0x8b410006;
650
651 #define MISC_MAGIC 0x4353494d
652 struct misc_save_data {
653         u32 magic;
654         u32 gteBusyCycle;
655         u32 muldivBusyCycle;
656         u32 biuReg;
657         u32 biosBranchCheck;
658         u32 gpuIdleAfter;
659         u32 gpuSr;
660         u32 frame_counter;
661         int CdromFrontendId;
662 };
663
664 int SaveState(const char *file) {
665         struct misc_save_data *misc = (void *)(psxH + 0xf000);
666         void *f;
667         GPUFreeze_t *gpufP = NULL;
668         SPUFreezeHdr_t spufH;
669         SPUFreeze_t *spufP = NULL;
670         unsigned char *pMem = NULL;
671         int result = -1;
672         int Size;
673
674         assert(!psxRegs.branching);
675         assert(!psxRegs.cpuInRecursion);
676         assert(!misc->magic);
677         misc->magic = MISC_MAGIC;
678         misc->gteBusyCycle = psxRegs.gteBusyCycle;
679         misc->muldivBusyCycle = psxRegs.muldivBusyCycle;
680         misc->biuReg = psxRegs.biuReg;
681         misc->biosBranchCheck = psxRegs.biosBranchCheck;
682         misc->gpuIdleAfter = psxRegs.gpuIdleAfter;
683         misc->gpuSr = HW_GPU_STATUS;
684         misc->frame_counter = frame_counter;
685         misc->CdromFrontendId = CdromFrontendId;
686
687         f = SaveFuncs.open(file, "wb");
688         if (f == NULL) return -1;
689
690         psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
691
692         SaveFuncs.write(f, (void *)PcsxHeader, 32);
693         SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
694         SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
695
696         pMem = (unsigned char *)malloc(128 * 96 * 3);
697         if (pMem == NULL) goto cleanup;
698         GPU_getScreenPic(pMem);
699         SaveFuncs.write(f, pMem, 128 * 96 * 3);
700         free(pMem);
701
702         if (Config.HLE)
703                 psxBiosFreeze(1);
704
705         SaveFuncs.write(f, psxM, 0x00200000);
706         SaveFuncs.write(f, psxR, 0x00080000);
707         SaveFuncs.write(f, psxH, 0x00010000);
708         // only partial save of psxRegisters to maintain savestate compat
709         SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
710
711         // gpu
712         gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
713         if (gpufP == NULL) goto cleanup;
714         gpufP->ulFreezeVersion = 1;
715         GPU_freeze(1, gpufP);
716         SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
717         free(gpufP); gpufP = NULL;
718
719         // spu
720         SPU_freeze(2, (SPUFreeze_t *)&spufH, psxRegs.cycle);
721         Size = spufH.Size; SaveFuncs.write(f, &Size, 4);
722         spufP = (SPUFreeze_t *) malloc(Size);
723         if (spufP == NULL) goto cleanup;
724         SPU_freeze(1, spufP, psxRegs.cycle);
725         SaveFuncs.write(f, spufP, Size);
726         free(spufP); spufP = NULL;
727
728         sioFreeze(f, 1);
729         cdrFreeze(f, 1);
730         psxHwFreeze(f, 1);
731         psxRcntFreeze(f, 1);
732         mdecFreeze(f, 1);
733         new_dyna_freeze(f, 1);
734         padFreeze(f, 1);
735
736         result = 0;
737 cleanup:
738         memset(misc, 0, sizeof(*misc));
739         SaveFuncs.close(f);
740         return result;
741 }
742
743 int LoadState(const char *file) {
744         struct misc_save_data *misc = (void *)(psxH + 0xf000);
745         u32 biosBranchCheckOld = psxRegs.biosBranchCheck;
746         void *f;
747         GPUFreeze_t *gpufP = NULL;
748         SPUFreeze_t *spufP = NULL;
749         int Size;
750         char header[32];
751         u32 version;
752         boolean hle;
753         int result = -1;
754
755         f = SaveFuncs.open(file, "rb");
756         if (f == NULL) return -1;
757
758         SaveFuncs.read(f, header, sizeof(header));
759         SaveFuncs.read(f, &version, sizeof(u32));
760         SaveFuncs.read(f, &hle, sizeof(boolean));
761
762         if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
763                 SysPrintf("incompatible savestate version %x\n", version);
764                 goto cleanup;
765         }
766         Config.HLE = hle;
767
768         if (Config.HLE)
769                 psxBiosInit();
770
771         SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
772         SaveFuncs.read(f, psxM, 0x00200000);
773         SaveFuncs.read(f, psxR, 0x00080000);
774         SaveFuncs.read(f, psxH, 0x00010000);
775         SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
776         psxRegs.gteBusyCycle = psxRegs.cycle;
777         psxRegs.biosBranchCheck = ~0;
778         psxRegs.gpuIdleAfter = psxRegs.cycle - 1;
779         HW_GPU_STATUS &= SWAP32(~PSXGPU_nBUSY);
780         if (misc->magic == MISC_MAGIC) {
781                 psxRegs.gteBusyCycle = misc->gteBusyCycle;
782                 psxRegs.muldivBusyCycle = misc->muldivBusyCycle;
783                 psxRegs.biuReg = misc->biuReg;
784                 psxRegs.biosBranchCheck = misc->biosBranchCheck;
785                 psxRegs.gpuIdleAfter = misc->gpuIdleAfter;
786                 HW_GPU_STATUS = misc->gpuSr;
787                 frame_counter = misc->frame_counter;
788                 CdromFrontendId = misc->CdromFrontendId;
789         }
790
791         psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
792
793         if (Config.HLE)
794                 psxBiosFreeze(0);
795
796         // gpu
797         gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
798         if (gpufP == NULL) goto cleanup;
799         SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
800         GPU_freeze(0, gpufP);
801         free(gpufP);
802         gpuSyncPluginSR();
803
804         // spu
805         SaveFuncs.read(f, &Size, 4);
806         spufP = (SPUFreeze_t *)malloc(Size);
807         if (spufP == NULL) goto cleanup;
808         SaveFuncs.read(f, spufP, Size);
809         SPU_freeze(0, spufP, psxRegs.cycle);
810         free(spufP);
811
812         sioFreeze(f, 0);
813         cdrFreeze(f, 0);
814         psxHwFreeze(f, 0);
815         psxRcntFreeze(f, 0);
816         mdecFreeze(f, 0);
817         new_dyna_freeze(f, 0);
818         padFreeze(f, 0);
819
820         events_restore();
821         if (Config.HLE)
822                 psxBiosCheckExe(biosBranchCheckOld, 0x60, 1);
823
824         result = 0;
825 cleanup:
826         memset(misc, 0, sizeof(*misc));
827         SaveFuncs.close(f);
828         return result;
829 }
830
831 int CheckState(const char *file) {
832         void *f;
833         char header[32];
834         u32 version;
835         boolean hle;
836
837         f = SaveFuncs.open(file, "rb");
838         if (f == NULL) return -1;
839
840         SaveFuncs.read(f, header, sizeof(header));
841         SaveFuncs.read(f, &version, sizeof(u32));
842         SaveFuncs.read(f, &hle, sizeof(boolean));
843
844         SaveFuncs.close(f);
845
846         if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
847                 return -1;
848
849         return 0;
850 }
851
852 // NET Function Helpers
853
854 int SendPcsxInfo() {
855         if (NET_recvData == NULL || NET_sendData == NULL)
856                 return 0;
857
858         boolean Sio_old = 0;
859         boolean SpuIrq_old = 0;
860         boolean RCntFix_old = 0;
861         NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
862         NET_sendData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
863         NET_sendData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
864         NET_sendData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
865         NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
866         NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
867
868         return 0;
869 }
870
871 int RecvPcsxInfo() {
872         int tmp;
873
874         if (NET_recvData == NULL || NET_sendData == NULL)
875                 return 0;
876
877         boolean Sio_old = 0;
878         boolean SpuIrq_old = 0;
879         boolean RCntFix_old = 0;
880         NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
881         NET_recvData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
882         NET_recvData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
883         NET_recvData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
884         NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
885
886         tmp = Config.Cpu;
887         NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
888         if (tmp != Config.Cpu) {
889                 psxCpu->Shutdown();
890 #ifndef DRC_DISABLE
891                 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
892                 else psxCpu = &psxRec;
893 #else
894                 psxCpu = &psxInt;
895 #endif
896                 if (psxCpu->Init() == -1) {
897                         SysClose(); return -1;
898                 }
899                 psxCpu->Reset();
900                 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
901         }
902
903         return 0;
904 }
905
906 // remove the leading and trailing spaces in a string
907 void trim(char *str) {
908         int pos = 0;
909         char *dest = str;
910
911         // skip leading blanks
912         while (str[pos] <= ' ' && str[pos] > 0)
913                 pos++;
914
915         while (str[pos]) {
916                 *(dest++) = str[pos];
917                 pos++;
918         }
919
920         *(dest--) = '\0'; // store the null
921
922         // remove trailing blanks
923         while (dest >= str && *dest <= ' ' && *dest > 0)
924                 *(dest--) = '\0';
925 }
926
927 // lookup table for crc calculation
928 static unsigned short crctab[256] = {
929         0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
930         0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
931         0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
932         0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
933         0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
934         0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
935         0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
936         0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
937         0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
938         0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
939         0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
940         0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
941         0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
942         0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
943         0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
944         0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
945         0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
946         0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
947         0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
948         0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
949         0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
950         0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
951         0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
952         0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
953         0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
954         0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
955         0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
956         0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
957         0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
958 };
959
960 u16 calcCrc(u8 *d, int len) {
961         u16 crc = 0;
962         int i;
963
964         for (i = 0; i < len; i++) {
965                 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);
966         }
967
968         return ~crc;
969 }