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