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