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