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