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