frontend: handle double res rendering, enable on x86_64
[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) == -1) 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, sizeof(mybuf), 1, 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, sizeof(EXE_HEADER), 1, 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, sizeof(opcode), 1, tmpFile) != sizeof(opcode))
503                                                 goto fail_io;
504                                         switch (opcode) {
505                                                 case 1: /* Section loading */
506                                                         if (fread(&section_address, sizeof(section_address), 1, tmpFile) != sizeof(section_address))
507                                                                 goto fail_io;
508                                                         if (fread(&section_size, sizeof(section_size), 1, 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, sizeof(psxRegs.pc), 1, 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         fclose(tmpFile);
554         return retval;
555
556 fail_io:
557 #ifndef NDEBUG
558         SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
559 #endif
560         fclose(tmpFile);
561         return -1;
562 }
563
564 // STATES
565
566 static void *zlib_open(const char *name, const char *mode)
567 {
568         return gzopen(name, mode);
569 }
570
571 static int zlib_read(void *file, void *buf, u32 len)
572 {
573         return gzread(file, buf, len);
574 }
575
576 static int zlib_write(void *file, const void *buf, u32 len)
577 {
578         return gzwrite(file, buf, len);
579 }
580
581 static long zlib_seek(void *file, long offs, int whence)
582 {
583         return gzseek(file, offs, whence);
584 }
585
586 static void zlib_close(void *file)
587 {
588         gzclose(file);
589 }
590
591 struct PcsxSaveFuncs SaveFuncs = {
592         zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
593 };
594
595 static const char PcsxHeader[32] = "STv4 PCSX v" PCSX_VERSION;
596
597 // Savestate Versioning!
598 // If you make changes to the savestate version, please increment the value below.
599 static const u32 SaveVersion = 0x8b410006;
600
601 int SaveState(const char *file) {
602         void *f;
603         GPUFreeze_t *gpufP;
604         SPUFreeze_t *spufP;
605         int Size;
606         unsigned char *pMem;
607
608         f = SaveFuncs.open(file, "wb");
609         if (f == NULL) return -1;
610
611         new_dyna_before_save();
612
613         if (drc_is_lightrec() && Config.Cpu != CPU_INTERPRETER)
614                 lightrec_plugin_prepare_save_state();
615
616         SaveFuncs.write(f, (void *)PcsxHeader, 32);
617         SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
618         SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
619
620         pMem = (unsigned char *)malloc(128 * 96 * 3);
621         if (pMem == NULL) return -1;
622         GPU_getScreenPic(pMem);
623         SaveFuncs.write(f, pMem, 128 * 96 * 3);
624         free(pMem);
625
626         if (Config.HLE)
627                 psxBiosFreeze(1);
628
629         SaveFuncs.write(f, psxM, 0x00200000);
630         SaveFuncs.write(f, psxR, 0x00080000);
631         SaveFuncs.write(f, psxH, 0x00010000);
632         // only partial save of psxRegisters to maintain savestate compat
633         SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
634
635         // gpu
636         gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
637         gpufP->ulFreezeVersion = 1;
638         GPU_freeze(1, gpufP);
639         SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
640         free(gpufP);
641
642         // spu
643         spufP = (SPUFreeze_t *) malloc(16);
644         SPU_freeze(2, spufP, psxRegs.cycle);
645         Size = spufP->Size; SaveFuncs.write(f, &Size, 4);
646         free(spufP);
647         spufP = (SPUFreeze_t *) malloc(Size);
648         SPU_freeze(1, spufP, psxRegs.cycle);
649         SaveFuncs.write(f, spufP, Size);
650         free(spufP);
651
652         sioFreeze(f, 1);
653         cdrFreeze(f, 1);
654         psxHwFreeze(f, 1);
655         psxRcntFreeze(f, 1);
656         mdecFreeze(f, 1);
657         new_dyna_freeze(f, 1);
658
659         SaveFuncs.close(f);
660
661         new_dyna_after_save();
662
663         return 0;
664 }
665
666 int LoadState(const char *file) {
667         void *f;
668         GPUFreeze_t *gpufP;
669         SPUFreeze_t *spufP;
670         int Size;
671         char header[32];
672         u32 version;
673         boolean hle;
674
675         f = SaveFuncs.open(file, "rb");
676         if (f == NULL) return -1;
677
678         SaveFuncs.read(f, header, sizeof(header));
679         SaveFuncs.read(f, &version, sizeof(u32));
680         SaveFuncs.read(f, &hle, sizeof(boolean));
681
682         if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
683                 SaveFuncs.close(f);
684                 return -1;
685         }
686         Config.HLE = hle;
687
688         if (Config.HLE)
689                 psxBiosInit();
690
691         if (!drc_is_lightrec() || Config.Cpu == CPU_INTERPRETER)
692                 psxCpu->Reset();
693         SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
694
695         SaveFuncs.read(f, psxM, 0x00200000);
696         SaveFuncs.read(f, psxR, 0x00080000);
697         SaveFuncs.read(f, psxH, 0x00010000);
698         SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
699         psxRegs.gteBusyCycle = psxRegs.cycle;
700
701         if (drc_is_lightrec() && Config.Cpu != CPU_INTERPRETER)
702                 lightrec_plugin_prepare_load_state();
703
704         if (Config.HLE)
705                 psxBiosFreeze(0);
706
707         // gpu
708         gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
709         SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
710         GPU_freeze(0, gpufP);
711         free(gpufP);
712         if (HW_GPU_STATUS == 0)
713                 HW_GPU_STATUS = SWAP32(GPU_readStatus());
714
715         // spu
716         SaveFuncs.read(f, &Size, 4);
717         spufP = (SPUFreeze_t *)malloc(Size);
718         SaveFuncs.read(f, spufP, Size);
719         SPU_freeze(0, spufP, psxRegs.cycle);
720         free(spufP);
721
722         sioFreeze(f, 0);
723         cdrFreeze(f, 0);
724         psxHwFreeze(f, 0);
725         psxRcntFreeze(f, 0);
726         mdecFreeze(f, 0);
727         new_dyna_freeze(f, 0);
728
729         SaveFuncs.close(f);
730
731         return 0;
732 }
733
734 int CheckState(const char *file) {
735         void *f;
736         char header[32];
737         u32 version;
738         boolean hle;
739
740         f = SaveFuncs.open(file, "rb");
741         if (f == NULL) return -1;
742
743         SaveFuncs.read(f, header, sizeof(header));
744         SaveFuncs.read(f, &version, sizeof(u32));
745         SaveFuncs.read(f, &hle, sizeof(boolean));
746
747         SaveFuncs.close(f);
748
749         if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
750                 return -1;
751
752         return 0;
753 }
754
755 // NET Function Helpers
756
757 int SendPcsxInfo() {
758         if (NET_recvData == NULL || NET_sendData == NULL)
759                 return 0;
760
761         NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
762         NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
763         NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
764         NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
765         NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
766         NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
767
768         return 0;
769 }
770
771 int RecvPcsxInfo() {
772         int tmp;
773
774         if (NET_recvData == NULL || NET_sendData == NULL)
775                 return 0;
776
777         NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
778         NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
779         NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
780         NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
781         NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
782
783         SysUpdate();
784
785         tmp = Config.Cpu;
786         NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
787         if (tmp != Config.Cpu) {
788                 psxCpu->Shutdown();
789 #ifndef DRC_DISABLE
790                 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
791                 else psxCpu = &psxRec;
792 #else
793                 psxCpu = &psxInt;
794 #endif
795                 if (psxCpu->Init() == -1) {
796                         SysClose(); return -1;
797                 }
798                 psxCpu->Reset();
799         }
800
801         return 0;
802 }
803
804 // remove the leading and trailing spaces in a string
805 void trim(char *str) {
806         int pos = 0;
807         char *dest = str;
808
809         // skip leading blanks
810         while (str[pos] <= ' ' && str[pos] > 0)
811                 pos++;
812
813         while (str[pos]) {
814                 *(dest++) = str[pos];
815                 pos++;
816         }
817
818         *(dest--) = '\0'; // store the null
819
820         // remove trailing blanks
821         while (dest >= str && *dest <= ' ' && *dest > 0)
822                 *(dest--) = '\0';
823 }
824
825 // lookup table for crc calculation
826 static unsigned short crctab[256] = {
827         0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
828         0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
829         0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
830         0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
831         0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
832         0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
833         0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
834         0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
835         0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
836         0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
837         0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
838         0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
839         0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
840         0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
841         0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
842         0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
843         0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
844         0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
845         0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
846         0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
847         0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
848         0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
849         0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
850         0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
851         0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
852         0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
853         0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
854         0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
855         0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
856 };
857
858 u16 calcCrc(u8 *d, int len) {
859         u16 crc = 0;
860         int i;
861
862         for (i = 0; i < len; i++) {
863                 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);
864         }
865
866         return ~crc;
867 }