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