cdriso: hack .chd track handling to match .cue
[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
630b122b 24#include <stddef.h>
ef79bbde
P
25#include "misc.h"
26#include "cdrom.h"
27#include "mdec.h"
ddbaf678 28#include "gpu.h"
ef79bbde 29#include "ppf.h"
630b122b 30#include "database.h"
70939d49 31#include "lightrec/plugin.h"
496d88d4 32#include <zlib.h>
ef79bbde
P
33
34char CdromId[10] = "";
35char CdromLabel[33] = "";
36
37// PSX Executable types
38#define PSX_EXE 1
39#define CPE_EXE 2
40#define COFF_EXE 3
41#define INVALID_EXE 4
42
43#define ISODCL(from, to) (to - from + 1)
44
45struct iso_directory_record {
46 char length [ISODCL (1, 1)]; /* 711 */
47 char ext_attr_length [ISODCL (2, 2)]; /* 711 */
48 char extent [ISODCL (3, 10)]; /* 733 */
49 char size [ISODCL (11, 18)]; /* 733 */
50 char date [ISODCL (19, 25)]; /* 7 by 711 */
51 char flags [ISODCL (26, 26)];
52 char file_unit_size [ISODCL (27, 27)]; /* 711 */
53 char interleave [ISODCL (28, 28)]; /* 711 */
54 char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
55 unsigned char name_len [ISODCL (33, 33)]; /* 711 */
56 char name [1];
57};
58
59void mmssdd( char *b, char *p )
60{
61 int m, s, d;
507aaf98 62#if defined(__arm__)
7e443736 63 unsigned char *u = (void *)b;
64 int block = (u[3] << 24) | (u[2] << 16) | (u[1] << 8) | u[0];
507aaf98 65#elif defined(__BIGENDIAN__)
ef79bbde
P
66 int block = (b[0] & 0xff) | ((b[1] & 0xff) << 8) | ((b[2] & 0xff) << 16) | (b[3] << 24);
67#else
68 int block = *((int*)b);
69#endif
70
71 block += 150;
72 m = block / 4500; // minutes
73 block = block - m * 4500; // minutes rest
74 s = block / 75; // seconds
75 d = block - s * 75; // seconds rest
76
77 m = ((m / 10) << 4) | m % 10;
78 s = ((s / 10) << 4) | s % 10;
3e42b115 79 d = ((d / 10) << 4) | d % 10;
ef79bbde
P
80
81 p[0] = m;
82 p[1] = s;
83 p[2] = d;
84}
85
86#define incTime() \
87 time[0] = btoi(time[0]); time[1] = btoi(time[1]); time[2] = btoi(time[2]); \
88 time[2]++; \
89 if(time[2] == 75) { \
90 time[2] = 0; \
91 time[1]++; \
92 if (time[1] == 60) { \
93 time[1] = 0; \
94 time[0]++; \
95 } \
96 } \
97 time[0] = itob(time[0]); time[1] = itob(time[1]); time[2] = itob(time[2]);
98
99#define READTRACK() \
100 if (CDR_readTrack(time) == -1) return -1; \
305c8c93 101 buf = (void *)CDR_getBuffer(); \
102 if (buf == NULL) return -1; \
103 else CheckPPFCache((u8 *)buf, time[0], time[1], time[2]);
ef79bbde
P
104
105#define READDIR(_dir) \
106 READTRACK(); \
107 memcpy(_dir, buf + 12, 2048); \
108 \
109 incTime(); \
110 READTRACK(); \
111 memcpy(_dir + 2048, buf + 12, 2048);
112
305c8c93 113int GetCdromFile(u8 *mdir, u8 *time, char *filename) {
ef79bbde 114 struct iso_directory_record *dir;
5be6eaeb 115 int retval = -1;
305c8c93 116 u8 ddir[4096];
ef79bbde
P
117 u8 *buf;
118 int i;
119
120 // only try to scan if a filename is given
121 if (!strlen(filename)) return -1;
122
123 i = 0;
124 while (i < 4096) {
125 dir = (struct iso_directory_record*) &mdir[i];
126 if (dir->length[0] == 0) {
127 return -1;
128 }
1c2c2bfb 129 i += (u8)dir->length[0];
ef79bbde
P
130
131 if (dir->flags[0] & 0x2) { // it's a dir
132 if (!strnicmp((char *)&dir->name[0], filename, dir->name_len[0])) {
133 if (filename[dir->name_len[0]] != '\\') continue;
134
135 filename += dir->name_len[0] + 1;
136
137 mmssdd(dir->extent, (char *)time);
138 READDIR(ddir);
139 i = 0;
140 mdir = ddir;
141 }
142 } else {
143 if (!strnicmp((char *)&dir->name[0], filename, strlen(filename))) {
144 mmssdd(dir->extent, (char *)time);
5be6eaeb 145 retval = 0;
ef79bbde
P
146 break;
147 }
148 }
149 }
5be6eaeb 150 return retval;
ef79bbde
P
151}
152
f3a78e7e 153static const unsigned int gpu_ctl_def[] = {
154 0x00000000, 0x01000000, 0x03000000, 0x04000000,
155 0x05000800, 0x06c60260, 0x0703fc10, 0x08000027,
156};
157
158static const unsigned int gpu_data_def[] = {
159 0xe100360b, 0xe2000000, 0xe3000800, 0xe4077e7f,
160 0xe5001000, 0xe6000000,
161 0x02000000, 0x00000000, 0x01ff03ff,
162};
163
164static void fake_bios_gpu_setup(void)
165{
166 int i;
167
168 for (i = 0; i < sizeof(gpu_ctl_def) / sizeof(gpu_ctl_def[0]); i++)
169 GPU_writeStatus(gpu_ctl_def[i]);
170
171 for (i = 0; i < sizeof(gpu_data_def) / sizeof(gpu_data_def[0]); i++)
172 GPU_writeData(gpu_data_def[i]);
173}
174
ef79bbde
P
175int LoadCdrom() {
176 EXE_HEADER tmpHead;
177 struct iso_directory_record *dir;
178 u8 time[4], *buf;
179 u8 mdir[4096];
305c8c93 180 char exename[256];
ef79bbde 181
f3a78e7e 182 // not the best place to do it, but since BIOS boot logo killer
183 // is just below, do it here
184 fake_bios_gpu_setup();
185
f422f444 186 if (!Config.HLE && !Config.SlowBoot) {
f3a78e7e 187 // skip BIOS logos
ef79bbde
P
188 psxRegs.pc = psxRegs.GPR.n.ra;
189 return 0;
190 }
191
192 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
193
194 READTRACK();
195
196 // skip head and sub, and go to the root directory record
3e42b115 197 dir = (struct iso_directory_record*) &buf[12+156];
ef79bbde
P
198
199 mmssdd(dir->extent, (char*)time);
200
201 READDIR(mdir);
202
203 // Load SYSTEM.CNF and scan for the main executable
204 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
205 // if SYSTEM.CNF is missing, start an existing PSX.EXE
206 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
207
208 READTRACK();
209 }
210 else {
211 // read the SYSTEM.CNF
212 READTRACK();
213
9a9bcd78 214 sscanf((char *)buf + 12, "BOOT = cdrom:\\%255s", exename);
ef79bbde 215 if (GetCdromFile(mdir, time, exename) == -1) {
9a9bcd78 216 sscanf((char *)buf + 12, "BOOT = cdrom:%255s", exename);
ef79bbde 217 if (GetCdromFile(mdir, time, exename) == -1) {
305c8c93 218 char *ptr = strstr((char *)buf + 12, "cdrom:");
ef79bbde
P
219 if (ptr != NULL) {
220 ptr += 6;
221 while (*ptr == '\\' || *ptr == '/') ptr++;
222 strncpy(exename, ptr, 255);
223 exename[255] = '\0';
224 ptr = exename;
225 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
226 *ptr = '\0';
227 if (GetCdromFile(mdir, time, exename) == -1)
228 return -1;
229 } else
230 return -1;
231 }
232 }
233
234 // Read the EXE-Header
235 READTRACK();
236 }
237
238 memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
239
240 psxRegs.pc = SWAP32(tmpHead.pc0);
241 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
3e42b115 242 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
ef79bbde
P
243 if (psxRegs.GPR.n.sp == 0) psxRegs.GPR.n.sp = 0x801fff00;
244
245 tmpHead.t_size = SWAP32(tmpHead.t_size);
246 tmpHead.t_addr = SWAP32(tmpHead.t_addr);
247
ebe6ff2b 248 psxCpu->Clear(tmpHead.t_addr, tmpHead.t_size / 4);
952943a7 249 psxCpu->Reset();
ebe6ff2b 250
ef79bbde 251 // Read the rest of the main executable
ebe6ff2b 252 while (tmpHead.t_size & ~2047) {
ef79bbde
P
253 void *ptr = (void *)PSXM(tmpHead.t_addr);
254
255 incTime();
256 READTRACK();
257
b012a437 258 if (ptr != INVALID_PTR) memcpy(ptr, buf+12, 2048);
ef79bbde
P
259
260 tmpHead.t_size -= 2048;
261 tmpHead.t_addr += 2048;
262 }
263
264 return 0;
265}
266
267int LoadCdromFile(const char *filename, EXE_HEADER *head) {
268 struct iso_directory_record *dir;
269 u8 time[4],*buf;
305c8c93 270 u8 mdir[4096];
271 char exename[256];
ef79bbde 272 u32 size, addr;
305c8c93 273 void *mem;
ef79bbde 274
9a9bcd78 275 sscanf(filename, "cdrom:\\%255s", exename);
ef79bbde
P
276
277 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
278
279 READTRACK();
280
281 // skip head and sub, and go to the root directory record
3e42b115 282 dir = (struct iso_directory_record *)&buf[12 + 156];
ef79bbde
P
283
284 mmssdd(dir->extent, (char*)time);
285
286 READDIR(mdir);
287
288 if (GetCdromFile(mdir, time, exename) == -1) return -1;
289
290 READTRACK();
291
292 memcpy(head, buf + 12, sizeof(EXE_HEADER));
293 size = head->t_size;
294 addr = head->t_addr;
295
ebe6ff2b 296 psxCpu->Clear(addr, size / 4);
952943a7 297 psxCpu->Reset();
ebe6ff2b 298
299 while (size & ~2047) {
ef79bbde
P
300 incTime();
301 READTRACK();
302
305c8c93 303 mem = PSXM(addr);
b012a437 304 if (mem != INVALID_PTR)
305c8c93 305 memcpy(mem, buf + 12, 2048);
ef79bbde
P
306
307 size -= 2048;
308 addr += 2048;
309 }
310
311 return 0;
312}
313
314int CheckCdrom() {
315 struct iso_directory_record *dir;
305c8c93 316 unsigned char time[4];
317 char *buf;
ef79bbde
P
318 unsigned char mdir[4096];
319 char exename[256];
e06ee7f4 320 int i, len, c;
ef79bbde
P
321
322 FreePPFCache();
323
324 time[0] = itob(0);
325 time[1] = itob(2);
326 time[2] = itob(0x10);
327
328 READTRACK();
329
e06ee7f4 330 memset(CdromLabel, 0, sizeof(CdromLabel));
331 memset(CdromId, 0, sizeof(CdromId));
332 memset(exename, 0, sizeof(exename));
ef79bbde
P
333
334 strncpy(CdromLabel, buf + 52, 32);
335
336 // skip head and sub, and go to the root directory record
3e42b115 337 dir = (struct iso_directory_record *)&buf[12 + 156];
ef79bbde
P
338
339 mmssdd(dir->extent, (char *)time);
340
341 READDIR(mdir);
342
343 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
344 READTRACK();
345
9a9bcd78 346 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
ef79bbde 347 if (GetCdromFile(mdir, time, exename) == -1) {
9a9bcd78 348 sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
ef79bbde
P
349 if (GetCdromFile(mdir, time, exename) == -1) {
350 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
351 if (ptr != NULL) {
352 ptr += 6;
353 while (*ptr == '\\' || *ptr == '/') ptr++;
354 strncpy(exename, ptr, 255);
355 exename[255] = '\0';
356 ptr = exename;
357 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
358 *ptr = '\0';
359 if (GetCdromFile(mdir, time, exename) == -1)
360 return -1; // main executable not found
361 } else
362 return -1;
363 }
364 }
1893f408 365 /* Workaround for Wild Arms EU/US which has non-standard string causing incorrect region detection */
366 if (exename[0] == 'E' && exename[1] == 'X' && exename[2] == 'E' && exename[3] == '\\') {
367 size_t offset = 4;
368 size_t i, len = strlen(exename) - offset;
369 for (i = 0; i < len; i++)
370 exename[i] = exename[i + offset];
371 exename[i] = '\0';
372 }
ef79bbde
P
373 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
374 strcpy(exename, "PSX.EXE;1");
375 strcpy(CdromId, "SLUS99999");
376 } else
377 return -1; // SYSTEM.CNF and PSX.EXE not found
378
379 if (CdromId[0] == '\0') {
e06ee7f4 380 len = strlen(exename);
381 c = 0;
382 for (i = 0; i < len; ++i) {
383 if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
384 break;
385 if (isalnum(exename[i]))
386 CdromId[c++] = exename[i];
ef79bbde
P
387 }
388 }
389
0e2e3f49 390 if (CdromId[0] == '\0')
391 strcpy(CdromId, "SLUS99999");
392
ef79bbde 393 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
630b122b 394 if (
395 /* Make sure Wild Arms SCUS-94608 is not detected as a PAL game. */
396 ((CdromId[0] == 's' || CdromId[0] == 'S') && (CdromId[2] == 'e' || CdromId[2] == 'E')) ||
397 !strncmp(CdromId, "DTLS3035", 8) ||
398 !strncmp(CdromId, "PBPX95001", 9) || // according to redump.org, these PAL
399 !strncmp(CdromId, "PBPX95007", 9) || // discs have a non-standard ID;
400 !strncmp(CdromId, "PBPX95008", 9)) // add more serials if they are discovered.
ef79bbde
P
401 Config.PsxType = PSX_TYPE_PAL; // pal
402 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
403 }
404
405 if (CdromLabel[0] == ' ') {
630b122b 406 strncpy(CdromLabel, CdromId, 9);
ef79bbde
P
407 }
408 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
409 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
e06ee7f4 410 SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
630b122b 411
412 Apply_Hacks_Cdrom();
ef79bbde
P
413
414 BuildPPFCache();
415
416 return 0;
417}
418
419static int PSXGetFileType(FILE *f) {
420 unsigned long current;
421 u8 mybuf[2048];
422 EXE_HEADER *exe_hdr;
423 FILHDR *coff_hdr;
424
425 current = ftell(f);
426 fseek(f, 0L, SEEK_SET);
9361a5aa
EC
427 if (fread(&mybuf, sizeof(mybuf), 1, f) != sizeof(mybuf))
428 goto io_fail;
429
ef79bbde
P
430 fseek(f, current, SEEK_SET);
431
432 exe_hdr = (EXE_HEADER *)mybuf;
433 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
434 return PSX_EXE;
435
436 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
437 return CPE_EXE;
438
439 coff_hdr = (FILHDR *)mybuf;
440 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
441 return COFF_EXE;
442
443 return INVALID_EXE;
9361a5aa
EC
444
445io_fail:
446#ifndef NDEBUG
447 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
448#endif
449 return INVALID_EXE;
ef79bbde
P
450}
451
96bef96f 452// temporary pandora workaround..
453// FIXME: remove
454size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
455{
456 void *tmp;
457 size_t ret = 0;
3e42b115 458
96bef96f 459 tmp = malloc(size * nmemb);
460 if (tmp) {
461 ret = fread(tmp, size, nmemb, stream);
462 memcpy(ptr, tmp, size * nmemb);
463 free(tmp);
464 }
465 return ret;
466}
467
ef79bbde
P
468int Load(const char *ExePath) {
469 FILE *tmpFile;
470 EXE_HEADER tmpHead;
471 int type;
472 int retval = 0;
473 u8 opcode;
474 u32 section_address, section_size;
ebe6ff2b 475 void *mem;
ef79bbde 476
3e42b115 477 strcpy(CdromId, "SLUS99999");
478 strcpy(CdromLabel, "SLUS_999.99");
ef79bbde
P
479
480 tmpFile = fopen(ExePath, "rb");
481 if (tmpFile == NULL) {
482 SysPrintf(_("Error opening file: %s.\n"), ExePath);
483 retval = -1;
484 } else {
485 type = PSXGetFileType(tmpFile);
486 switch (type) {
487 case PSX_EXE:
9361a5aa
EC
488 if (fread(&tmpHead, sizeof(EXE_HEADER), 1, tmpFile) != sizeof(EXE_HEADER))
489 goto fail_io;
ebe6ff2b 490 section_address = SWAP32(tmpHead.t_addr);
491 section_size = SWAP32(tmpHead.t_size);
492 mem = PSXM(section_address);
b012a437 493 if (mem != INVALID_PTR) {
3e42b115 494 fseek(tmpFile, 0x800, SEEK_SET);
96bef96f 495 fread_to_ram(mem, section_size, 1, tmpFile);
ebe6ff2b 496 psxCpu->Clear(section_address, section_size / 4);
497 }
ef79bbde
P
498 psxRegs.pc = SWAP32(tmpHead.pc0);
499 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
3e42b115 500 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
ef79bbde
P
501 if (psxRegs.GPR.n.sp == 0)
502 psxRegs.GPR.n.sp = 0x801fff00;
503 retval = 0;
504 break;
505 case CPE_EXE:
506 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
507 do {
9361a5aa
EC
508 if (fread(&opcode, sizeof(opcode), 1, tmpFile) != sizeof(opcode))
509 goto fail_io;
ef79bbde
P
510 switch (opcode) {
511 case 1: /* Section loading */
9361a5aa
EC
512 if (fread(&section_address, sizeof(section_address), 1, tmpFile) != sizeof(section_address))
513 goto fail_io;
514 if (fread(&section_size, sizeof(section_size), 1, tmpFile) != sizeof(section_size))
515 goto fail_io;
ef79bbde
P
516 section_address = SWAPu32(section_address);
517 section_size = SWAPu32(section_size);
518#ifdef EMU_LOG
519 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
520#endif
ebe6ff2b 521 mem = PSXM(section_address);
b012a437 522 if (mem != INVALID_PTR) {
96bef96f 523 fread_to_ram(mem, section_size, 1, tmpFile);
ebe6ff2b 524 psxCpu->Clear(section_address, section_size / 4);
525 }
ef79bbde
P
526 break;
527 case 3: /* register loading (PC only?) */
528 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
9361a5aa
EC
529 if (fread(&psxRegs.pc, sizeof(psxRegs.pc), 1, tmpFile) != sizeof(psxRegs.pc))
530 goto fail_io;
ef79bbde
P
531 psxRegs.pc = SWAPu32(psxRegs.pc);
532 break;
533 case 0: /* End of file */
534 break;
535 default:
536 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
537 retval = -1;
538 break;
539 }
540 } while (opcode != 0 && retval == 0);
541 break;
542 case COFF_EXE:
543 SysPrintf(_("COFF files not supported.\n"));
544 retval = -1;
545 break;
546 case INVALID_EXE:
8f00f96c 547 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
548 SysPrintf(_("(did you forget -cdfile ?)\n"));
ef79bbde
P
549 retval = -1;
550 break;
551 }
552 }
553
554 if (retval != 0) {
555 CdromId[0] = '\0';
556 CdromLabel[0] = '\0';
557 }
558
9361a5aa 559 fclose(tmpFile);
ef79bbde 560 return retval;
9361a5aa
EC
561
562fail_io:
563#ifndef NDEBUG
564 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
565#endif
566 fclose(tmpFile);
567 return -1;
ef79bbde
P
568}
569
570// STATES
571
496d88d4 572static void *zlib_open(const char *name, const char *mode)
573{
574 return gzopen(name, mode);
575}
576
577static int zlib_read(void *file, void *buf, u32 len)
578{
579 return gzread(file, buf, len);
580}
581
582static int zlib_write(void *file, const void *buf, u32 len)
583{
584 return gzwrite(file, buf, len);
585}
586
587static long zlib_seek(void *file, long offs, int whence)
588{
589 return gzseek(file, offs, whence);
590}
591
592static void zlib_close(void *file)
593{
594 gzclose(file);
595}
596
597struct PcsxSaveFuncs SaveFuncs = {
598 zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
599};
600
202b29d4 601static const char PcsxHeader[32] = "STv4 PCSX v" PCSX_VERSION;
ef79bbde
P
602
603// Savestate Versioning!
604// If you make changes to the savestate version, please increment the value below.
df656dde 605static const u32 SaveVersion = 0x8b410006;
ef79bbde
P
606
607int SaveState(const char *file) {
496d88d4 608 void *f;
ef79bbde
P
609 GPUFreeze_t *gpufP;
610 SPUFreeze_t *spufP;
611 int Size;
612 unsigned char *pMem;
613
496d88d4 614 f = SaveFuncs.open(file, "wb");
ef79bbde
P
615 if (f == NULL) return -1;
616
03f55e6b 617 new_dyna_before_save();
52082bc1 618
02dbc869 619 if (drc_is_lightrec() && Config.Cpu != CPU_INTERPRETER)
6962f770
PC
620 lightrec_plugin_prepare_save_state();
621
496d88d4 622 SaveFuncs.write(f, (void *)PcsxHeader, 32);
623 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
624 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
ef79bbde
P
625
626 pMem = (unsigned char *)malloc(128 * 96 * 3);
627 if (pMem == NULL) return -1;
628 GPU_getScreenPic(pMem);
496d88d4 629 SaveFuncs.write(f, pMem, 128 * 96 * 3);
ef79bbde
P
630 free(pMem);
631
632 if (Config.HLE)
633 psxBiosFreeze(1);
634
496d88d4 635 SaveFuncs.write(f, psxM, 0x00200000);
636 SaveFuncs.write(f, psxR, 0x00080000);
637 SaveFuncs.write(f, psxH, 0x00010000);
630b122b 638 // only partial save of psxRegisters to maintain savestate compat
639 SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
ef79bbde
P
640
641 // gpu
642 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
643 gpufP->ulFreezeVersion = 1;
644 GPU_freeze(1, gpufP);
496d88d4 645 SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
ef79bbde
P
646 free(gpufP);
647
648 // spu
649 spufP = (SPUFreeze_t *) malloc(16);
650adfd2 650 SPU_freeze(2, spufP, psxRegs.cycle);
496d88d4 651 Size = spufP->Size; SaveFuncs.write(f, &Size, 4);
ef79bbde
P
652 free(spufP);
653 spufP = (SPUFreeze_t *) malloc(Size);
650adfd2 654 SPU_freeze(1, spufP, psxRegs.cycle);
496d88d4 655 SaveFuncs.write(f, spufP, Size);
ef79bbde
P
656 free(spufP);
657
658 sioFreeze(f, 1);
659 cdrFreeze(f, 1);
660 psxHwFreeze(f, 1);
661 psxRcntFreeze(f, 1);
662 mdecFreeze(f, 1);
03f55e6b 663 new_dyna_freeze(f, 1);
ef79bbde 664
496d88d4 665 SaveFuncs.close(f);
ef79bbde 666
5b8c000f 667 new_dyna_after_save();
668
ef79bbde
P
669 return 0;
670}
671
672int LoadState(const char *file) {
496d88d4 673 void *f;
ef79bbde
P
674 GPUFreeze_t *gpufP;
675 SPUFreeze_t *spufP;
676 int Size;
677 char header[32];
678 u32 version;
679 boolean hle;
680
496d88d4 681 f = SaveFuncs.open(file, "rb");
ef79bbde
P
682 if (f == NULL) return -1;
683
496d88d4 684 SaveFuncs.read(f, header, sizeof(header));
685 SaveFuncs.read(f, &version, sizeof(u32));
686 SaveFuncs.read(f, &hle, sizeof(boolean));
ef79bbde 687
33f56da1 688 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
496d88d4 689 SaveFuncs.close(f);
ef79bbde
P
690 return -1;
691 }
33f56da1 692 Config.HLE = hle;
693
694 if (Config.HLE)
695 psxBiosInit();
ef79bbde 696
6962f770
PC
697 if (!drc_is_lightrec() || Config.Cpu == CPU_INTERPRETER)
698 psxCpu->Reset();
496d88d4 699 SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
ef79bbde 700
496d88d4 701 SaveFuncs.read(f, psxM, 0x00200000);
702 SaveFuncs.read(f, psxR, 0x00080000);
703 SaveFuncs.read(f, psxH, 0x00010000);
630b122b 704 SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
705 psxRegs.gteBusyCycle = psxRegs.cycle;
ef79bbde 706
6962f770
PC
707 if (drc_is_lightrec() && Config.Cpu != CPU_INTERPRETER)
708 lightrec_plugin_prepare_load_state();
709
ef79bbde
P
710 if (Config.HLE)
711 psxBiosFreeze(0);
712
713 // gpu
714 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
496d88d4 715 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
ef79bbde
P
716 GPU_freeze(0, gpufP);
717 free(gpufP);
ddbaf678 718 if (HW_GPU_STATUS == 0)
719 HW_GPU_STATUS = GPU_readStatus();
ef79bbde
P
720
721 // spu
496d88d4 722 SaveFuncs.read(f, &Size, 4);
ef79bbde 723 spufP = (SPUFreeze_t *)malloc(Size);
496d88d4 724 SaveFuncs.read(f, spufP, Size);
650adfd2 725 SPU_freeze(0, spufP, psxRegs.cycle);
ef79bbde
P
726 free(spufP);
727
728 sioFreeze(f, 0);
729 cdrFreeze(f, 0);
730 psxHwFreeze(f, 0);
731 psxRcntFreeze(f, 0);
732 mdecFreeze(f, 0);
03f55e6b 733 new_dyna_freeze(f, 0);
ef79bbde 734
496d88d4 735 SaveFuncs.close(f);
ef79bbde
P
736
737 return 0;
738}
739
740int CheckState(const char *file) {
496d88d4 741 void *f;
ef79bbde
P
742 char header[32];
743 u32 version;
744 boolean hle;
745
496d88d4 746 f = SaveFuncs.open(file, "rb");
ef79bbde
P
747 if (f == NULL) return -1;
748
496d88d4 749 SaveFuncs.read(f, header, sizeof(header));
750 SaveFuncs.read(f, &version, sizeof(u32));
751 SaveFuncs.read(f, &hle, sizeof(boolean));
ef79bbde 752
496d88d4 753 SaveFuncs.close(f);
ef79bbde 754
33f56da1 755 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
ef79bbde
P
756 return -1;
757
758 return 0;
759}
760
761// NET Function Helpers
762
763int SendPcsxInfo() {
764 if (NET_recvData == NULL || NET_sendData == NULL)
765 return 0;
766
767 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
768 NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
769 NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
770 NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
771 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
772 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
773
774 return 0;
775}
776
777int RecvPcsxInfo() {
778 int tmp;
779
780 if (NET_recvData == NULL || NET_sendData == NULL)
781 return 0;
782
783 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
784 NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
785 NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
786 NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
787 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
788
789 SysUpdate();
790
791 tmp = Config.Cpu;
792 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
793 if (tmp != Config.Cpu) {
794 psxCpu->Shutdown();
630b122b 795#ifndef DRC_DISABLE
ef79bbde
P
796 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
797 else psxCpu = &psxRec;
798#else
799 psxCpu = &psxInt;
800#endif
801 if (psxCpu->Init() == -1) {
802 SysClose(); return -1;
803 }
804 psxCpu->Reset();
805 }
806
807 return 0;
808}
809
810// remove the leading and trailing spaces in a string
811void trim(char *str) {
812 int pos = 0;
813 char *dest = str;
814
815 // skip leading blanks
816 while (str[pos] <= ' ' && str[pos] > 0)
817 pos++;
818
819 while (str[pos]) {
820 *(dest++) = str[pos];
821 pos++;
822 }
823
824 *(dest--) = '\0'; // store the null
825
826 // remove trailing blanks
827 while (dest >= str && *dest <= ' ' && *dest > 0)
828 *(dest--) = '\0';
829}
830
831// lookup table for crc calculation
832static unsigned short crctab[256] = {
833 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
834 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
835 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
836 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
837 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
838 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
839 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
840 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
841 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
842 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
843 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
844 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
845 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
846 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
847 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
848 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
849 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
850 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
851 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
852 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
853 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
854 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
855 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
856 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
857 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
858 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
859 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
860 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
861 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
862};
863
864u16 calcCrc(u8 *d, int len) {
865 u16 crc = 0;
866 int i;
867
868 for (i = 0; i < len; i++) {
869 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);
870 }
871
872 return ~crc;
873}