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