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