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