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