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