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