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