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