drc: handle regs-not-in-psxRegs case better
[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>
ef79bbde
P
25#include "misc.h"
26#include "cdrom.h"
27#include "mdec.h"
ddbaf678 28#include "gpu.h"
ef79bbde 29#include "ppf.h"
eedfe806 30#include "database.h"
496d88d4 31#include <zlib.h>
ef79bbde
P
32
33char CdromId[10] = "";
34char CdromLabel[33] = "";
35
36// PSX Executable types
37#define PSX_EXE 1
38#define CPE_EXE 2
39#define COFF_EXE 3
40#define INVALID_EXE 4
41
42#define ISODCL(from, to) (to - from + 1)
43
44struct iso_directory_record {
45 char length [ISODCL (1, 1)]; /* 711 */
46 char ext_attr_length [ISODCL (2, 2)]; /* 711 */
47 char extent [ISODCL (3, 10)]; /* 733 */
48 char size [ISODCL (11, 18)]; /* 733 */
49 char date [ISODCL (19, 25)]; /* 7 by 711 */
50 char flags [ISODCL (26, 26)];
51 char file_unit_size [ISODCL (27, 27)]; /* 711 */
52 char interleave [ISODCL (28, 28)]; /* 711 */
53 char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
54 unsigned char name_len [ISODCL (33, 33)]; /* 711 */
55 char name [1];
56};
57
1c654475 58static void mmssdd( char *b, char *p )
ef79bbde
P
59{
60 int m, s, d;
1c654475 61 unsigned char *ub = (void *)b;
62 int block = (ub[3] << 24) | (ub[2] << 16) | (ub[1] << 8) | ub[0];
ef79bbde
P
63
64 block += 150;
65 m = block / 4500; // minutes
66 block = block - m * 4500; // minutes rest
67 s = block / 75; // seconds
68 d = block - s * 75; // seconds rest
69
70 m = ((m / 10) << 4) | m % 10;
71 s = ((s / 10) << 4) | s % 10;
7a8d521f 72 d = ((d / 10) << 4) | d % 10;
ef79bbde
P
73
74 p[0] = m;
75 p[1] = s;
76 p[2] = d;
77}
78
79#define incTime() \
80 time[0] = btoi(time[0]); time[1] = btoi(time[1]); time[2] = btoi(time[2]); \
81 time[2]++; \
82 if(time[2] == 75) { \
83 time[2] = 0; \
84 time[1]++; \
85 if (time[1] == 60) { \
86 time[1] = 0; \
87 time[0]++; \
88 } \
89 } \
90 time[0] = itob(time[0]); time[1] = itob(time[1]); time[2] = itob(time[2]);
91
92#define READTRACK() \
415213c9 93 if (!CDR_readTrack(time)) return -1; \
305c8c93 94 buf = (void *)CDR_getBuffer(); \
95 if (buf == NULL) return -1; \
96 else CheckPPFCache((u8 *)buf, time[0], time[1], time[2]);
ef79bbde
P
97
98#define READDIR(_dir) \
99 READTRACK(); \
100 memcpy(_dir, buf + 12, 2048); \
101 \
102 incTime(); \
103 READTRACK(); \
104 memcpy(_dir + 2048, buf + 12, 2048);
105
305c8c93 106int GetCdromFile(u8 *mdir, u8 *time, char *filename) {
ef79bbde 107 struct iso_directory_record *dir;
5be6eaeb 108 int retval = -1;
305c8c93 109 u8 ddir[4096];
ef79bbde
P
110 u8 *buf;
111 int i;
112
113 // only try to scan if a filename is given
114 if (!strlen(filename)) return -1;
115
116 i = 0;
117 while (i < 4096) {
118 dir = (struct iso_directory_record*) &mdir[i];
119 if (dir->length[0] == 0) {
120 return -1;
121 }
1c2c2bfb 122 i += (u8)dir->length[0];
ef79bbde
P
123
124 if (dir->flags[0] & 0x2) { // it's a dir
125 if (!strnicmp((char *)&dir->name[0], filename, dir->name_len[0])) {
126 if (filename[dir->name_len[0]] != '\\') continue;
127
128 filename += dir->name_len[0] + 1;
129
130 mmssdd(dir->extent, (char *)time);
131 READDIR(ddir);
132 i = 0;
133 mdir = ddir;
134 }
135 } else {
136 if (!strnicmp((char *)&dir->name[0], filename, strlen(filename))) {
137 mmssdd(dir->extent, (char *)time);
5be6eaeb 138 retval = 0;
ef79bbde
P
139 break;
140 }
141 }
142 }
5be6eaeb 143 return retval;
ef79bbde
P
144}
145
f3a78e7e 146static const unsigned int gpu_ctl_def[] = {
147 0x00000000, 0x01000000, 0x03000000, 0x04000000,
148 0x05000800, 0x06c60260, 0x0703fc10, 0x08000027,
149};
150
151static const unsigned int gpu_data_def[] = {
152 0xe100360b, 0xe2000000, 0xe3000800, 0xe4077e7f,
153 0xe5001000, 0xe6000000,
154 0x02000000, 0x00000000, 0x01ff03ff,
155};
156
157static void fake_bios_gpu_setup(void)
158{
159 int i;
160
161 for (i = 0; i < sizeof(gpu_ctl_def) / sizeof(gpu_ctl_def[0]); i++)
162 GPU_writeStatus(gpu_ctl_def[i]);
163
164 for (i = 0; i < sizeof(gpu_data_def) / sizeof(gpu_data_def[0]); i++)
165 GPU_writeData(gpu_data_def[i]);
166}
167
6d75addf 168static void SetBootRegs(u32 pc, u32 gp, u32 sp)
169{
170 //printf("%s %08x %08x %08x\n", __func__, pc, gp, sp);
171 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
172
173 psxRegs.pc = pc;
174 psxRegs.GPR.n.gp = gp;
175 psxRegs.GPR.n.sp = sp ? sp : 0x801fff00;
176
177 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
178}
179
ef79bbde
P
180int LoadCdrom() {
181 EXE_HEADER tmpHead;
182 struct iso_directory_record *dir;
183 u8 time[4], *buf;
184 u8 mdir[4096];
305c8c93 185 char exename[256];
ef79bbde 186
f3a78e7e 187 // not the best place to do it, but since BIOS boot logo killer
188 // is just below, do it here
189 fake_bios_gpu_setup();
190
7a8d521f 191 if (!Config.HLE && !Config.SlowBoot) {
f3a78e7e 192 // skip BIOS logos
6d75addf 193 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
ef79bbde
P
194 psxRegs.pc = psxRegs.GPR.n.ra;
195 return 0;
196 }
197
198 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
199
200 READTRACK();
201
202 // skip head and sub, and go to the root directory record
7a8d521f 203 dir = (struct iso_directory_record*) &buf[12+156];
ef79bbde
P
204
205 mmssdd(dir->extent, (char*)time);
206
207 READDIR(mdir);
208
209 // Load SYSTEM.CNF and scan for the main executable
210 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
211 // if SYSTEM.CNF is missing, start an existing PSX.EXE
212 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
213
214 READTRACK();
215 }
216 else {
217 // read the SYSTEM.CNF
218 READTRACK();
219
9a9bcd78 220 sscanf((char *)buf + 12, "BOOT = cdrom:\\%255s", exename);
ef79bbde 221 if (GetCdromFile(mdir, time, exename) == -1) {
9a9bcd78 222 sscanf((char *)buf + 12, "BOOT = cdrom:%255s", exename);
ef79bbde 223 if (GetCdromFile(mdir, time, exename) == -1) {
305c8c93 224 char *ptr = strstr((char *)buf + 12, "cdrom:");
ef79bbde
P
225 if (ptr != NULL) {
226 ptr += 6;
227 while (*ptr == '\\' || *ptr == '/') ptr++;
228 strncpy(exename, ptr, 255);
229 exename[255] = '\0';
230 ptr = exename;
231 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
232 *ptr = '\0';
233 if (GetCdromFile(mdir, time, exename) == -1)
234 return -1;
235 } else
236 return -1;
237 }
238 }
239
240 // Read the EXE-Header
241 READTRACK();
242 }
243
244 memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
245
6d75addf 246 SetBootRegs(SWAP32(tmpHead.pc0), SWAP32(tmpHead.gp0), SWAP32(tmpHead.s_addr));
ef79bbde
P
247
248 tmpHead.t_size = SWAP32(tmpHead.t_size);
249 tmpHead.t_addr = SWAP32(tmpHead.t_addr);
250
ebe6ff2b 251 psxCpu->Clear(tmpHead.t_addr, tmpHead.t_size / 4);
7bbabe80 252 psxCpu->Reset();
ebe6ff2b 253
ef79bbde 254 // Read the rest of the main executable
ebe6ff2b 255 while (tmpHead.t_size & ~2047) {
ef79bbde
P
256 void *ptr = (void *)PSXM(tmpHead.t_addr);
257
258 incTime();
259 READTRACK();
260
7a8d521f 261 if (ptr != INVALID_PTR) memcpy(ptr, buf+12, 2048);
ef79bbde
P
262
263 tmpHead.t_size -= 2048;
264 tmpHead.t_addr += 2048;
265 }
266
267 return 0;
268}
269
270int LoadCdromFile(const char *filename, EXE_HEADER *head) {
271 struct iso_directory_record *dir;
272 u8 time[4],*buf;
305c8c93 273 u8 mdir[4096];
274 char exename[256];
ef79bbde 275 u32 size, addr;
305c8c93 276 void *mem;
ef79bbde 277
9a9bcd78 278 sscanf(filename, "cdrom:\\%255s", exename);
ef79bbde
P
279
280 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
281
282 READTRACK();
283
284 // skip head and sub, and go to the root directory record
7a8d521f 285 dir = (struct iso_directory_record *)&buf[12 + 156];
ef79bbde
P
286
287 mmssdd(dir->extent, (char*)time);
288
289 READDIR(mdir);
290
291 if (GetCdromFile(mdir, time, exename) == -1) return -1;
292
293 READTRACK();
294
295 memcpy(head, buf + 12, sizeof(EXE_HEADER));
296 size = head->t_size;
297 addr = head->t_addr;
298
ebe6ff2b 299 psxCpu->Clear(addr, size / 4);
7bbabe80 300 psxCpu->Reset();
ebe6ff2b 301
302 while (size & ~2047) {
ef79bbde
P
303 incTime();
304 READTRACK();
305
305c8c93 306 mem = PSXM(addr);
7a8d521f 307 if (mem != INVALID_PTR)
305c8c93 308 memcpy(mem, buf + 12, 2048);
ef79bbde
P
309
310 size -= 2048;
311 addr += 2048;
312 }
313
314 return 0;
315}
316
317int CheckCdrom() {
318 struct iso_directory_record *dir;
305c8c93 319 unsigned char time[4];
320 char *buf;
ef79bbde
P
321 unsigned char mdir[4096];
322 char exename[256];
e06ee7f4 323 int i, len, c;
ef79bbde
P
324
325 FreePPFCache();
326
327 time[0] = itob(0);
328 time[1] = itob(2);
329 time[2] = itob(0x10);
330
331 READTRACK();
332
e06ee7f4 333 memset(CdromLabel, 0, sizeof(CdromLabel));
334 memset(CdromId, 0, sizeof(CdromId));
335 memset(exename, 0, sizeof(exename));
ef79bbde
P
336
337 strncpy(CdromLabel, buf + 52, 32);
338
339 // skip head and sub, and go to the root directory record
7a8d521f 340 dir = (struct iso_directory_record *)&buf[12 + 156];
ef79bbde
P
341
342 mmssdd(dir->extent, (char *)time);
343
344 READDIR(mdir);
345
346 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
347 READTRACK();
348
9a9bcd78 349 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
ef79bbde 350 if (GetCdromFile(mdir, time, exename) == -1) {
9a9bcd78 351 sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
ef79bbde
P
352 if (GetCdromFile(mdir, time, exename) == -1) {
353 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
354 if (ptr != NULL) {
355 ptr += 6;
356 while (*ptr == '\\' || *ptr == '/') ptr++;
357 strncpy(exename, ptr, 255);
358 exename[255] = '\0';
359 ptr = exename;
360 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
361 *ptr = '\0';
362 if (GetCdromFile(mdir, time, exename) == -1)
363 return -1; // main executable not found
364 } else
365 return -1;
366 }
367 }
7a8d521f 368 /* Workaround for Wild Arms EU/US which has non-standard string causing incorrect region detection */
369 if (exename[0] == 'E' && exename[1] == 'X' && exename[2] == 'E' && exename[3] == '\\') {
370 size_t offset = 4;
371 size_t i, len = strlen(exename) - offset;
372 for (i = 0; i < len; i++)
373 exename[i] = exename[i + offset];
374 exename[i] = '\0';
375 }
ef79bbde
P
376 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
377 strcpy(exename, "PSX.EXE;1");
378 strcpy(CdromId, "SLUS99999");
379 } else
380 return -1; // SYSTEM.CNF and PSX.EXE not found
381
382 if (CdromId[0] == '\0') {
e06ee7f4 383 len = strlen(exename);
384 c = 0;
385 for (i = 0; i < len; ++i) {
386 if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
387 break;
388 if (isalnum(exename[i]))
389 CdromId[c++] = exename[i];
ef79bbde
P
390 }
391 }
392
0e2e3f49 393 if (CdromId[0] == '\0')
394 strcpy(CdromId, "SLUS99999");
395
ef79bbde 396 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
40337130 397 if (
398 /* Make sure Wild Arms SCUS-94608 is not detected as a PAL game. */
399 ((CdromId[0] == 's' || CdromId[0] == 'S') && (CdromId[2] == 'e' || CdromId[2] == 'E')) ||
400 !strncmp(CdromId, "DTLS3035", 8) ||
401 !strncmp(CdromId, "PBPX95001", 9) || // according to redump.org, these PAL
402 !strncmp(CdromId, "PBPX95007", 9) || // discs have a non-standard ID;
403 !strncmp(CdromId, "PBPX95008", 9)) // add more serials if they are discovered.
ef79bbde
P
404 Config.PsxType = PSX_TYPE_PAL; // pal
405 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
406 }
407
408 if (CdromLabel[0] == ' ') {
409 strncpy(CdromLabel, CdromId, 9);
410 }
411 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
412 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
e06ee7f4 413 SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
eedfe806 414
415 Apply_Hacks_Cdrom();
ef79bbde
P
416
417 BuildPPFCache();
418
419 return 0;
420}
421
422static int PSXGetFileType(FILE *f) {
423 unsigned long current;
424 u8 mybuf[2048];
425 EXE_HEADER *exe_hdr;
426 FILHDR *coff_hdr;
427
428 current = ftell(f);
429 fseek(f, 0L, SEEK_SET);
7a8d521f 430 if (fread(&mybuf, 1, sizeof(mybuf), f) != sizeof(mybuf))
431 goto io_fail;
432
ef79bbde
P
433 fseek(f, current, SEEK_SET);
434
435 exe_hdr = (EXE_HEADER *)mybuf;
436 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
437 return PSX_EXE;
438
439 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
440 return CPE_EXE;
441
442 coff_hdr = (FILHDR *)mybuf;
443 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
444 return COFF_EXE;
445
446 return INVALID_EXE;
7a8d521f 447
448io_fail:
449#ifndef NDEBUG
450 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
451#endif
452 return INVALID_EXE;
ef79bbde
P
453}
454
96bef96f 455// temporary pandora workaround..
456// FIXME: remove
457size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
458{
459 void *tmp;
460 size_t ret = 0;
7a8d521f 461
96bef96f 462 tmp = malloc(size * nmemb);
463 if (tmp) {
464 ret = fread(tmp, size, nmemb, stream);
465 memcpy(ptr, tmp, size * nmemb);
466 free(tmp);
467 }
468 return ret;
469}
470
ef79bbde
P
471int Load(const char *ExePath) {
472 FILE *tmpFile;
473 EXE_HEADER tmpHead;
474 int type;
475 int retval = 0;
476 u8 opcode;
477 u32 section_address, section_size;
ebe6ff2b 478 void *mem;
ef79bbde 479
7a8d521f 480 strcpy(CdromId, "SLUS99999");
481 strcpy(CdromLabel, "SLUS_999.99");
ef79bbde
P
482
483 tmpFile = fopen(ExePath, "rb");
484 if (tmpFile == NULL) {
485 SysPrintf(_("Error opening file: %s.\n"), ExePath);
486 retval = -1;
487 } else {
488 type = PSXGetFileType(tmpFile);
489 switch (type) {
490 case PSX_EXE:
7a8d521f 491 if (fread(&tmpHead, 1, sizeof(EXE_HEADER), tmpFile) != sizeof(EXE_HEADER))
492 goto fail_io;
ebe6ff2b 493 section_address = SWAP32(tmpHead.t_addr);
494 section_size = SWAP32(tmpHead.t_size);
495 mem = PSXM(section_address);
7a8d521f 496 if (mem != INVALID_PTR) {
497 fseek(tmpFile, 0x800, SEEK_SET);
96bef96f 498 fread_to_ram(mem, section_size, 1, tmpFile);
ebe6ff2b 499 psxCpu->Clear(section_address, section_size / 4);
500 }
6d75addf 501 SetBootRegs(SWAP32(tmpHead.pc0), SWAP32(tmpHead.gp0),
502 SWAP32(tmpHead.s_addr));
ef79bbde
P
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 {
7a8d521f 508 if (fread(&opcode, 1, sizeof(opcode), tmpFile) != sizeof(opcode))
509 goto fail_io;
ef79bbde
P
510 switch (opcode) {
511 case 1: /* Section loading */
7a8d521f 512 if (fread(&section_address, 1, sizeof(section_address), tmpFile) != sizeof(section_address))
513 goto fail_io;
514 if (fread(&section_size, 1, sizeof(section_size), 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);
7a8d521f 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 */
7a8d521f 529 if (fread(&psxRegs.pc, 1, sizeof(psxRegs.pc), 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
7a8d521f 559 if (tmpFile)
560 fclose(tmpFile);
ef79bbde 561 return retval;
7a8d521f 562
563fail_io:
564#ifndef NDEBUG
565 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
566#endif
567 fclose(tmpFile);
568 return -1;
ef79bbde
P
569}
570
571// STATES
572
496d88d4 573static void *zlib_open(const char *name, const char *mode)
574{
575 return gzopen(name, mode);
576}
577
578static int zlib_read(void *file, void *buf, u32 len)
579{
580 return gzread(file, buf, len);
581}
582
583static int zlib_write(void *file, const void *buf, u32 len)
584{
585 return gzwrite(file, buf, len);
586}
587
588static long zlib_seek(void *file, long offs, int whence)
589{
590 return gzseek(file, offs, whence);
591}
592
593static void zlib_close(void *file)
594{
595 gzclose(file);
596}
597
598struct PcsxSaveFuncs SaveFuncs = {
599 zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
600};
601
7a8d521f 602static const char PcsxHeader[32] = "STv4 PCSX v" PCSX_VERSION;
ef79bbde
P
603
604// Savestate Versioning!
605// If you make changes to the savestate version, please increment the value below.
df656dde 606static const u32 SaveVersion = 0x8b410006;
ef79bbde
P
607
608int SaveState(const char *file) {
496d88d4 609 void *f;
ef79bbde 610 GPUFreeze_t *gpufP;
6d75addf 611 SPUFreezeHdr_t *spufH;
ef79bbde
P
612 SPUFreeze_t *spufP;
613 int Size;
614 unsigned char *pMem;
615
496d88d4 616 f = SaveFuncs.open(file, "wb");
ef79bbde
P
617 if (f == NULL) return -1;
618
980f7a58 619 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
52082bc1 620
496d88d4 621 SaveFuncs.write(f, (void *)PcsxHeader, 32);
622 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
623 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
ef79bbde
P
624
625 pMem = (unsigned char *)malloc(128 * 96 * 3);
626 if (pMem == NULL) return -1;
627 GPU_getScreenPic(pMem);
496d88d4 628 SaveFuncs.write(f, pMem, 128 * 96 * 3);
ef79bbde
P
629 free(pMem);
630
631 if (Config.HLE)
632 psxBiosFreeze(1);
633
496d88d4 634 SaveFuncs.write(f, psxM, 0x00200000);
635 SaveFuncs.write(f, psxR, 0x00080000);
636 SaveFuncs.write(f, psxH, 0x00010000);
81dbbf4c 637 // only partial save of psxRegisters to maintain savestate compat
638 SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
ef79bbde
P
639
640 // gpu
641 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
642 gpufP->ulFreezeVersion = 1;
643 GPU_freeze(1, gpufP);
496d88d4 644 SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
ef79bbde
P
645 free(gpufP);
646
647 // spu
6d75addf 648 spufH = malloc(sizeof(*spufH));
649 SPU_freeze(2, (SPUFreeze_t *)spufH, psxRegs.cycle);
650 Size = spufH->Size; SaveFuncs.write(f, &Size, 4);
651 free(spufH);
ef79bbde 652 spufP = (SPUFreeze_t *) malloc(Size);
650adfd2 653 SPU_freeze(1, spufP, psxRegs.cycle);
496d88d4 654 SaveFuncs.write(f, spufP, Size);
ef79bbde
P
655 free(spufP);
656
657 sioFreeze(f, 1);
658 cdrFreeze(f, 1);
659 psxHwFreeze(f, 1);
660 psxRcntFreeze(f, 1);
661 mdecFreeze(f, 1);
03f55e6b 662 new_dyna_freeze(f, 1);
ef79bbde 663
496d88d4 664 SaveFuncs.close(f);
ef79bbde
P
665
666 return 0;
667}
668
669int LoadState(const char *file) {
496d88d4 670 void *f;
ef79bbde
P
671 GPUFreeze_t *gpufP;
672 SPUFreeze_t *spufP;
673 int Size;
674 char header[32];
675 u32 version;
676 boolean hle;
677
496d88d4 678 f = SaveFuncs.open(file, "rb");
ef79bbde
P
679 if (f == NULL) return -1;
680
496d88d4 681 SaveFuncs.read(f, header, sizeof(header));
682 SaveFuncs.read(f, &version, sizeof(u32));
683 SaveFuncs.read(f, &hle, sizeof(boolean));
ef79bbde 684
33f56da1 685 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
496d88d4 686 SaveFuncs.close(f);
ef79bbde
P
687 return -1;
688 }
33f56da1 689 Config.HLE = hle;
690
691 if (Config.HLE)
692 psxBiosInit();
ef79bbde 693
496d88d4 694 SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
496d88d4 695 SaveFuncs.read(f, psxM, 0x00200000);
696 SaveFuncs.read(f, psxR, 0x00080000);
697 SaveFuncs.read(f, psxH, 0x00010000);
81dbbf4c 698 SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
699 psxRegs.gteBusyCycle = psxRegs.cycle;
ef79bbde 700
980f7a58 701 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
702
ef79bbde
P
703 if (Config.HLE)
704 psxBiosFreeze(0);
705
706 // gpu
707 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
496d88d4 708 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
ef79bbde
P
709 GPU_freeze(0, gpufP);
710 free(gpufP);
ddbaf678 711 if (HW_GPU_STATUS == 0)
086adfff 712 HW_GPU_STATUS = SWAP32(GPU_readStatus());
ef79bbde
P
713
714 // spu
496d88d4 715 SaveFuncs.read(f, &Size, 4);
ef79bbde 716 spufP = (SPUFreeze_t *)malloc(Size);
496d88d4 717 SaveFuncs.read(f, spufP, Size);
650adfd2 718 SPU_freeze(0, spufP, psxRegs.cycle);
ef79bbde
P
719 free(spufP);
720
721 sioFreeze(f, 0);
722 cdrFreeze(f, 0);
723 psxHwFreeze(f, 0);
724 psxRcntFreeze(f, 0);
725 mdecFreeze(f, 0);
03f55e6b 726 new_dyna_freeze(f, 0);
ef79bbde 727
496d88d4 728 SaveFuncs.close(f);
ef79bbde
P
729
730 return 0;
731}
732
733int CheckState(const char *file) {
496d88d4 734 void *f;
ef79bbde
P
735 char header[32];
736 u32 version;
737 boolean hle;
738
496d88d4 739 f = SaveFuncs.open(file, "rb");
ef79bbde
P
740 if (f == NULL) return -1;
741
496d88d4 742 SaveFuncs.read(f, header, sizeof(header));
743 SaveFuncs.read(f, &version, sizeof(u32));
744 SaveFuncs.read(f, &hle, sizeof(boolean));
ef79bbde 745
496d88d4 746 SaveFuncs.close(f);
ef79bbde 747
33f56da1 748 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
ef79bbde
P
749 return -1;
750
751 return 0;
752}
753
754// NET Function Helpers
755
756int SendPcsxInfo() {
757 if (NET_recvData == NULL || NET_sendData == NULL)
758 return 0;
759
d014a471 760 boolean Sio_old = 0;
761 boolean SpuIrq_old = 0;
762 boolean RCntFix_old = 0;
ef79bbde 763 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
d014a471 764 NET_sendData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
765 NET_sendData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
766 NET_sendData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
ef79bbde
P
767 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
768 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
769
770 return 0;
771}
772
773int RecvPcsxInfo() {
774 int tmp;
775
776 if (NET_recvData == NULL || NET_sendData == NULL)
777 return 0;
778
d014a471 779 boolean Sio_old = 0;
780 boolean SpuIrq_old = 0;
781 boolean RCntFix_old = 0;
ef79bbde 782 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
d014a471 783 NET_recvData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
784 NET_recvData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
785 NET_recvData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
ef79bbde
P
786 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
787
788 SysUpdate();
789
790 tmp = Config.Cpu;
791 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
792 if (tmp != Config.Cpu) {
793 psxCpu->Shutdown();
41e82ad4 794#ifndef DRC_DISABLE
ef79bbde
P
795 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
796 else psxCpu = &psxRec;
797#else
798 psxCpu = &psxInt;
799#endif
800 if (psxCpu->Init() == -1) {
801 SysClose(); return -1;
802 }
803 psxCpu->Reset();
980f7a58 804 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
ef79bbde
P
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}