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