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