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