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