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