log some build info
[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
694 result = 0;
695cleanup:
696 SaveFuncs.close(f);
697 return result;
698}
699
700int LoadState(const char *file) {
701 u32 biosBranchCheckOld = psxRegs.biosBranchCheck;
702 void *f;
703 GPUFreeze_t *gpufP = NULL;
704 SPUFreeze_t *spufP = NULL;
705 int Size;
706 char header[32];
707 u32 version;
708 boolean hle;
709 int result = -1;
710
711 f = SaveFuncs.open(file, "rb");
712 if (f == NULL) return -1;
713
714 SaveFuncs.read(f, header, sizeof(header));
715 SaveFuncs.read(f, &version, sizeof(u32));
716 SaveFuncs.read(f, &hle, sizeof(boolean));
717
718 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
719 SysPrintf("incompatible savestate version %x\n", version);
720 goto cleanup;
721 }
722 Config.HLE = hle;
723
724 if (Config.HLE)
725 psxBiosInit();
726
727 SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
728 SaveFuncs.read(f, psxM, 0x00200000);
729 SaveFuncs.read(f, psxR, 0x00080000);
730 SaveFuncs.read(f, psxH, 0x00010000);
731 SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
732 psxRegs.gteBusyCycle = psxRegs.cycle;
733 psxRegs.biosBranchCheck = ~0;
734
735 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
736
737 if (Config.HLE)
738 psxBiosFreeze(0);
739
740 // gpu
741 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
742 if (gpufP == NULL) goto cleanup;
743 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
744 GPU_freeze(0, gpufP);
745 free(gpufP);
746 if (HW_GPU_STATUS == 0)
747 HW_GPU_STATUS = SWAP32(GPU_readStatus());
748
749 // spu
750 SaveFuncs.read(f, &Size, 4);
751 spufP = (SPUFreeze_t *)malloc(Size);
752 if (spufP == NULL) goto cleanup;
753 SaveFuncs.read(f, spufP, Size);
754 SPU_freeze(0, spufP, psxRegs.cycle);
755 free(spufP);
756
757 sioFreeze(f, 0);
758 cdrFreeze(f, 0);
759 psxHwFreeze(f, 0);
760 psxRcntFreeze(f, 0);
761 mdecFreeze(f, 0);
762 new_dyna_freeze(f, 0);
763
764 if (Config.HLE)
765 psxBiosCheckExe(biosBranchCheckOld, 0x60);
766
767 result = 0;
768cleanup:
769 SaveFuncs.close(f);
770 return result;
771}
772
773int CheckState(const char *file) {
774 void *f;
775 char header[32];
776 u32 version;
777 boolean hle;
778
779 f = SaveFuncs.open(file, "rb");
780 if (f == NULL) return -1;
781
782 SaveFuncs.read(f, header, sizeof(header));
783 SaveFuncs.read(f, &version, sizeof(u32));
784 SaveFuncs.read(f, &hle, sizeof(boolean));
785
786 SaveFuncs.close(f);
787
788 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
789 return -1;
790
791 return 0;
792}
793
794// NET Function Helpers
795
796int SendPcsxInfo() {
797 if (NET_recvData == NULL || NET_sendData == NULL)
798 return 0;
799
800 boolean Sio_old = 0;
801 boolean SpuIrq_old = 0;
802 boolean RCntFix_old = 0;
803 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
804 NET_sendData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
805 NET_sendData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
806 NET_sendData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
807 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
808 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
809
810 return 0;
811}
812
813int RecvPcsxInfo() {
814 int tmp;
815
816 if (NET_recvData == NULL || NET_sendData == NULL)
817 return 0;
818
819 boolean Sio_old = 0;
820 boolean SpuIrq_old = 0;
821 boolean RCntFix_old = 0;
822 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
823 NET_recvData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
824 NET_recvData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
825 NET_recvData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
826 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
827
828 tmp = Config.Cpu;
829 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
830 if (tmp != Config.Cpu) {
831 psxCpu->Shutdown();
832#ifndef DRC_DISABLE
833 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
834 else psxCpu = &psxRec;
835#else
836 psxCpu = &psxInt;
837#endif
838 if (psxCpu->Init() == -1) {
839 SysClose(); return -1;
840 }
841 psxCpu->Reset();
842 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
843 }
844
845 return 0;
846}
847
848// remove the leading and trailing spaces in a string
849void trim(char *str) {
850 int pos = 0;
851 char *dest = str;
852
853 // skip leading blanks
854 while (str[pos] <= ' ' && str[pos] > 0)
855 pos++;
856
857 while (str[pos]) {
858 *(dest++) = str[pos];
859 pos++;
860 }
861
862 *(dest--) = '\0'; // store the null
863
864 // remove trailing blanks
865 while (dest >= str && *dest <= ' ' && *dest > 0)
866 *(dest--) = '\0';
867}
868
869// lookup table for crc calculation
870static unsigned short crctab[256] = {
871 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
872 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
873 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
874 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
875 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
876 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
877 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
878 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
879 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
880 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
881 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
882 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
883 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
884 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
885 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
886 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
887 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
888 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
889 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
890 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
891 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
892 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
893 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
894 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
895 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
896 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
897 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
898 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
899 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
900};
901
902u16 calcCrc(u8 *d, int len) {
903 u16 crc = 0;
904 int i;
905
906 for (i = 0; i < len; i++) {
907 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);
908 }
909
910 return ~crc;
911}