frontend: update libpicofe, fix missed callbacks
[pcsx_rearmed.git] / libpcsxcore / misc.c
CommitLineData
ef79bbde
P
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
81dbbf4c 24#include <stddef.h>
dc3178e9 25#include <errno.h>
7b75929b 26#include <assert.h>
ef79bbde
P
27#include "misc.h"
28#include "cdrom.h"
08d9d257 29#include "cdrom-async.h"
ef79bbde 30#include "mdec.h"
ddbaf678 31#include "gpu.h"
ef79bbde 32#include "ppf.h"
de74f599 33#include "psxbios.h"
eedfe806 34#include "database.h"
496d88d4 35#include <zlib.h>
f906311d 36#include "revision.h"
ef79bbde
P
37
38char CdromId[10] = "";
39char CdromLabel[33] = "";
6a131b03 40int CdromFrontendId; // for frontend use
ef79bbde 41
f906311d 42static u32 save_counter;
43
ef79bbde
P
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
0bcd56e9 66static void mmssdd(const char *b, char *p)
ef79bbde 67{
0bcd56e9 68 const unsigned char *ub = (void *)b;
1c654475 69 int block = (ub[3] << 24) | (ub[2] << 16) | (ub[1] << 8) | ub[0];
0bcd56e9 70 int m, s, d;
ef79bbde
P
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
ef79bbde
P
78 p[0] = m;
79 p[1] = s;
80 p[2] = d;
81}
82
83#define incTime() \
ef79bbde
P
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 } \
ef79bbde
P
93
94#define READTRACK() \
08d9d257 95 if (cdra_readTrack(time)) return -1; \
96 buf = cdra_getBuffer(); \
305c8c93 97 if (buf == NULL) return -1; \
98 else CheckPPFCache((u8 *)buf, time[0], time[1], time[2]);
ef79bbde
P
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
305c8c93 108int GetCdromFile(u8 *mdir, u8 *time, char *filename) {
ef79bbde 109 struct iso_directory_record *dir;
5be6eaeb 110 int retval = -1;
305c8c93 111 u8 ddir[4096];
ef79bbde
P
112 u8 *buf;
113 int i;
114
115 // only try to scan if a filename is given
0a50313e 116 if (filename == INVALID_PTR || !strlen(filename)) return -1;
ef79bbde
P
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 }
1c2c2bfb 124 i += (u8)dir->length[0];
ef79bbde
P
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);
5be6eaeb 140 retval = 0;
ef79bbde
P
141 break;
142 }
143 }
144 }
5be6eaeb 145 return retval;
ef79bbde
P
146}
147
6d75addf 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;
14b3bd95 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;
6d75addf 160
161 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
162}
163
02b1a085 164int BiosBootBypass() {
165 struct CdrStat stat = { 0, 0, };
7b75929b 166 assert(psxRegs.pc == 0x80030000);
167
02b1a085 168 // no bypass if the lid is open
169 CDR__getStatus(&stat);
170 if (stat.Status & 0x10)
171 return 0;
172
7b75929b 173 // skip BIOS logos and region check
174 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
175 psxRegs.pc = psxRegs.GPR.n.ra;
02b1a085 176 return 1;
7b75929b 177}
178
dc4fa8bc 179static void getFromCnf(char *buf, const char *key, u32 *val)
180{
181 buf = strstr(buf, key);
182 if (buf)
183 buf = strchr(buf, '=');
dc3178e9 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 }
dc4fa8bc 191}
192
ef79bbde 193int LoadCdrom() {
de74f599 194 union {
195 EXE_HEADER h;
196 u32 d[sizeof(EXE_HEADER) / sizeof(u32)];
197 } tmpHead;
ef79bbde
P
198 struct iso_directory_record *dir;
199 u8 time[4], *buf;
200 u8 mdir[4096];
305c8c93 201 char exename[256];
dc4fa8bc 202 u32 cnf_tcb = 4;
203 u32 cnf_event = 16;
204 u32 cnf_stack = 0;
de74f599 205 u32 t_addr;
206 u32 t_size;
dc4fa8bc 207 u32 sp = 0;
de74f599 208 int i, ret;
ef79bbde 209
f906311d 210 save_counter = 0;
211
7b75929b 212 if (!Config.HLE) {
da65071f 213 if (psxRegs.pc != 0x80030000) // BiosBootBypass'ed or custom BIOS?
214 return 0;
215 if (Config.SlowBoot)
216 return 0;
ef79bbde
P
217 }
218
08d9d257 219 time[0] = 0; time[1] = 2; time[2] = 0x10;
ef79bbde
P
220
221 READTRACK();
222
223 // skip head and sub, and go to the root directory record
7a8d521f 224 dir = (struct iso_directory_record*) &buf[12+156];
ef79bbde
P
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;
7b75929b 234 strcpy(exename, "PSX.EXE;1");
ef79bbde
P
235
236 READTRACK();
237 }
238 else {
239 // read the SYSTEM.CNF
240 READTRACK();
dc4fa8bc 241 buf[1023] = 0;
ef79bbde 242
dc4fa8bc 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) {
305c8c93 247 char *ptr = strstr((char *)buf + 12, "cdrom:");
ef79bbde
P
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 }
dc4fa8bc 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)
5c8119b8 266 psxBiosCnfLoaded(cnf_tcb, cnf_event, cnf_stack);
ef79bbde
P
267
268 // Read the EXE-Header
269 READTRACK();
270 }
271
272 memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
de74f599 273 for (i = 2; i < sizeof(tmpHead.d) / sizeof(tmpHead.d[0]); i++)
274 tmpHead.d[i] = SWAP32(tmpHead.d[i]);
ef79bbde 275
de74f599 276 SysPrintf("manual booting '%s' pc=%x\n", exename, tmpHead.h.pc0);
277 sp = tmpHead.h.s_addr;
dc4fa8bc 278 if (cnf_stack)
279 sp = cnf_stack;
de74f599 280 SetBootRegs(tmpHead.h.pc0, tmpHead.h.gp0, sp);
ebe6ff2b 281
ef79bbde 282 // Read the rest of the main executable
de74f599 283 for (t_addr = tmpHead.h.t_addr, t_size = tmpHead.h.t_size; t_size & ~2047; ) {
284 void *ptr = (void *)PSXM(t_addr);
ef79bbde
P
285
286 incTime();
287 READTRACK();
288
7a8d521f 289 if (ptr != INVALID_PTR) memcpy(ptr, buf+12, 2048);
ef79bbde 290
de74f599 291 t_addr += 2048;
292 t_size -= 2048;
ef79bbde
P
293 }
294
de74f599 295 psxCpu->Clear(tmpHead.h.t_addr, tmpHead.h.t_size / 4);
296 //psxCpu->Reset();
297
298 if (Config.HLE)
b34d6a80 299 psxBiosCheckExe(tmpHead.h.t_addr, tmpHead.h.t_size, 0);
de74f599 300
ef79bbde
P
301 return 0;
302}
303
1dc68512 304int LoadCdromFile(const char *filename, EXE_HEADER *head, u8 *time_bcd_out) {
ef79bbde
P
305 struct iso_directory_record *dir;
306 u8 time[4],*buf;
305c8c93 307 u8 mdir[4096];
308 char exename[256];
457a46ec 309 const char *p1, *p2;
ef79bbde 310 u32 size, addr;
305c8c93 311 void *mem;
ef79bbde 312
86c70511 313 if (filename == INVALID_PTR)
314 return -1;
315
457a46ec 316 p1 = filename;
317 if ((p2 = strchr(p1, ':')))
318 p1 = p2 + 1;
319 while (*p1 == '\\')
320 p1++;
321 snprintf(exename, sizeof(exename), "%s", p1);
ef79bbde 322
08d9d257 323 time[0] = 0; time[1] = 2; time[2] = 0x10;
ef79bbde
P
324
325 READTRACK();
326
327 // skip head and sub, and go to the root directory record
7a8d521f 328 dir = (struct iso_directory_record *)&buf[12 + 156];
ef79bbde
P
329
330 mmssdd(dir->extent, (char*)time);
331
332 READDIR(mdir);
333
334 if (GetCdromFile(mdir, time, exename) == -1) return -1;
335
336 READTRACK();
1dc68512 337 incTime();
ef79bbde
P
338
339 memcpy(head, buf + 12, sizeof(EXE_HEADER));
86c70511 340 size = SWAP32(head->t_size);
341 addr = SWAP32(head->t_addr);
ef79bbde 342
ebe6ff2b 343 psxCpu->Clear(addr, size / 4);
86c70511 344 //psxCpu->Reset();
ebe6ff2b 345
346 while (size & ~2047) {
ef79bbde 347 READTRACK();
1dc68512 348 incTime();
ef79bbde 349
305c8c93 350 mem = PSXM(addr);
7a8d521f 351 if (mem != INVALID_PTR)
305c8c93 352 memcpy(mem, buf + 12, 2048);
ef79bbde
P
353
354 size -= 2048;
355 addr += 2048;
356 }
0bcd56e9 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 }
ef79bbde
P
362
363 return 0;
364}
365
366int CheckCdrom() {
367 struct iso_directory_record *dir;
1f6c92b8 368 struct CdrStat stat = { 0, 0, };
c86d5b21 369 unsigned char time[4] = { 0, 2, 4 };
305c8c93 370 char *buf;
ef79bbde
P
371 unsigned char mdir[4096];
372 char exename[256];
0b6f8a28 373 int lic_region_detected = -1;
e06ee7f4 374 int i, len, c;
ef79bbde
P
375
376 FreePPFCache();
1f6c92b8 377 memset(CdromLabel, 0, sizeof(CdromLabel));
378 memset(CdromId, 0, sizeof(CdromId));
379 memset(exename, 0, sizeof(exename));
ef79bbde 380
1f6c92b8 381 if (!Config.HLE && Config.SlowBoot) {
0aa7361c 382 // boot to BIOS in case of CDDA or lid is open
08d9d257 383 cdra_getStatus(&stat);
384 if ((stat.Status & 0x10) || stat.Type == 2 || cdra_readTrack(time))
1f6c92b8 385 return 0;
386 }
0b6f8a28 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;
ef79bbde
P
400 READTRACK();
401
ef79bbde
P
402 strncpy(CdromLabel, buf + 52, 32);
403
404 // skip head and sub, and go to the root directory record
7a8d521f 405 dir = (struct iso_directory_record *)&buf[12 + 156];
ef79bbde
P
406
407 mmssdd(dir->extent, (char *)time);
408
409 READDIR(mdir);
410
411 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
412 READTRACK();
413
9a9bcd78 414 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
ef79bbde 415 if (GetCdromFile(mdir, time, exename) == -1) {
9a9bcd78 416 sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
ef79bbde
P
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 }
7a8d521f 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 }
ef79bbde
P
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') {
e06ee7f4 448 len = strlen(exename);
449 c = 0;
450 for (i = 0; i < len; ++i) {
451 if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
452 break;
de0ed23d 453 if (isalnum((int)exename[i]))
e06ee7f4 454 CdromId[c++] = exename[i];
ef79bbde
P
455 }
456 }
457
0e2e3f49 458 if (CdromId[0] == '\0')
459 strcpy(CdromId, "SLUS99999");
460
ef79bbde 461 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
0b6f8a28 462 if (lic_region_detected >= 0)
463 Config.PsxType = lic_region_detected;
464 else if (
40337130 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.
ef79bbde
P
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);
e06ee7f4 480 SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
eedfe806 481
482 Apply_Hacks_Cdrom();
ef79bbde 483
e1fbc1a8 484 BuildPPFCache(NULL);
ef79bbde
P
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);
7a8d521f 497 if (fread(&mybuf, 1, sizeof(mybuf), f) != sizeof(mybuf))
498 goto io_fail;
499
ef79bbde
P
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;
7a8d521f 514
515io_fail:
516#ifndef NDEBUG
517 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
518#endif
519 return INVALID_EXE;
ef79bbde
P
520}
521
96bef96f 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;
7a8d521f 528
96bef96f 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 }
f906311d 535 else
536 ret = fread(ptr, size, nmemb, stream);
96bef96f 537 return ret;
538}
539
ef79bbde
P
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;
ebe6ff2b 547 void *mem;
ef79bbde 548
7a8d521f 549 strcpy(CdromId, "SLUS99999");
550 strcpy(CdromLabel, "SLUS_999.99");
ef79bbde
P
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:
7a8d521f 560 if (fread(&tmpHead, 1, sizeof(EXE_HEADER), tmpFile) != sizeof(EXE_HEADER))
561 goto fail_io;
ebe6ff2b 562 section_address = SWAP32(tmpHead.t_addr);
563 section_size = SWAP32(tmpHead.t_size);
564 mem = PSXM(section_address);
7a8d521f 565 if (mem != INVALID_PTR) {
566 fseek(tmpFile, 0x800, SEEK_SET);
96bef96f 567 fread_to_ram(mem, section_size, 1, tmpFile);
ebe6ff2b 568 psxCpu->Clear(section_address, section_size / 4);
569 }
6d75addf 570 SetBootRegs(SWAP32(tmpHead.pc0), SWAP32(tmpHead.gp0),
571 SWAP32(tmpHead.s_addr));
ef79bbde
P
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 {
7a8d521f 577 if (fread(&opcode, 1, sizeof(opcode), tmpFile) != sizeof(opcode))
578 goto fail_io;
ef79bbde
P
579 switch (opcode) {
580 case 1: /* Section loading */
7a8d521f 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;
ef79bbde
P
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
ebe6ff2b 590 mem = PSXM(section_address);
7a8d521f 591 if (mem != INVALID_PTR) {
96bef96f 592 fread_to_ram(mem, section_size, 1, tmpFile);
ebe6ff2b 593 psxCpu->Clear(section_address, section_size / 4);
594 }
ef79bbde
P
595 break;
596 case 3: /* register loading (PC only?) */
597 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
7a8d521f 598 if (fread(&psxRegs.pc, 1, sizeof(psxRegs.pc), tmpFile) != sizeof(psxRegs.pc))
599 goto fail_io;
ef79bbde
P
600 psxRegs.pc = SWAPu32(psxRegs.pc);
601 break;
602 case 0: /* End of file */
603 break;
604 default:
32922925 605 SysPrintf(_("Unknown CPE opcode %02x at position %08lx.\n"), opcode, ftell(tmpFile) - 1);
ef79bbde
P
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:
8f00f96c 616 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
617 SysPrintf(_("(did you forget -cdfile ?)\n"));
ef79bbde
P
618 retval = -1;
619 break;
620 }
621 }
622
623 if (retval != 0) {
624 CdromId[0] = '\0';
625 CdromLabel[0] = '\0';
626 }
627
7a8d521f 628 if (tmpFile)
629 fclose(tmpFile);
ef79bbde 630 return retval;
7a8d521f 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;
ef79bbde
P
638}
639
640// STATES
641
496d88d4 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
f906311d 671static const char PcsxHeader[32] = "STv4 PCSXra " REV;
ef79bbde
P
672
673// Savestate Versioning!
674// If you make changes to the savestate version, please increment the value below.
df656dde 675static const u32 SaveVersion = 0x8b410006;
ef79bbde 676
f906311d 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
6a131b03 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;
f906311d 700 u32 save_counter;
6a131b03 701};
702
f906311d 703#define EX_SCREENPIC_SIZE (128 * 96 * 3)
704
ef79bbde 705int SaveState(const char *file) {
6a131b03 706 struct misc_save_data *misc = (void *)(psxH + 0xf000);
f906311d 707 struct origin_info oi = { 0, };
cfa5a2af 708 GPUFreeze_t *gpufP = NULL;
709 SPUFreezeHdr_t spufH;
710 SPUFreeze_t *spufP = NULL;
f906311d 711 u8 buf[EX_SCREENPIC_SIZE];
cfa5a2af 712 int result = -1;
ef79bbde 713 int Size;
f906311d 714 void *f;
ef79bbde 715
6a131b03 716 assert(!psxRegs.branching);
717 assert(!psxRegs.cpuInRecursion);
718 assert(!misc->magic);
8338889c 719
720 f = SaveFuncs.open(file, "wb");
721 if (f == NULL) return -1;
722
6a131b03 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;
f906311d 732 misc->save_counter = ++save_counter;
6a131b03 733
980f7a58 734 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
52082bc1 735
496d88d4 736 SaveFuncs.write(f, (void *)PcsxHeader, 32);
737 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
738 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
ef79bbde 739
f906311d 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);
ef79bbde
P
756
757 if (Config.HLE)
758 psxBiosFreeze(1);
759
496d88d4 760 SaveFuncs.write(f, psxM, 0x00200000);
761 SaveFuncs.write(f, psxR, 0x00080000);
762 SaveFuncs.write(f, psxH, 0x00010000);
81dbbf4c 763 // only partial save of psxRegisters to maintain savestate compat
764 SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
ef79bbde
P
765
766 // gpu
767 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
cfa5a2af 768 if (gpufP == NULL) goto cleanup;
ef79bbde
P
769 gpufP->ulFreezeVersion = 1;
770 GPU_freeze(1, gpufP);
496d88d4 771 SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
cfa5a2af 772 free(gpufP); gpufP = NULL;
ef79bbde
P
773
774 // spu
cfa5a2af 775 SPU_freeze(2, (SPUFreeze_t *)&spufH, psxRegs.cycle);
776 Size = spufH.Size; SaveFuncs.write(f, &Size, 4);
ef79bbde 777 spufP = (SPUFreeze_t *) malloc(Size);
cfa5a2af 778 if (spufP == NULL) goto cleanup;
650adfd2 779 SPU_freeze(1, spufP, psxRegs.cycle);
496d88d4 780 SaveFuncs.write(f, spufP, Size);
cfa5a2af 781 free(spufP); spufP = NULL;
ef79bbde
P
782
783 sioFreeze(f, 1);
784 cdrFreeze(f, 1);
785 psxHwFreeze(f, 1);
786 psxRcntFreeze(f, 1);
787 mdecFreeze(f, 1);
0b1633d7 788 ndrc_freeze(f, 1);
3faf5c23 789 padFreeze(f, 1);
ef79bbde 790
cfa5a2af 791 result = 0;
792cleanup:
6a131b03 793 memset(misc, 0, sizeof(*misc));
496d88d4 794 SaveFuncs.close(f);
cfa5a2af 795 return result;
ef79bbde
P
796}
797
798int LoadState(const char *file) {
6a131b03 799 struct misc_save_data *misc = (void *)(psxH + 0xf000);
de74f599 800 u32 biosBranchCheckOld = psxRegs.biosBranchCheck;
496d88d4 801 void *f;
cfa5a2af 802 GPUFreeze_t *gpufP = NULL;
803 SPUFreeze_t *spufP = NULL;
8e9d82d0 804 boolean hle, oldhle;
ef79bbde
P
805 int Size;
806 char header[32];
807 u32 version;
cfa5a2af 808 int result = -1;
ef79bbde 809
496d88d4 810 f = SaveFuncs.open(file, "rb");
ef79bbde
P
811 if (f == NULL) return -1;
812
d37ac0de 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);
496d88d4 823 SaveFuncs.read(f, &version, sizeof(u32));
824 SaveFuncs.read(f, &hle, sizeof(boolean));
ef79bbde 825
d37ac0de 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);
cfa5a2af 832 goto cleanup;
ef79bbde 833 }
8e9d82d0 834 oldhle = Config.HLE;
33f56da1 835 Config.HLE = hle;
836
837 if (Config.HLE)
838 psxBiosInit();
ef79bbde 839
f906311d 840 // ex-ScreenPic space
841 SaveFuncs.seek(f, EX_SCREENPIC_SIZE, SEEK_CUR);
842
496d88d4 843 SaveFuncs.read(f, psxM, 0x00200000);
844 SaveFuncs.read(f, psxR, 0x00080000);
845 SaveFuncs.read(f, psxH, 0x00010000);
81dbbf4c 846 SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
847 psxRegs.gteBusyCycle = psxRegs.cycle;
c87406ff 848 psxRegs.branching = 0;
de74f599 849 psxRegs.biosBranchCheck = ~0;
c87406ff 850 psxRegs.cpuInRecursion = 0;
f8896d18 851 psxRegs.gpuIdleAfter = psxRegs.cycle - 1;
852 HW_GPU_STATUS &= SWAP32(~PSXGPU_nBUSY);
6a131b03 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;
f906311d 862 if (misc->save_counter)
863 save_counter = misc->save_counter;
6a131b03 864 }
ef79bbde
P
865
866 if (Config.HLE)
867 psxBiosFreeze(0);
868
869 // gpu
870 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
cfa5a2af 871 if (gpufP == NULL) goto cleanup;
496d88d4 872 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
ef79bbde
P
873 GPU_freeze(0, gpufP);
874 free(gpufP);
f8896d18 875 gpuSyncPluginSR();
ef79bbde
P
876
877 // spu
496d88d4 878 SaveFuncs.read(f, &Size, 4);
ef79bbde 879 spufP = (SPUFreeze_t *)malloc(Size);
cfa5a2af 880 if (spufP == NULL) goto cleanup;
496d88d4 881 SaveFuncs.read(f, spufP, Size);
650adfd2 882 SPU_freeze(0, spufP, psxRegs.cycle);
ef79bbde
P
883 free(spufP);
884
885 sioFreeze(f, 0);
886 cdrFreeze(f, 0);
887 psxHwFreeze(f, 0);
888 psxRcntFreeze(f, 0);
889 mdecFreeze(f, 0);
c6809aec 890
891 if (Config.HLE != oldhle) {
892 // at least ari64 drc compiles differently so hard reset
893 psxCpu->Shutdown();
894 psxCpu->Init();
895 }
0b1633d7 896 ndrc_freeze(f, 0);
3faf5c23 897 padFreeze(f, 0);
ef79bbde 898
9a0a61d2 899 events_restore();
de74f599 900 if (Config.HLE)
b34d6a80 901 psxBiosCheckExe(biosBranchCheckOld, 0x60, 1);
de74f599 902
c8658870 903 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
904
cfa5a2af 905 result = 0;
906cleanup:
6a131b03 907 memset(misc, 0, sizeof(*misc));
496d88d4 908 SaveFuncs.close(f);
cfa5a2af 909 return result;
ef79bbde
P
910}
911
912int CheckState(const char *file) {
496d88d4 913 void *f;
ef79bbde
P
914 char header[32];
915 u32 version;
916 boolean hle;
917
496d88d4 918 f = SaveFuncs.open(file, "rb");
ef79bbde
P
919 if (f == NULL) return -1;
920
d37ac0de 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);
496d88d4 926 SaveFuncs.read(f, &version, sizeof(u32));
927 SaveFuncs.read(f, &hle, sizeof(boolean));
ef79bbde 928
496d88d4 929 SaveFuncs.close(f);
ef79bbde 930
33f56da1 931 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
ef79bbde
P
932 return -1;
933
934 return 0;
935}
936
ef79bbde
P
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
08d9d257 991u16 calcCrc(const u8 *d, int len) {
ef79bbde
P
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}
f906311d 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}