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