Merge pull request #389 from notaz/master
[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                 strncpy(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         fread(mybuf, 2048, 1, f);
415         fseek(f, current, SEEK_SET);
416
417         exe_hdr = (EXE_HEADER *)mybuf;
418         if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
419                 return PSX_EXE;
420
421         if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
422                 return CPE_EXE;
423
424         coff_hdr = (FILHDR *)mybuf;
425         if (SWAPu16(coff_hdr->f_magic) == 0x0162)
426                 return COFF_EXE;
427
428         return INVALID_EXE;
429 }
430
431 // temporary pandora workaround..
432 // FIXME: remove
433 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
434 {
435         void *tmp;
436         size_t ret = 0;
437
438         tmp = malloc(size * nmemb);
439         if (tmp) {
440                 ret = fread(tmp, size, nmemb, stream);
441                 memcpy(ptr, tmp, size * nmemb);
442                 free(tmp);
443         }
444         return ret;
445 }
446
447 int Load(const char *ExePath) {
448         FILE *tmpFile;
449         EXE_HEADER tmpHead;
450         int type;
451         int retval = 0;
452         u8 opcode;
453         u32 section_address, section_size;
454         void *mem;
455
456         strcpy(CdromId, "SLUS99999");
457         strcpy(CdromLabel, "SLUS_999.99");
458
459         tmpFile = fopen(ExePath, "rb");
460         if (tmpFile == NULL) {
461                 SysPrintf(_("Error opening file: %s.\n"), ExePath);
462                 retval = -1;
463         } else {
464                 type = PSXGetFileType(tmpFile);
465                 switch (type) {
466                         case PSX_EXE:
467                                 fread(&tmpHead,sizeof(EXE_HEADER),1,tmpFile);
468                                 section_address = SWAP32(tmpHead.t_addr);
469                                 section_size = SWAP32(tmpHead.t_size);
470                                 mem = PSXM(section_address);
471                                 if (mem != NULL) {
472                                         fseek(tmpFile, 0x800, SEEK_SET);
473                                         fread_to_ram(mem, section_size, 1, tmpFile);
474                                         psxCpu->Clear(section_address, section_size / 4);
475                                 }
476                                 fclose(tmpFile);
477                                 psxRegs.pc = SWAP32(tmpHead.pc0);
478                                 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
479                                 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
480                                 if (psxRegs.GPR.n.sp == 0)
481                                         psxRegs.GPR.n.sp = 0x801fff00;
482                                 retval = 0;
483                                 break;
484                         case CPE_EXE:
485                                 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
486                                 do {
487                                         fread(&opcode, 1, 1, tmpFile);
488                                         switch (opcode) {
489                                                 case 1: /* Section loading */
490                                                         fread(&section_address, 4, 1, tmpFile);
491                                                         fread(&section_size, 4, 1, tmpFile);
492                                                         section_address = SWAPu32(section_address);
493                                                         section_size = SWAPu32(section_size);
494 #ifdef EMU_LOG
495                                                         EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
496 #endif
497                                                         mem = PSXM(section_address);
498                                                         if (mem != NULL) {
499                                                                 fread_to_ram(mem, section_size, 1, tmpFile);
500                                                                 psxCpu->Clear(section_address, section_size / 4);
501                                                         }
502                                                         break;
503                                                 case 3: /* register loading (PC only?) */
504                                                         fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
505                                                         fread(&psxRegs.pc, 4, 1, tmpFile);
506                                                         psxRegs.pc = SWAPu32(psxRegs.pc);
507                                                         break;
508                                                 case 0: /* End of file */
509                                                         break;
510                                                 default:
511                                                         SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
512                                                         retval = -1;
513                                                         break;
514                                         }
515                                 } while (opcode != 0 && retval == 0);
516                                 break;
517                         case COFF_EXE:
518                                 SysPrintf(_("COFF files not supported.\n"));
519                                 retval = -1;
520                                 break;
521                         case INVALID_EXE:
522                                 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
523                                 SysPrintf(_("(did you forget -cdfile ?)\n"));
524                                 retval = -1;
525                                 break;
526                 }
527         }
528
529         if (retval != 0) {
530                 CdromId[0] = '\0';
531                 CdromLabel[0] = '\0';
532         }
533
534         return retval;
535 }
536
537 // STATES
538
539 static void *zlib_open(const char *name, const char *mode)
540 {
541         return gzopen(name, mode);
542 }
543
544 static int zlib_read(void *file, void *buf, u32 len)
545 {
546         return gzread(file, buf, len);
547 }
548
549 static int zlib_write(void *file, const void *buf, u32 len)
550 {
551         return gzwrite(file, buf, len);
552 }
553
554 static long zlib_seek(void *file, long offs, int whence)
555 {
556         return gzseek(file, offs, whence);
557 }
558
559 static void zlib_close(void *file)
560 {
561         gzclose(file);
562 }
563
564 struct PcsxSaveFuncs SaveFuncs = {
565         zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
566 };
567
568 static const char PcsxHeader[32] = "STv4 PCSX v" PCSX_VERSION;
569
570 // Savestate Versioning!
571 // If you make changes to the savestate version, please increment the value below.
572 static const u32 SaveVersion = 0x8b410006;
573
574 int SaveState(const char *file) {
575         void *f;
576         GPUFreeze_t *gpufP;
577         SPUFreeze_t *spufP;
578         int Size;
579         unsigned char *pMem;
580
581         f = SaveFuncs.open(file, "wb");
582         if (f == NULL) return -1;
583
584         new_dyna_before_save();
585
586         SaveFuncs.write(f, (void *)PcsxHeader, 32);
587         SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
588         SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
589
590         pMem = (unsigned char *)malloc(128 * 96 * 3);
591         if (pMem == NULL) return -1;
592         GPU_getScreenPic(pMem);
593         SaveFuncs.write(f, pMem, 128 * 96 * 3);
594         free(pMem);
595
596         if (Config.HLE)
597                 psxBiosFreeze(1);
598
599         SaveFuncs.write(f, psxM, 0x00200000);
600         SaveFuncs.write(f, psxR, 0x00080000);
601         SaveFuncs.write(f, psxH, 0x00010000);
602         SaveFuncs.write(f, (void *)&psxRegs, sizeof(psxRegs));
603
604         // gpu
605         gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
606         gpufP->ulFreezeVersion = 1;
607         GPU_freeze(1, gpufP);
608         SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
609         free(gpufP);
610
611         // spu
612         spufP = (SPUFreeze_t *) malloc(16);
613         SPU_freeze(2, spufP, psxRegs.cycle);
614         Size = spufP->Size; SaveFuncs.write(f, &Size, 4);
615         free(spufP);
616         spufP = (SPUFreeze_t *) malloc(Size);
617         SPU_freeze(1, spufP, psxRegs.cycle);
618         SaveFuncs.write(f, spufP, Size);
619         free(spufP);
620
621         sioFreeze(f, 1);
622         cdrFreeze(f, 1);
623         psxHwFreeze(f, 1);
624         psxRcntFreeze(f, 1);
625         mdecFreeze(f, 1);
626         new_dyna_freeze(f, 1);
627
628         SaveFuncs.close(f);
629
630         new_dyna_after_save();
631
632         return 0;
633 }
634
635 int LoadState(const char *file) {
636         void *f;
637         GPUFreeze_t *gpufP;
638         SPUFreeze_t *spufP;
639         int Size;
640         char header[32];
641         u32 version;
642         boolean hle;
643
644         f = SaveFuncs.open(file, "rb");
645         if (f == NULL) return -1;
646
647         SaveFuncs.read(f, header, sizeof(header));
648         SaveFuncs.read(f, &version, sizeof(u32));
649         SaveFuncs.read(f, &hle, sizeof(boolean));
650
651         if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
652                 SaveFuncs.close(f);
653                 return -1;
654         }
655         Config.HLE = hle;
656
657         if (Config.HLE)
658                 psxBiosInit();
659
660 #if defined(LIGHTREC)
661         if (Config.Cpu != CPU_INTERPRETER)
662                 psxCpu->Clear(0, UINT32_MAX); //clear all
663         else
664 #endif
665         psxCpu->Reset();
666         SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
667
668         SaveFuncs.read(f, psxM, 0x00200000);
669         SaveFuncs.read(f, psxR, 0x00080000);
670         SaveFuncs.read(f, psxH, 0x00010000);
671         SaveFuncs.read(f, (void *)&psxRegs, sizeof(psxRegs));
672
673         if (Config.HLE)
674                 psxBiosFreeze(0);
675
676         // gpu
677         gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
678         SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
679         GPU_freeze(0, gpufP);
680         free(gpufP);
681         if (HW_GPU_STATUS == 0)
682                 HW_GPU_STATUS = GPU_readStatus();
683
684         // spu
685         SaveFuncs.read(f, &Size, 4);
686         spufP = (SPUFreeze_t *)malloc(Size);
687         SaveFuncs.read(f, spufP, Size);
688         SPU_freeze(0, spufP, psxRegs.cycle);
689         free(spufP);
690
691         sioFreeze(f, 0);
692         cdrFreeze(f, 0);
693         psxHwFreeze(f, 0);
694         psxRcntFreeze(f, 0);
695         mdecFreeze(f, 0);
696         new_dyna_freeze(f, 0);
697
698         SaveFuncs.close(f);
699
700         return 0;
701 }
702
703 int CheckState(const char *file) {
704         void *f;
705         char header[32];
706         u32 version;
707         boolean hle;
708
709         f = SaveFuncs.open(file, "rb");
710         if (f == NULL) return -1;
711
712         SaveFuncs.read(f, header, sizeof(header));
713         SaveFuncs.read(f, &version, sizeof(u32));
714         SaveFuncs.read(f, &hle, sizeof(boolean));
715
716         SaveFuncs.close(f);
717
718         if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
719                 return -1;
720
721         return 0;
722 }
723
724 // NET Function Helpers
725
726 int SendPcsxInfo() {
727         if (NET_recvData == NULL || NET_sendData == NULL)
728                 return 0;
729
730         NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
731         NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
732         NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
733         NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
734         NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
735         NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
736
737         return 0;
738 }
739
740 int RecvPcsxInfo() {
741         int tmp;
742
743         if (NET_recvData == NULL || NET_sendData == NULL)
744                 return 0;
745
746         NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
747         NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
748         NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
749         NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
750         NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
751
752         SysUpdate();
753
754         tmp = Config.Cpu;
755         NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
756         if (tmp != Config.Cpu) {
757                 psxCpu->Shutdown();
758 #if defined(NEW_DYNAREC) || defined(LIGHTREC)
759                 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
760                 else psxCpu = &psxRec;
761 #else
762                 psxCpu = &psxInt;
763 #endif
764                 if (psxCpu->Init() == -1) {
765                         SysClose(); return -1;
766                 }
767                 psxCpu->Reset();
768         }
769
770         return 0;
771 }
772
773 // remove the leading and trailing spaces in a string
774 void trim(char *str) {
775         int pos = 0;
776         char *dest = str;
777
778         // skip leading blanks
779         while (str[pos] <= ' ' && str[pos] > 0)
780                 pos++;
781
782         while (str[pos]) {
783                 *(dest++) = str[pos];
784                 pos++;
785         }
786
787         *(dest--) = '\0'; // store the null
788
789         // remove trailing blanks
790         while (dest >= str && *dest <= ' ' && *dest > 0)
791                 *(dest--) = '\0';
792 }
793
794 // lookup table for crc calculation
795 static unsigned short crctab[256] = {
796         0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
797         0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
798         0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
799         0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
800         0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
801         0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
802         0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
803         0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
804         0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
805         0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
806         0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
807         0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
808         0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
809         0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
810         0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
811         0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
812         0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
813         0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
814         0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
815         0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
816         0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
817         0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
818         0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
819         0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
820         0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
821         0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
822         0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
823         0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
824         0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
825 };
826
827 u16 calcCrc(u8 *d, int len) {
828         u16 crc = 0;
829         int i;
830
831         for (i = 0; i < len; i++) {
832                 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);
833         }
834
835         return ~crc;
836 }