Merge pull request #665 from QuarkTheAwesome/libretro-merge-be
[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 void mmssdd( char *b, char *p )
60 {
61         int m, s, d;
62         int block = SWAP32(*((uint32_t*) b));
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) == -1) 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, sizeof(mybuf), 1, 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, sizeof(EXE_HEADER), 1, 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, sizeof(opcode), 1, tmpFile) != sizeof(opcode))
502                                                 goto fail_io;
503                                         switch (opcode) {
504                                                 case 1: /* Section loading */
505                                                         if (fread(&section_address, sizeof(section_address), 1, tmpFile) != sizeof(section_address))
506                                                                 goto fail_io;
507                                                         if (fread(&section_size, sizeof(section_size), 1, 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, sizeof(psxRegs.pc), 1, 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         fclose(tmpFile);
553         return retval;
554
555 fail_io:
556 #ifndef NDEBUG
557         SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
558 #endif
559         fclose(tmpFile);
560         return -1;
561 }
562
563 // STATES
564
565 static void *zlib_open(const char *name, const char *mode)
566 {
567         return gzopen(name, mode);
568 }
569
570 static int zlib_read(void *file, void *buf, u32 len)
571 {
572         return gzread(file, buf, len);
573 }
574
575 static int zlib_write(void *file, const void *buf, u32 len)
576 {
577         return gzwrite(file, buf, len);
578 }
579
580 static long zlib_seek(void *file, long offs, int whence)
581 {
582         return gzseek(file, offs, whence);
583 }
584
585 static void zlib_close(void *file)
586 {
587         gzclose(file);
588 }
589
590 struct PcsxSaveFuncs SaveFuncs = {
591         zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
592 };
593
594 static const char PcsxHeader[32] = "STv4 PCSX v" PCSX_VERSION;
595
596 // Savestate Versioning!
597 // If you make changes to the savestate version, please increment the value below.
598 static const u32 SaveVersion = 0x8b410006;
599
600 int SaveState(const char *file) {
601         void *f;
602         GPUFreeze_t *gpufP;
603         SPUFreeze_t *spufP;
604         int Size;
605         unsigned char *pMem;
606
607         f = SaveFuncs.open(file, "wb");
608         if (f == NULL) return -1;
609
610         new_dyna_before_save();
611
612         if (drc_is_lightrec() && Config.Cpu != CPU_INTERPRETER)
613                 lightrec_plugin_prepare_save_state();
614
615         SaveFuncs.write(f, (void *)PcsxHeader, 32);
616         SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
617         SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
618
619         pMem = (unsigned char *)malloc(128 * 96 * 3);
620         if (pMem == NULL) return -1;
621         GPU_getScreenPic(pMem);
622         SaveFuncs.write(f, pMem, 128 * 96 * 3);
623         free(pMem);
624
625         if (Config.HLE)
626                 psxBiosFreeze(1);
627
628         SaveFuncs.write(f, psxM, 0x00200000);
629         SaveFuncs.write(f, psxR, 0x00080000);
630         SaveFuncs.write(f, psxH, 0x00010000);
631         // only partial save of psxRegisters to maintain savestate compat
632         SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
633
634         // gpu
635         gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
636         gpufP->ulFreezeVersion = 1;
637         GPU_freeze(1, gpufP);
638         SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
639         free(gpufP);
640
641         // spu
642         spufP = (SPUFreeze_t *) malloc(16);
643         SPU_freeze(2, spufP, psxRegs.cycle);
644         Size = spufP->Size; SaveFuncs.write(f, &Size, 4);
645         free(spufP);
646         spufP = (SPUFreeze_t *) malloc(Size);
647         SPU_freeze(1, spufP, psxRegs.cycle);
648         SaveFuncs.write(f, spufP, Size);
649         free(spufP);
650
651         sioFreeze(f, 1);
652         cdrFreeze(f, 1);
653         psxHwFreeze(f, 1);
654         psxRcntFreeze(f, 1);
655         mdecFreeze(f, 1);
656         new_dyna_freeze(f, 1);
657
658         SaveFuncs.close(f);
659
660         new_dyna_after_save();
661
662         return 0;
663 }
664
665 int LoadState(const char *file) {
666         void *f;
667         GPUFreeze_t *gpufP;
668         SPUFreeze_t *spufP;
669         int Size;
670         char header[32];
671         u32 version;
672         boolean hle;
673
674         f = SaveFuncs.open(file, "rb");
675         if (f == NULL) return -1;
676
677         SaveFuncs.read(f, header, sizeof(header));
678         SaveFuncs.read(f, &version, sizeof(u32));
679         SaveFuncs.read(f, &hle, sizeof(boolean));
680
681         if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
682                 SaveFuncs.close(f);
683                 return -1;
684         }
685         Config.HLE = hle;
686
687         if (Config.HLE)
688                 psxBiosInit();
689
690         if (!drc_is_lightrec() || Config.Cpu == CPU_INTERPRETER)
691                 psxCpu->Reset();
692         SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
693
694         SaveFuncs.read(f, psxM, 0x00200000);
695         SaveFuncs.read(f, psxR, 0x00080000);
696         SaveFuncs.read(f, psxH, 0x00010000);
697         SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
698         psxRegs.gteBusyCycle = psxRegs.cycle;
699
700         if (drc_is_lightrec() && Config.Cpu != CPU_INTERPRETER)
701                 lightrec_plugin_prepare_load_state();
702
703         if (Config.HLE)
704                 psxBiosFreeze(0);
705
706         // gpu
707         gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
708         SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
709         GPU_freeze(0, gpufP);
710         free(gpufP);
711         if (HW_GPU_STATUS == 0)
712                 HW_GPU_STATUS = SWAP32(GPU_readStatus());
713
714         // spu
715         SaveFuncs.read(f, &Size, 4);
716         spufP = (SPUFreeze_t *)malloc(Size);
717         SaveFuncs.read(f, spufP, Size);
718         SPU_freeze(0, spufP, psxRegs.cycle);
719         free(spufP);
720
721         sioFreeze(f, 0);
722         cdrFreeze(f, 0);
723         psxHwFreeze(f, 0);
724         psxRcntFreeze(f, 0);
725         mdecFreeze(f, 0);
726         new_dyna_freeze(f, 0);
727
728         SaveFuncs.close(f);
729
730         return 0;
731 }
732
733 int CheckState(const char *file) {
734         void *f;
735         char header[32];
736         u32 version;
737         boolean hle;
738
739         f = SaveFuncs.open(file, "rb");
740         if (f == NULL) return -1;
741
742         SaveFuncs.read(f, header, sizeof(header));
743         SaveFuncs.read(f, &version, sizeof(u32));
744         SaveFuncs.read(f, &hle, sizeof(boolean));
745
746         SaveFuncs.close(f);
747
748         if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
749                 return -1;
750
751         return 0;
752 }
753
754 // NET Function Helpers
755
756 int SendPcsxInfo() {
757         if (NET_recvData == NULL || NET_sendData == NULL)
758                 return 0;
759
760         NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
761         NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
762         NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
763         NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
764         NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
765         NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
766
767         return 0;
768 }
769
770 int RecvPcsxInfo() {
771         int tmp;
772
773         if (NET_recvData == NULL || NET_sendData == NULL)
774                 return 0;
775
776         NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
777         NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
778         NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
779         NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), 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 }