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