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