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