psxbios: implement some cdrom related stuff
[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         misc->magic = MISC_MAGIC;
681         misc->gteBusyCycle = psxRegs.gteBusyCycle;
682         misc->muldivBusyCycle = psxRegs.muldivBusyCycle;
683         misc->biuReg = psxRegs.biuReg;
684         misc->biosBranchCheck = psxRegs.biosBranchCheck;
685         misc->gpuIdleAfter = psxRegs.gpuIdleAfter;
686         misc->gpuSr = HW_GPU_STATUS;
687         misc->frame_counter = frame_counter;
688         misc->CdromFrontendId = CdromFrontendId;
689
690         f = SaveFuncs.open(file, "wb");
691         if (f == NULL) return -1;
692
693         psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
694
695         SaveFuncs.write(f, (void *)PcsxHeader, 32);
696         SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
697         SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
698
699         pMem = (unsigned char *)malloc(128 * 96 * 3);
700         if (pMem == NULL) goto cleanup;
701         GPU_getScreenPic(pMem);
702         SaveFuncs.write(f, pMem, 128 * 96 * 3);
703         free(pMem);
704
705         if (Config.HLE)
706                 psxBiosFreeze(1);
707
708         SaveFuncs.write(f, psxM, 0x00200000);
709         SaveFuncs.write(f, psxR, 0x00080000);
710         SaveFuncs.write(f, psxH, 0x00010000);
711         // only partial save of psxRegisters to maintain savestate compat
712         SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
713
714         // gpu
715         gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
716         if (gpufP == NULL) goto cleanup;
717         gpufP->ulFreezeVersion = 1;
718         GPU_freeze(1, gpufP);
719         SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
720         free(gpufP); gpufP = NULL;
721
722         // spu
723         SPU_freeze(2, (SPUFreeze_t *)&spufH, psxRegs.cycle);
724         Size = spufH.Size; SaveFuncs.write(f, &Size, 4);
725         spufP = (SPUFreeze_t *) malloc(Size);
726         if (spufP == NULL) goto cleanup;
727         SPU_freeze(1, spufP, psxRegs.cycle);
728         SaveFuncs.write(f, spufP, Size);
729         free(spufP); spufP = NULL;
730
731         sioFreeze(f, 1);
732         cdrFreeze(f, 1);
733         psxHwFreeze(f, 1);
734         psxRcntFreeze(f, 1);
735         mdecFreeze(f, 1);
736         new_dyna_freeze(f, 1);
737         padFreeze(f, 1);
738
739         result = 0;
740 cleanup:
741         memset(misc, 0, sizeof(*misc));
742         SaveFuncs.close(f);
743         return result;
744 }
745
746 int LoadState(const char *file) {
747         struct misc_save_data *misc = (void *)(psxH + 0xf000);
748         u32 biosBranchCheckOld = psxRegs.biosBranchCheck;
749         void *f;
750         GPUFreeze_t *gpufP = NULL;
751         SPUFreeze_t *spufP = NULL;
752         int Size;
753         char header[32];
754         u32 version;
755         boolean hle;
756         int result = -1;
757
758         f = SaveFuncs.open(file, "rb");
759         if (f == NULL) return -1;
760
761         SaveFuncs.read(f, header, sizeof(header));
762         SaveFuncs.read(f, &version, sizeof(u32));
763         SaveFuncs.read(f, &hle, sizeof(boolean));
764
765         if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
766                 SysPrintf("incompatible savestate version %x\n", version);
767                 goto cleanup;
768         }
769         Config.HLE = hle;
770
771         if (Config.HLE)
772                 psxBiosInit();
773
774         SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
775         SaveFuncs.read(f, psxM, 0x00200000);
776         SaveFuncs.read(f, psxR, 0x00080000);
777         SaveFuncs.read(f, psxH, 0x00010000);
778         SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
779         psxRegs.gteBusyCycle = psxRegs.cycle;
780         psxRegs.biosBranchCheck = ~0;
781         psxRegs.gpuIdleAfter = psxRegs.cycle - 1;
782         HW_GPU_STATUS &= SWAP32(~PSXGPU_nBUSY);
783         if (misc->magic == MISC_MAGIC) {
784                 psxRegs.gteBusyCycle = misc->gteBusyCycle;
785                 psxRegs.muldivBusyCycle = misc->muldivBusyCycle;
786                 psxRegs.biuReg = misc->biuReg;
787                 psxRegs.biosBranchCheck = misc->biosBranchCheck;
788                 psxRegs.gpuIdleAfter = misc->gpuIdleAfter;
789                 HW_GPU_STATUS = misc->gpuSr;
790                 frame_counter = misc->frame_counter;
791                 CdromFrontendId = misc->CdromFrontendId;
792         }
793
794         psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
795
796         if (Config.HLE)
797                 psxBiosFreeze(0);
798
799         // gpu
800         gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
801         if (gpufP == NULL) goto cleanup;
802         SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
803         GPU_freeze(0, gpufP);
804         free(gpufP);
805         gpuSyncPluginSR();
806
807         // spu
808         SaveFuncs.read(f, &Size, 4);
809         spufP = (SPUFreeze_t *)malloc(Size);
810         if (spufP == NULL) goto cleanup;
811         SaveFuncs.read(f, spufP, Size);
812         SPU_freeze(0, spufP, psxRegs.cycle);
813         free(spufP);
814
815         sioFreeze(f, 0);
816         cdrFreeze(f, 0);
817         psxHwFreeze(f, 0);
818         psxRcntFreeze(f, 0);
819         mdecFreeze(f, 0);
820         new_dyna_freeze(f, 0);
821         padFreeze(f, 0);
822
823         events_restore();
824         if (Config.HLE)
825                 psxBiosCheckExe(biosBranchCheckOld, 0x60, 1);
826
827         result = 0;
828 cleanup:
829         memset(misc, 0, sizeof(*misc));
830         SaveFuncs.close(f);
831         return result;
832 }
833
834 int CheckState(const char *file) {
835         void *f;
836         char header[32];
837         u32 version;
838         boolean hle;
839
840         f = SaveFuncs.open(file, "rb");
841         if (f == NULL) return -1;
842
843         SaveFuncs.read(f, header, sizeof(header));
844         SaveFuncs.read(f, &version, sizeof(u32));
845         SaveFuncs.read(f, &hle, sizeof(boolean));
846
847         SaveFuncs.close(f);
848
849         if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
850                 return -1;
851
852         return 0;
853 }
854
855 // NET Function Helpers
856
857 int SendPcsxInfo() {
858         if (NET_recvData == NULL || NET_sendData == NULL)
859                 return 0;
860
861         boolean Sio_old = 0;
862         boolean SpuIrq_old = 0;
863         boolean RCntFix_old = 0;
864         NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
865         NET_sendData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
866         NET_sendData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
867         NET_sendData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
868         NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
869         NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
870
871         return 0;
872 }
873
874 int RecvPcsxInfo() {
875         int tmp;
876
877         if (NET_recvData == NULL || NET_sendData == NULL)
878                 return 0;
879
880         boolean Sio_old = 0;
881         boolean SpuIrq_old = 0;
882         boolean RCntFix_old = 0;
883         NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
884         NET_recvData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
885         NET_recvData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
886         NET_recvData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
887         NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
888
889         tmp = Config.Cpu;
890         NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
891         if (tmp != Config.Cpu) {
892                 psxCpu->Shutdown();
893 #ifndef DRC_DISABLE
894                 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
895                 else psxCpu = &psxRec;
896 #else
897                 psxCpu = &psxInt;
898 #endif
899                 if (psxCpu->Init() == -1) {
900                         SysClose(); return -1;
901                 }
902                 psxCpu->Reset();
903                 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
904         }
905
906         return 0;
907 }
908
909 // remove the leading and trailing spaces in a string
910 void trim(char *str) {
911         int pos = 0;
912         char *dest = str;
913
914         // skip leading blanks
915         while (str[pos] <= ' ' && str[pos] > 0)
916                 pos++;
917
918         while (str[pos]) {
919                 *(dest++) = str[pos];
920                 pos++;
921         }
922
923         *(dest--) = '\0'; // store the null
924
925         // remove trailing blanks
926         while (dest >= str && *dest <= ' ' && *dest > 0)
927                 *(dest--) = '\0';
928 }
929
930 // lookup table for crc calculation
931 static unsigned short crctab[256] = {
932         0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
933         0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
934         0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
935         0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
936         0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
937         0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
938         0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
939         0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
940         0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
941         0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
942         0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
943         0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
944         0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
945         0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
946         0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
947         0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
948         0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
949         0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
950         0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
951         0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
952         0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
953         0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
954         0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
955         0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
956         0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
957         0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
958         0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
959         0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
960         0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
961 };
962
963 u16 calcCrc(u8 *d, int len) {
964         u16 crc = 0;
965         int i;
966
967         for (i = 0; i < len; i++) {
968                 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);
969         }
970
971         return ~crc;
972 }