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