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