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