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