1 /***************************************************************************
2 * Copyright (C) 2007 Ryan Schultz, PCSX-df Team, PCSX team *
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. *
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. *
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 ***************************************************************************/
21 * Miscellaneous functions, including savestates and CD-ROM loading.
29 #include "cdrom-async.h"
38 char CdromId[10] = "";
39 char CdromLabel[33] = "";
40 int CdromFrontendId; // for frontend use
42 static u32 save_counter;
44 // PSX Executable types
50 #define ISODCL(from, to) (to - from + 1)
52 struct iso_directory_record {
53 char length [ISODCL (1, 1)]; /* 711 */
54 char ext_attr_length [ISODCL (2, 2)]; /* 711 */
55 char extent [ISODCL (3, 10)]; /* 733 */
56 char size [ISODCL (11, 18)]; /* 733 */
57 char date [ISODCL (19, 25)]; /* 7 by 711 */
58 char flags [ISODCL (26, 26)];
59 char file_unit_size [ISODCL (27, 27)]; /* 711 */
60 char interleave [ISODCL (28, 28)]; /* 711 */
61 char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
62 unsigned char name_len [ISODCL (33, 33)]; /* 711 */
66 static void mmssdd(const char *b, char *p)
68 const unsigned char *ub = (void *)b;
69 int block = (ub[3] << 24) | (ub[2] << 16) | (ub[1] << 8) | ub[0];
73 m = block / 4500; // minutes
74 block = block - m * 4500; // minutes rest
75 s = block / 75; // seconds
76 d = block - s * 75; // seconds rest
88 if (time[1] == 60) { \
95 if (cdra_readTrack(time)) return -1; \
96 buf = cdra_getBuffer(); \
97 if (buf == NULL) return -1; \
98 else CheckPPFCache((u8 *)buf, time[0], time[1], time[2]);
100 #define READDIR(_dir) \
102 memcpy(_dir, buf + 12, 2048); \
106 memcpy(_dir + 2048, buf + 12, 2048);
108 int GetCdromFile(u8 *mdir, u8 *time, char *filename) {
109 struct iso_directory_record *dir;
115 // only try to scan if a filename is given
116 if (filename == INVALID_PTR || !strlen(filename)) return -1;
120 dir = (struct iso_directory_record*) &mdir[i];
121 if (dir->length[0] == 0) {
124 i += (u8)dir->length[0];
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;
130 filename += dir->name_len[0] + 1;
132 mmssdd(dir->extent, (char *)time);
138 if (!strnicmp((char *)&dir->name[0], filename, strlen(filename))) {
139 mmssdd(dir->extent, (char *)time);
148 static void SetBootRegs(u32 pc, u32 gp, u32 sp)
150 //printf("%s %08x %08x %08x\n", __func__, pc, gp, sp);
151 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
154 psxRegs.GPR.n.gp = gp;
155 psxRegs.GPR.n.sp = sp ? sp : 0x801fff00;
156 psxRegs.GPR.n.fp = psxRegs.GPR.n.sp;
158 psxRegs.GPR.n.t0 = psxRegs.GPR.n.sp; // mimic A(43)
159 psxRegs.GPR.n.t3 = pc;
161 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
164 int BiosBootBypass() {
165 struct CdrStat stat = { 0, 0, };
166 assert(psxRegs.pc == 0x80030000);
168 // no bypass if the lid is open
169 CDR__getStatus(&stat);
170 if (stat.Status & 0x10)
173 // skip BIOS logos and region check
174 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
175 psxRegs.pc = psxRegs.GPR.n.ra;
179 static void getFromCnf(char *buf, const char *key, u32 *val)
181 buf = strstr(buf, key);
183 buf = strchr(buf, '=');
187 v = strtoul(buf + 1, NULL, 16);
196 u32 d[sizeof(EXE_HEADER) / sizeof(u32)];
198 struct iso_directory_record *dir;
213 if (psxRegs.pc != 0x80030000) // BiosBootBypass'ed or custom BIOS?
219 time[0] = 0; time[1] = 2; time[2] = 0x10;
223 // skip head and sub, and go to the root directory record
224 dir = (struct iso_directory_record*) &buf[12+156];
226 mmssdd(dir->extent, (char*)time);
230 // Load SYSTEM.CNF and scan for the main executable
231 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
232 // if SYSTEM.CNF is missing, start an existing PSX.EXE
233 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
234 strcpy(exename, "PSX.EXE;1");
239 // read the SYSTEM.CNF
243 ret = sscanf((char *)buf + 12, "BOOT = cdrom:\\%255s", exename);
244 if (ret < 1 || GetCdromFile(mdir, time, exename) == -1) {
245 ret = sscanf((char *)buf + 12, "BOOT = cdrom:%255s", exename);
246 if (ret < 1 || GetCdromFile(mdir, time, exename) == -1) {
247 char *ptr = strstr((char *)buf + 12, "cdrom:");
250 while (*ptr == '\\' || *ptr == '/') ptr++;
251 strncpy(exename, ptr, 255);
254 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
256 if (GetCdromFile(mdir, time, exename) == -1)
262 getFromCnf((char *)buf + 12, "TCB", &cnf_tcb);
263 getFromCnf((char *)buf + 12, "EVENT", &cnf_event);
264 getFromCnf((char *)buf + 12, "STACK", &cnf_stack);
266 psxBiosCnfLoaded(cnf_tcb, cnf_event, cnf_stack);
268 // Read the EXE-Header
272 memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
273 for (i = 2; i < sizeof(tmpHead.d) / sizeof(tmpHead.d[0]); i++)
274 tmpHead.d[i] = SWAP32(tmpHead.d[i]);
276 SysPrintf("manual booting '%s' pc=%x\n", exename, tmpHead.h.pc0);
277 sp = tmpHead.h.s_addr;
280 SetBootRegs(tmpHead.h.pc0, tmpHead.h.gp0, sp);
282 // Read the rest of the main executable
283 for (t_addr = tmpHead.h.t_addr, t_size = tmpHead.h.t_size; t_size & ~2047; ) {
284 void *ptr = (void *)PSXM(t_addr);
289 if (ptr != INVALID_PTR) memcpy(ptr, buf+12, 2048);
295 psxCpu->Clear(tmpHead.h.t_addr, tmpHead.h.t_size / 4);
299 psxBiosCheckExe(tmpHead.h.t_addr, tmpHead.h.t_size, 0);
304 int LoadCdromFile(const char *filename, EXE_HEADER *head, u8 *time_bcd_out) {
305 struct iso_directory_record *dir;
313 if (filename == INVALID_PTR)
317 if ((p2 = strchr(p1, ':')))
321 snprintf(exename, sizeof(exename), "%s", p1);
323 time[0] = 0; time[1] = 2; time[2] = 0x10;
327 // skip head and sub, and go to the root directory record
328 dir = (struct iso_directory_record *)&buf[12 + 156];
330 mmssdd(dir->extent, (char*)time);
334 if (GetCdromFile(mdir, time, exename) == -1) return -1;
339 memcpy(head, buf + 12, sizeof(EXE_HEADER));
340 size = SWAP32(head->t_size);
341 addr = SWAP32(head->t_addr);
343 psxCpu->Clear(addr, size / 4);
346 while (size & ~2047) {
351 if (mem != INVALID_PTR)
352 memcpy(mem, buf + 12, 2048);
358 time_bcd_out[0] = itob(time[0]);
359 time_bcd_out[1] = itob(time[1]);
360 time_bcd_out[2] = itob(time[2]);
367 struct iso_directory_record *dir;
368 struct CdrStat stat = { 0, 0, };
369 unsigned char time[4] = { 0, 2, 4 };
371 unsigned char mdir[4096];
373 int lic_region_detected = -1;
377 memset(CdromLabel, 0, sizeof(CdromLabel));
378 memset(CdromId, 0, sizeof(CdromId));
379 memset(exename, 0, sizeof(exename));
381 if (!Config.HLE && Config.SlowBoot) {
382 // boot to BIOS in case of CDDA or lid is open
383 cdra_getStatus(&stat);
384 if ((stat.Status & 0x10) || stat.Type == 2 || cdra_readTrack(time))
387 if (Config.PsxAuto) {
392 if (strcmp((char *)buf + 12 + 46, "Entertainment Euro pe ") == 0)
393 lic_region_detected = PSX_TYPE_PAL;
394 // else it'll default to NTSC anyway
402 strncpy(CdromLabel, buf + 52, 32);
404 // skip head and sub, and go to the root directory record
405 dir = (struct iso_directory_record *)&buf[12 + 156];
407 mmssdd(dir->extent, (char *)time);
411 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
414 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
415 if (GetCdromFile(mdir, time, exename) == -1) {
416 sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
417 if (GetCdromFile(mdir, time, exename) == -1) {
418 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
421 while (*ptr == '\\' || *ptr == '/') ptr++;
422 strncpy(exename, ptr, 255);
425 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
427 if (GetCdromFile(mdir, time, exename) == -1)
428 return -1; // main executable not found
433 /* Workaround for Wild Arms EU/US which has non-standard string causing incorrect region detection */
434 if (exename[0] == 'E' && exename[1] == 'X' && exename[2] == 'E' && exename[3] == '\\') {
436 size_t i, len = strlen(exename) - offset;
437 for (i = 0; i < len; i++)
438 exename[i] = exename[i + offset];
441 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
442 strcpy(exename, "PSX.EXE;1");
443 strcpy(CdromId, "SLUS99999");
445 return -1; // SYSTEM.CNF and PSX.EXE not found
447 if (CdromId[0] == '\0') {
448 len = strlen(exename);
450 for (i = 0; i < len; ++i) {
451 if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
453 if (isalnum((int)exename[i]))
454 CdromId[c++] = exename[i];
458 if (CdromId[0] == '\0')
459 strcpy(CdromId, "SLUS99999");
461 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
462 if (lic_region_detected >= 0)
463 Config.PsxType = lic_region_detected;
465 /* Make sure Wild Arms SCUS-94608 is not detected as a PAL game. */
466 ((CdromId[0] == 's' || CdromId[0] == 'S') && (CdromId[2] == 'e' || CdromId[2] == 'E')) ||
467 !strncmp(CdromId, "DTLS3035", 8) ||
468 !strncmp(CdromId, "PBPX95001", 9) || // according to redump.org, these PAL
469 !strncmp(CdromId, "PBPX95007", 9) || // discs have a non-standard ID;
470 !strncmp(CdromId, "PBPX95008", 9)) // add more serials if they are discovered.
471 Config.PsxType = PSX_TYPE_PAL; // pal
472 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
475 if (CdromLabel[0] == ' ') {
476 strncpy(CdromLabel, CdromId, 9);
478 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
479 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
480 SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
489 static int PSXGetFileType(FILE *f) {
490 unsigned long current;
496 fseek(f, 0L, SEEK_SET);
497 if (fread(&mybuf, 1, sizeof(mybuf), f) != sizeof(mybuf))
500 fseek(f, current, SEEK_SET);
502 exe_hdr = (EXE_HEADER *)mybuf;
503 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
506 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
509 coff_hdr = (FILHDR *)mybuf;
510 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
517 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
522 // temporary pandora workaround..
524 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
529 tmp = malloc(size * nmemb);
531 ret = fread(tmp, size, nmemb, stream);
532 memcpy(ptr, tmp, size * nmemb);
536 ret = fread(ptr, size, nmemb, stream);
540 int Load(const char *ExePath) {
546 u32 section_address, section_size;
549 strcpy(CdromId, "SLUS99999");
550 strcpy(CdromLabel, "SLUS_999.99");
552 tmpFile = fopen(ExePath, "rb");
553 if (tmpFile == NULL) {
554 SysPrintf(_("Error opening file: %s.\n"), ExePath);
557 type = PSXGetFileType(tmpFile);
560 if (fread(&tmpHead, 1, sizeof(EXE_HEADER), tmpFile) != sizeof(EXE_HEADER))
562 section_address = SWAP32(tmpHead.t_addr);
563 section_size = SWAP32(tmpHead.t_size);
564 mem = PSXM(section_address);
565 if (mem != INVALID_PTR) {
566 fseek(tmpFile, 0x800, SEEK_SET);
567 fread_to_ram(mem, section_size, 1, tmpFile);
568 psxCpu->Clear(section_address, section_size / 4);
570 SetBootRegs(SWAP32(tmpHead.pc0), SWAP32(tmpHead.gp0),
571 SWAP32(tmpHead.s_addr));
575 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
577 if (fread(&opcode, 1, sizeof(opcode), tmpFile) != sizeof(opcode))
580 case 1: /* Section loading */
581 if (fread(§ion_address, 1, sizeof(section_address), tmpFile) != sizeof(section_address))
583 if (fread(§ion_size, 1, sizeof(section_size), tmpFile) != sizeof(section_size))
585 section_address = SWAPu32(section_address);
586 section_size = SWAPu32(section_size);
588 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
590 mem = PSXM(section_address);
591 if (mem != INVALID_PTR) {
592 fread_to_ram(mem, section_size, 1, tmpFile);
593 psxCpu->Clear(section_address, section_size / 4);
596 case 3: /* register loading (PC only?) */
597 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
598 if (fread(&psxRegs.pc, 1, sizeof(psxRegs.pc), tmpFile) != sizeof(psxRegs.pc))
600 psxRegs.pc = SWAPu32(psxRegs.pc);
602 case 0: /* End of file */
605 SysPrintf(_("Unknown CPE opcode %02x at position %08lx.\n"), opcode, ftell(tmpFile) - 1);
609 } while (opcode != 0 && retval == 0);
612 SysPrintf(_("COFF files not supported.\n"));
616 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
617 SysPrintf(_("(did you forget -cdfile ?)\n"));
625 CdromLabel[0] = '\0';
634 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
642 static void *zlib_open(const char *name, const char *mode)
644 return gzopen(name, mode);
647 static int zlib_read(void *file, void *buf, u32 len)
649 return gzread(file, buf, len);
652 static int zlib_write(void *file, const void *buf, u32 len)
654 return gzwrite(file, buf, len);
657 static long zlib_seek(void *file, long offs, int whence)
659 return gzseek(file, offs, whence);
662 static void zlib_close(void *file)
667 struct PcsxSaveFuncs SaveFuncs = {
668 zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
671 static const char PcsxHeader[32] = "STv4 PCSXra " REV;
673 // Savestate Versioning!
674 // If you make changes to the savestate version, please increment the value below.
675 static const u32 SaveVersion = 0x8b410006;
678 boolean icache_emulation;
679 boolean DisableStalls;
680 boolean PreciseExceptions;
683 s8 FractionalFramerate;
689 #define MISC_MAGIC 0x4353494d
690 struct misc_save_data {
703 #define EX_SCREENPIC_SIZE (128 * 96 * 3)
705 int SaveState(const char *file) {
706 struct misc_save_data *misc = (void *)(psxH + 0xf000);
707 struct origin_info oi = { 0, };
708 GPUFreeze_t *gpufP = NULL;
709 SPUFreezeHdr_t spufH;
710 SPUFreeze_t *spufP = NULL;
711 u8 buf[EX_SCREENPIC_SIZE];
716 assert(!psxRegs.branching);
717 assert(!psxRegs.cpuInRecursion);
718 assert(!misc->magic);
720 f = SaveFuncs.open(file, "wb");
721 if (f == NULL) return -1;
723 misc->magic = MISC_MAGIC;
724 misc->gteBusyCycle = psxRegs.gteBusyCycle;
725 misc->muldivBusyCycle = psxRegs.muldivBusyCycle;
726 misc->biuReg = psxRegs.biuReg;
727 misc->biosBranchCheck = psxRegs.biosBranchCheck;
728 misc->gpuIdleAfter = psxRegs.gpuIdleAfter;
729 misc->gpuSr = HW_GPU_STATUS;
730 misc->frame_counter = frame_counter;
731 misc->CdromFrontendId = CdromFrontendId;
732 misc->save_counter = ++save_counter;
734 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
736 SaveFuncs.write(f, (void *)PcsxHeader, 32);
737 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
738 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
740 oi.icache_emulation = Config.icache_emulation;
741 oi.DisableStalls = Config.DisableStalls;
742 oi.PreciseExceptions = Config.PreciseExceptions;
743 oi.TurboCD = Config.TurboCD;
744 oi.GpuListWalking = Config.GpuListWalking;
745 oi.FractionalFramerate = Config.FractionalFramerate;
747 oi.PsxType = Config.PsxType;
748 snprintf(oi.build_info, sizeof(oi.build_info), "%s", get_build_info());
750 // this was space for ScreenPic
751 assert(sizeof(buf) >= EX_SCREENPIC_SIZE);
752 assert(sizeof(oi) - 3 <= EX_SCREENPIC_SIZE);
753 memset(buf, 0, sizeof(buf));
754 memcpy(buf + 3, &oi, sizeof(oi));
755 SaveFuncs.write(f, buf, EX_SCREENPIC_SIZE);
760 SaveFuncs.write(f, psxM, 0x00200000);
761 SaveFuncs.write(f, psxR, 0x00080000);
762 SaveFuncs.write(f, psxH, 0x00010000);
763 // only partial save of psxRegisters to maintain savestate compat
764 SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
767 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
768 if (gpufP == NULL) goto cleanup;
769 gpufP->ulFreezeVersion = 1;
770 GPU_freeze(1, gpufP);
771 SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
772 free(gpufP); gpufP = NULL;
775 SPU_freeze(2, (SPUFreeze_t *)&spufH, psxRegs.cycle);
776 Size = spufH.Size; SaveFuncs.write(f, &Size, 4);
777 spufP = (SPUFreeze_t *) malloc(Size);
778 if (spufP == NULL) goto cleanup;
779 SPU_freeze(1, spufP, psxRegs.cycle);
780 SaveFuncs.write(f, spufP, Size);
781 free(spufP); spufP = NULL;
793 memset(misc, 0, sizeof(*misc));
798 int LoadState(const char *file) {
799 struct misc_save_data *misc = (void *)(psxH + 0xf000);
800 u32 biosBranchCheckOld = psxRegs.biosBranchCheck;
802 GPUFreeze_t *gpufP = NULL;
803 SPUFreeze_t *spufP = NULL;
810 f = SaveFuncs.open(file, "rb");
811 if (f == NULL) return -1;
815 memset(header, 0, sizeof(header));
816 SaveFuncs.read(f, header, 16);
817 if (strncmp("RASTATE", header, 7) == 0) {
818 // looks like RA header, normal savestate should follow
819 SysPrintf("%s: trying to skip RASTATE header\n", file);
820 SaveFuncs.read(f, header, 16);
822 SaveFuncs.read(f, header + 16, 16);
823 SaveFuncs.read(f, &version, sizeof(u32));
824 SaveFuncs.read(f, &hle, sizeof(boolean));
826 if (strncmp("STv4 PCSX", header, 9) != 0) {
827 SysPrintf("%s: is not a savestate?\n", file);
830 if (version != SaveVersion) {
831 SysPrintf("%s: incompatible savestate version %x\n", file, version);
840 // ex-ScreenPic space
841 SaveFuncs.seek(f, EX_SCREENPIC_SIZE, SEEK_CUR);
843 SaveFuncs.read(f, psxM, 0x00200000);
844 SaveFuncs.read(f, psxR, 0x00080000);
845 SaveFuncs.read(f, psxH, 0x00010000);
846 SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
847 psxRegs.gteBusyCycle = psxRegs.cycle;
848 psxRegs.branching = 0;
849 psxRegs.biosBranchCheck = ~0;
850 psxRegs.cpuInRecursion = 0;
851 psxRegs.gpuIdleAfter = psxRegs.cycle - 1;
852 HW_GPU_STATUS &= SWAP32(~PSXGPU_nBUSY);
853 if (misc->magic == MISC_MAGIC) {
854 psxRegs.gteBusyCycle = misc->gteBusyCycle;
855 psxRegs.muldivBusyCycle = misc->muldivBusyCycle;
856 psxRegs.biuReg = misc->biuReg;
857 psxRegs.biosBranchCheck = misc->biosBranchCheck;
858 psxRegs.gpuIdleAfter = misc->gpuIdleAfter;
859 HW_GPU_STATUS = misc->gpuSr;
860 frame_counter = misc->frame_counter;
861 CdromFrontendId = misc->CdromFrontendId;
862 if (misc->save_counter)
863 save_counter = misc->save_counter;
870 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
871 if (gpufP == NULL) goto cleanup;
872 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
873 GPU_freeze(0, gpufP);
878 SaveFuncs.read(f, &Size, 4);
879 spufP = (SPUFreeze_t *)malloc(Size);
880 if (spufP == NULL) goto cleanup;
881 SaveFuncs.read(f, spufP, Size);
882 SPU_freeze(0, spufP, psxRegs.cycle);
891 if (Config.HLE != oldhle) {
892 // at least ari64 drc compiles differently so hard reset
901 psxBiosCheckExe(biosBranchCheckOld, 0x60, 1);
903 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
907 memset(misc, 0, sizeof(*misc));
912 int CheckState(const char *file) {
918 f = SaveFuncs.open(file, "rb");
919 if (f == NULL) return -1;
921 memset(header, 0, sizeof(header));
922 SaveFuncs.read(f, header, 16);
923 if (strncmp("RASTATE", header, 7) == 0)
924 SaveFuncs.read(f, header, 16);
925 SaveFuncs.read(f, header + 16, 16);
926 SaveFuncs.read(f, &version, sizeof(u32));
927 SaveFuncs.read(f, &hle, sizeof(boolean));
931 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
937 // remove the leading and trailing spaces in a string
938 void trim(char *str) {
942 // skip leading blanks
943 while (str[pos] <= ' ' && str[pos] > 0)
947 *(dest++) = str[pos];
951 *(dest--) = '\0'; // store the null
953 // remove trailing blanks
954 while (dest >= str && *dest <= ' ' && *dest > 0)
958 // lookup table for crc calculation
959 static unsigned short crctab[256] = {
960 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
961 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
962 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
963 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
964 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
965 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
966 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
967 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
968 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
969 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
970 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
971 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
972 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
973 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
974 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
975 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
976 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
977 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
978 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
979 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
980 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
981 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
982 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
983 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
984 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
985 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
986 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
987 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
988 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
991 u16 calcCrc(const u8 *d, int len) {
995 for (i = 0; i < len; i++) {
996 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);
1002 #define MKSTR2(x) #x
1003 #define MKSTR(x) MKSTR2(x)
1004 const char *get_build_info(void)
1008 "cc " __VERSION__ " "
1010 #if defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 8
1012 #elif defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 4
1015 #if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1018 #if defined(__PIE__) || defined(__pie__)
1021 #if defined(__PIC__) || defined(__pic__)
1024 #if defined(__aarch64__)
1026 #elif defined(__arm__)
1030 "v" MKSTR(__ARM_ARCH) " "
1035 #if defined(__AVX__)
1037 #elif defined(__SSSE3__)
1039 #elif defined(__ARM_NEON) || defined(__ARM_NEON__)
1042 #if defined(__ARM_FEATURE_SVE) && __ARM_FEATURE_SVE
1045 #if defined(LIGHTREC)
1047 #elif !defined(DRC_DISABLE)
1050 "gpu=" MKSTR(BUILTIN_GPU);