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