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