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