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.
31 #include "cdrom-async.h"
40 char CdromId[10] = "";
41 char CdromLabel[33] = "";
42 int CdromFrontendId; // for frontend use
44 static u32 save_counter;
46 // PSX Executable types
52 #define ISODCL(from, to) (to - from + 1)
54 struct iso_directory_record {
55 char length [ISODCL (1, 1)]; /* 711 */
56 char ext_attr_length [ISODCL (2, 2)]; /* 711 */
57 char extent [ISODCL (3, 10)]; /* 733 */
58 char size [ISODCL (11, 18)]; /* 733 */
59 char date [ISODCL (19, 25)]; /* 7 by 711 */
60 char flags [ISODCL (26, 26)];
61 char file_unit_size [ISODCL (27, 27)]; /* 711 */
62 char interleave [ISODCL (28, 28)]; /* 711 */
63 char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
64 unsigned char name_len [ISODCL (33, 33)]; /* 711 */
68 static void mmssdd(const char *b, char *p)
70 const unsigned char *ub = (void *)b;
71 int block = (ub[3] << 24) | (ub[2] << 16) | (ub[1] << 8) | ub[0];
75 m = block / 4500; // minutes
76 block = block - m * 4500; // minutes rest
77 s = block / 75; // seconds
78 d = block - s * 75; // seconds rest
90 if (time[1] == 60) { \
97 if (cdra_readTrack(time)) return -1; \
98 buf = cdra_getBuffer(); \
99 if (buf == NULL) return -1; \
100 else CheckPPFCache((u8 *)buf, time[0], time[1], time[2]);
102 #define READDIR(_dir) \
104 memcpy(_dir, buf + 12, 2048); \
108 memcpy(_dir + 2048, buf + 12, 2048);
110 int GetCdromFile(u8 *mdir, u8 *time, char *filename) {
111 struct iso_directory_record *dir;
117 // only try to scan if a filename is given
118 if (filename == INVALID_PTR || !strlen(filename)) return -1;
122 dir = (struct iso_directory_record*) &mdir[i];
123 if (dir->length[0] == 0) {
126 i += (u8)dir->length[0];
128 if (dir->flags[0] & 0x2) { // it's a dir
129 if (!strnicmp((char *)&dir->name[0], filename, dir->name_len[0])) {
130 if (filename[dir->name_len[0]] != '\\') continue;
132 filename += dir->name_len[0] + 1;
134 mmssdd(dir->extent, (char *)time);
140 if (!strnicmp((char *)&dir->name[0], filename, strlen(filename))) {
141 mmssdd(dir->extent, (char *)time);
150 static void SetBootRegs(u32 pc, u32 gp, u32 sp)
152 //printf("%s %08x %08x %08x\n", __func__, pc, gp, sp);
153 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
156 psxRegs.GPR.n.gp = gp;
157 psxRegs.GPR.n.sp = sp ? sp : 0x801fff00;
158 psxRegs.GPR.n.fp = psxRegs.GPR.n.sp;
160 psxRegs.GPR.n.t0 = psxRegs.GPR.n.sp; // mimic A(43)
161 psxRegs.GPR.n.t3 = pc;
163 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
166 int BiosBootBypass() {
167 struct CdrStat stat = { 0, 0, };
168 assert(psxRegs.pc == 0x80030000);
170 // no bypass if the lid is open
171 CDR__getStatus(&stat);
172 if (stat.Status & 0x10)
175 // skip BIOS logos and region check
176 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
177 psxRegs.pc = psxRegs.GPR.n.ra;
181 static void getFromCnf(char *buf, const char *key, u32 *val)
183 buf = strstr(buf, key);
185 buf = strchr(buf, '=');
189 v = strtoul(buf + 1, NULL, 16);
198 u32 d[sizeof(EXE_HEADER) / sizeof(u32)];
200 struct iso_directory_record *dir;
215 if (psxRegs.pc != 0x80030000) // BiosBootBypass'ed or custom BIOS?
221 time[0] = 0; time[1] = 2; time[2] = 0x10;
225 // skip head and sub, and go to the root directory record
226 dir = (struct iso_directory_record*) &buf[12+156];
228 mmssdd(dir->extent, (char*)time);
232 // Load SYSTEM.CNF and scan for the main executable
233 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
234 // if SYSTEM.CNF is missing, start an existing PSX.EXE
235 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
236 strcpy(exename, "PSX.EXE;1");
241 // read the SYSTEM.CNF
245 ret = sscanf((char *)buf + 12, "BOOT = cdrom:\\%255s", exename);
246 if (ret < 1 || GetCdromFile(mdir, time, exename) == -1) {
247 ret = sscanf((char *)buf + 12, "BOOT = cdrom:%255s", exename);
248 if (ret < 1 || GetCdromFile(mdir, time, exename) == -1) {
249 char *ptr = strstr((char *)buf + 12, "cdrom:");
252 while (*ptr == '\\' || *ptr == '/') ptr++;
253 strncpy(exename, ptr, 255);
256 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
258 if (GetCdromFile(mdir, time, exename) == -1)
264 getFromCnf((char *)buf + 12, "TCB", &cnf_tcb);
265 getFromCnf((char *)buf + 12, "EVENT", &cnf_event);
266 getFromCnf((char *)buf + 12, "STACK", &cnf_stack);
268 psxBiosCnfLoaded(cnf_tcb, cnf_event, cnf_stack);
270 // Read the EXE-Header
274 memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
275 for (i = 2; i < sizeof(tmpHead.d) / sizeof(tmpHead.d[0]); i++)
276 tmpHead.d[i] = SWAP32(tmpHead.d[i]);
278 SysPrintf("manual booting '%s' pc=%x\n", exename, tmpHead.h.pc0);
279 sp = tmpHead.h.s_addr;
282 SetBootRegs(tmpHead.h.pc0, tmpHead.h.gp0, sp);
284 // Read the rest of the main executable
285 for (t_addr = tmpHead.h.t_addr, t_size = tmpHead.h.t_size; t_size & ~2047; ) {
286 void *ptr = (void *)PSXM(t_addr);
291 if (ptr != INVALID_PTR) memcpy(ptr, buf+12, 2048);
297 psxCpu->Clear(tmpHead.h.t_addr, tmpHead.h.t_size / 4);
301 psxBiosCheckExe(tmpHead.h.t_addr, tmpHead.h.t_size, 0);
306 int LoadCdromFile(const char *filename, EXE_HEADER *head, u8 *time_bcd_out) {
307 struct iso_directory_record *dir;
315 if (filename == INVALID_PTR)
319 if ((p2 = strchr(p1, ':')))
323 snprintf(exename, sizeof(exename), "%s", p1);
325 time[0] = 0; time[1] = 2; time[2] = 0x10;
329 // skip head and sub, and go to the root directory record
330 dir = (struct iso_directory_record *)&buf[12 + 156];
332 mmssdd(dir->extent, (char*)time);
336 if (GetCdromFile(mdir, time, exename) == -1) return -1;
341 memcpy(head, buf + 12, sizeof(EXE_HEADER));
342 size = SWAP32(head->t_size);
343 addr = SWAP32(head->t_addr);
345 psxCpu->Clear(addr, size / 4);
348 while (size & ~2047) {
353 if (mem != INVALID_PTR)
354 memcpy(mem, buf + 12, 2048);
360 time_bcd_out[0] = itob(time[0]);
361 time_bcd_out[1] = itob(time[1]);
362 time_bcd_out[2] = itob(time[2]);
369 struct iso_directory_record *dir;
370 struct CdrStat stat = { 0, 0, };
371 unsigned char time[4] = { 0, 2, 4 };
373 unsigned char mdir[4096];
375 int psxtype_from_lic = -1;
376 struct { u8 region; const char *str; } lic_strings[] = {
377 { PSX_REGION_JP, "Inc." },
378 { PSX_REGION_US, "Amer ica" },
379 { PSX_REGION_EU, "Euro pe" }
384 memset(CdromLabel, 0, sizeof(CdromLabel));
385 memset(CdromId, 0, sizeof(CdromId));
386 memset(exename, 0, sizeof(exename));
387 Config.PsxRegion = PSX_REGION_US;
389 if (!Config.HLE && Config.SlowBoot) {
390 // boot to BIOS in case of CDDA or lid is open
391 cdra_getStatus(&stat);
392 if ((stat.Status & 0x10) || stat.Type == 2 || cdra_readTrack(time))
399 for (i = 0; i < sizeof(lic_strings) / sizeof(lic_strings[0]); i++) {
400 len = strlen(lic_strings[i].str);
401 if (strncmp((char *)buf + 12 + 60, lic_strings[i].str, len) == 0) {
402 Config.PsxRegion = lic_strings[i].region;
403 psxtype_from_lic = Config.PsxRegion == PSX_REGION_EU
404 ? PSX_TYPE_PAL : PSX_TYPE_NTSC;
408 if (psxtype_from_lic < 0)
409 SysPrintf("CheckCdrom: missing lic sector?\n");
416 strncpy(CdromLabel, buf + 52, 32);
418 // skip head and sub, and go to the root directory record
419 dir = (struct iso_directory_record *)&buf[12 + 156];
421 mmssdd(dir->extent, (char *)time);
425 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
428 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
429 if (GetCdromFile(mdir, time, exename) == -1) {
430 sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
431 if (GetCdromFile(mdir, time, exename) == -1) {
432 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
435 while (*ptr == '\\' || *ptr == '/') ptr++;
436 strncpy(exename, ptr, 255);
439 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
441 if (GetCdromFile(mdir, time, exename) == -1)
442 return -1; // main executable not found
447 /* Workaround for Wild Arms EU/US which has non-standard string causing incorrect region detection */
448 if (exename[0] == 'E' && exename[1] == 'X' && exename[2] == 'E' && exename[3] == '\\') {
450 len = strlen(exename) - offset;
451 for (i = 0; i < len; i++)
452 exename[i] = exename[i + offset];
455 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
456 strcpy(exename, "PSX.EXE;1");
457 strcpy(CdromId, "SLUS99999");
459 return -1; // SYSTEM.CNF and PSX.EXE not found
461 if (CdromId[0] == '\0') {
462 len = strlen(exename);
464 for (i = 0; i < len; ++i) {
465 if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
467 if (isalnum((int)exename[i]))
468 CdromId[c++] = exename[i];
472 if (CdromId[0] == '\0')
473 strcpy(CdromId, "SLUS99999");
475 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
476 if (psxtype_from_lic >= 0)
477 Config.PsxType = psxtype_from_lic;
479 /* Make sure Wild Arms SCUS-94608 is not detected as a PAL game. */
480 ((CdromId[0] == 's' || CdromId[0] == 'S') && (CdromId[2] == 'e' || CdromId[2] == 'E')) ||
481 !strncmp(CdromId, "DTLS3035", 8) ||
482 !strncmp(CdromId, "PBPX95001", 9) || // according to redump.org, these PAL
483 !strncmp(CdromId, "PBPX95007", 9) || // discs have a non-standard ID;
484 !strncmp(CdromId, "PBPX95008", 9)) // add more serials if they are discovered.
485 Config.PsxType = PSX_TYPE_PAL; // pal
486 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
489 if (CdromLabel[0] == ' ') {
490 strncpy(CdromLabel, CdromId, 9);
492 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
493 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
494 SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
503 static int PSXGetFileType(FILE *f) {
504 unsigned long current;
510 fseek(f, 0L, SEEK_SET);
511 if (fread(&mybuf, 1, sizeof(mybuf), f) != sizeof(mybuf))
514 fseek(f, current, SEEK_SET);
516 exe_hdr = (EXE_HEADER *)mybuf;
517 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
520 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
523 coff_hdr = (FILHDR *)mybuf;
524 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
531 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
536 // temporary pandora workaround..
538 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
543 tmp = malloc(size * nmemb);
545 ret = fread(tmp, size, nmemb, stream);
546 memcpy(ptr, tmp, size * nmemb);
550 ret = fread(ptr, size, nmemb, stream);
554 int Load(const char *ExePath) {
560 u32 section_address, section_size;
563 strcpy(CdromId, "SLUS99999");
564 strcpy(CdromLabel, "SLUS_999.99");
566 tmpFile = fopen(ExePath, "rb");
567 if (tmpFile == NULL) {
568 SysPrintf(_("Error opening file: %s.\n"), ExePath);
571 type = PSXGetFileType(tmpFile);
574 if (fread(&tmpHead, 1, sizeof(EXE_HEADER), tmpFile) != sizeof(EXE_HEADER))
576 section_address = SWAP32(tmpHead.t_addr);
577 section_size = SWAP32(tmpHead.t_size);
578 mem = PSXM(section_address);
579 if (mem != INVALID_PTR) {
580 fseek(tmpFile, 0x800, SEEK_SET);
581 fread_to_ram(mem, section_size, 1, tmpFile);
582 psxCpu->Clear(section_address, section_size / 4);
584 SetBootRegs(SWAP32(tmpHead.pc0), SWAP32(tmpHead.gp0),
585 SWAP32(tmpHead.s_addr));
589 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
591 if (fread(&opcode, 1, sizeof(opcode), tmpFile) != sizeof(opcode))
594 case 1: /* Section loading */
595 if (fread(§ion_address, 1, sizeof(section_address), tmpFile) != sizeof(section_address))
597 if (fread(§ion_size, 1, sizeof(section_size), tmpFile) != sizeof(section_size))
599 section_address = SWAPu32(section_address);
600 section_size = SWAPu32(section_size);
601 //printf("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
602 mem = PSXM(section_address);
603 if (mem != INVALID_PTR) {
604 fread_to_ram(mem, section_size, 1, tmpFile);
605 psxCpu->Clear(section_address, section_size / 4);
608 case 3: /* register loading (PC only?) */
609 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
610 if (fread(&psxRegs.pc, 1, sizeof(psxRegs.pc), tmpFile) != sizeof(psxRegs.pc))
612 psxRegs.pc = SWAPu32(psxRegs.pc);
614 case 0: /* End of file */
617 SysPrintf(_("Unknown CPE opcode %02x at position %08lx.\n"), opcode, ftell(tmpFile) - 1);
621 } while (opcode != 0 && retval == 0);
624 SysPrintf(_("COFF files not supported.\n"));
628 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
629 SysPrintf(_("(did you forget -cdfile ?)\n"));
637 CdromLabel[0] = '\0';
646 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
654 static void *zlib_open(const char *name, const char *mode)
656 return gzopen(name, mode);
659 static int zlib_read(void *file, void *buf, u32 len)
661 return gzread(file, buf, len);
664 static int zlib_write(void *file, const void *buf, u32 len)
666 return gzwrite(file, buf, len);
669 static long zlib_seek(void *file, long offs, int whence)
671 return gzseek(file, offs, whence);
674 static void zlib_close(void *file)
679 struct PcsxSaveFuncs SaveFuncs = {
680 zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
683 static const char PcsxHeader[32] = "STv4 PCSXra " REV;
685 // Savestate Versioning!
686 // If you make changes to the savestate version, please increment the value below.
687 static const u32 SaveVersion = 0x8b410006;
690 boolean icache_emulation;
691 boolean DisableStalls;
692 boolean PreciseExceptions;
695 s8 FractionalFramerate;
701 #define MISC_MAGIC 0x4353494d
702 struct misc_save_data {
715 #define EX_SCREENPIC_SIZE (128 * 96 * 3)
717 int SaveState(const char *file) {
718 struct misc_save_data *misc = (void *)(psxRegs.ptrs.psxH + 0xf000);
719 struct origin_info oi = { 0, };
722 u8 buf[EX_SCREENPIC_SIZE];
726 u8 spu_part2[SPUFREEZE_F2_MAX_SIZE];
729 unsigned short *spuram = NULL;
730 uint16_t *vram = NULL;
733 assert(!psxRegs.branching);
734 assert(!psxRegs.cpuInRecursion);
735 assert(!misc->magic);
737 f = SaveFuncs.open(file, "wb");
738 if (f == NULL) return -1;
740 misc->magic = MISC_MAGIC;
741 misc->gteBusyCycle = psxRegs.gteBusyCycle;
742 misc->muldivBusyCycle = psxRegs.muldivBusyCycle;
743 misc->biuReg = psxRegs.biuReg;
744 misc->biosBranchCheck = psxRegs.biosBranchCheck;
745 misc->gpuIdleAfter = psxRegs.gpuIdleAfter;
746 misc->gpuSr = HW_GPU_STATUS;
747 misc->frame_counter = frame_counter;
748 misc->CdromFrontendId = CdromFrontendId;
749 misc->save_counter = ++save_counter;
751 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
753 SaveFuncs.write(f, (void *)PcsxHeader, 32);
754 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
755 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
757 oi.icache_emulation = Config.icache_emulation;
758 oi.DisableStalls = Config.DisableStalls;
759 oi.PreciseExceptions = Config.PreciseExceptions;
760 oi.TurboCD = Config.TurboCD;
761 oi.GpuListWalking = Config.GpuListWalking;
762 oi.FractionalFramerate = Config.FractionalFramerate;
764 oi.PsxType = Config.PsxType;
765 snprintf(oi.build_info, sizeof(oi.build_info), "%s", get_build_info());
767 // this was space for ScreenPic
768 assert(sizeof(oi) - 3 <= EX_SCREENPIC_SIZE);
769 memset(u.buf, 0, sizeof(u.buf));
770 memcpy(u.buf + 3, &oi, sizeof(oi));
771 SaveFuncs.write(f, u.buf, sizeof(u.buf));
776 SaveFuncs.write(f, psxRegs.ptrs.psxM, 0x00200000);
777 SaveFuncs.write(f, psxRegs.ptrs.psxR, 0x00080000);
778 SaveFuncs.write(f, psxRegs.ptrs.psxH, 0x00010000);
779 // only partial save of psxRegisters to maintain savestate compat
780 SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
783 u.gpu_hdr.ulFreezeVersion = 1;
784 u.gpu_hdr.ulStatus = 0;
785 memset(u.gpu_hdr.ulControl, 0, sizeof(u.gpu_hdr.ulControl));
786 GPU_freeze(1, &u.gpu_hdr, &vram);
787 SaveFuncs.write(f, &u.gpu_hdr, sizeof(u.gpu_hdr));
788 SaveFuncs.write(f, vram, 1024*512*2);
791 SPU_freeze(1, &u.spu_hdr, &spuram, u.spu_part2, psxRegs.cycle);
792 assert(u.spu_hdr.Size > sizeof(u.spu_hdr) + 512*1024);
793 assert(u.spu_hdr.Size <= sizeof(u.spu_hdr) + 512*1024 + sizeof(u.spu_part2));
795 SaveFuncs.write(f, &u.spu_hdr.Size, 4); // redundant, for compat
796 SaveFuncs.write(f, &u.spu_hdr, sizeof(u.spu_hdr));
797 SaveFuncs.write(f, spuram, 512*1024);
798 SaveFuncs.write(f, &u.spu_part2, u.spu_hdr.Size - sizeof(u.spu_hdr) - 512*1024);
808 memset(misc, 0, sizeof(*misc));
813 int LoadState(const char *file) {
814 struct misc_save_data *misc = (void *)(psxRegs.ptrs.psxH + 0xf000);
815 u32 biosBranchCheckOld = psxRegs.biosBranchCheck;
821 u8 spu_part2[SPUFREEZE_F2_MAX_SIZE];
824 unsigned short *spuram = NULL;
825 uint16_t *vram = NULL;
833 f = SaveFuncs.open(file, "rb");
834 if (f == NULL) return -1;
838 memset(header, 0, sizeof(header));
839 SaveFuncs.read(f, header, 16);
840 if (strncmp("RASTATE", header, 7) == 0) {
841 // looks like RA header, normal savestate should follow
842 SysPrintf("%s: trying to skip RASTATE header\n", file);
843 SaveFuncs.read(f, header, 16);
845 SaveFuncs.read(f, header + 16, 16);
846 SaveFuncs.read(f, &version, sizeof(u32));
847 SaveFuncs.read(f, &hle, sizeof(boolean));
849 if (strncmp("STv4 PCSX", header, 9) != 0) {
850 SysPrintf("%s: is not a savestate?\n", file);
853 if (version != SaveVersion) {
854 SysPrintf("%s: incompatible savestate version %x\n", file, version);
863 psxBiosResetTables();
865 // ex-ScreenPic space
866 SaveFuncs.seek(f, EX_SCREENPIC_SIZE, SEEK_CUR);
868 SaveFuncs.read(f, psxRegs.ptrs.psxM, 0x00200000);
869 SaveFuncs.read(f, psxRegs.ptrs.psxR, 0x00080000);
870 SaveFuncs.read(f, psxRegs.ptrs.psxH, 0x00010000);
871 SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
872 psxRegs.gteBusyCycle = psxRegs.cycle;
873 psxRegs.branching = 0;
874 psxRegs.biosBranchCheck = ~0;
875 psxRegs.cpuInRecursion = 0;
876 psxRegs.gpuIdleAfter = psxRegs.cycle - 1;
877 HW_GPU_STATUS &= SWAP32(~PSXGPU_nBUSY);
878 if (misc->magic == MISC_MAGIC) {
879 psxRegs.gteBusyCycle = misc->gteBusyCycle;
880 psxRegs.muldivBusyCycle = misc->muldivBusyCycle;
881 psxRegs.biuReg = misc->biuReg;
882 psxRegs.biosBranchCheck = misc->biosBranchCheck;
883 psxRegs.gpuIdleAfter = misc->gpuIdleAfter;
884 HW_GPU_STATUS = misc->gpuSr;
885 frame_counter = misc->frame_counter;
886 CdromFrontendId = misc->CdromFrontendId;
887 if (misc->save_counter)
888 save_counter = misc->save_counter;
895 SaveFuncs.read(f, &u.gpu_hdr, sizeof(u.gpu_hdr));
896 GPU_freeze(0, &u.gpu_hdr, &vram);
898 SaveFuncs.read(f, vram, 1024*512*2);
902 SaveFuncs.read(f, &Size, 4);
903 if (sizeof(u.spu_hdr) + 512*1024 < Size &&
904 (uint32_t)Size <= sizeof(u.spu_hdr) + 512*1024u + sizeof(u.spu_part2))
906 SPU_freeze(0, NULL, &spuram, NULL, psxRegs.cycle);
908 SaveFuncs.read(f, &u.spu_hdr, sizeof(u.spu_hdr));
909 SaveFuncs.read(f, spuram, 512*1024);
910 SaveFuncs.read(f, &u.spu_part2, Size - sizeof(u.spu_hdr) - 512*1024);
911 SPU_freeze(0, &u.spu_hdr, &spuram, u.spu_part2, psxRegs.cycle);
915 SysPrintf("broken spu save size %d, attempting to skip\n", Size);
916 SaveFuncs.seek(f, Size, SEEK_CUR);
925 if (Config.HLE != oldhle) {
926 // at least ari64 drc compiles differently so hard reset
935 psxBiosCheckExe(biosBranchCheckOld, 0x60, 1);
937 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD_STATE, NULL);
941 memset(misc, 0, sizeof(*misc));
946 int CheckState(const char *file) {
952 f = SaveFuncs.open(file, "rb");
953 if (f == NULL) return -1;
955 memset(header, 0, sizeof(header));
956 SaveFuncs.read(f, header, 16);
957 if (strncmp("RASTATE", header, 7) == 0)
958 SaveFuncs.read(f, header, 16);
959 SaveFuncs.read(f, header + 16, 16);
960 SaveFuncs.read(f, &version, sizeof(u32));
961 SaveFuncs.read(f, &hle, sizeof(boolean));
965 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
971 // remove the leading and trailing spaces in a string
972 void trim(char *str) {
976 // skip leading blanks
977 while (str[pos] <= ' ' && str[pos] > 0)
981 *(dest++) = str[pos];
985 *(dest--) = '\0'; // store the null
987 // remove trailing blanks
988 while (dest >= str && *dest <= ' ' && *dest > 0)
992 // lookup table for crc calculation
993 static unsigned short crctab[256] = {
994 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
995 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
996 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
997 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
998 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
999 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
1000 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
1001 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
1002 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
1003 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
1004 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
1005 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
1006 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
1007 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
1008 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
1009 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
1010 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
1011 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
1012 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
1013 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
1014 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
1015 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
1016 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
1017 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
1018 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
1019 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
1020 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
1021 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
1022 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
1025 u16 calcCrc(const u8 *d, int len) {
1029 for (i = 0; i < len; i++) {
1030 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);
1036 #define MKSTR2(x) #x
1037 #define MKSTR(x) MKSTR2(x)
1038 const char *get_build_info(void)
1042 "cc " __VERSION__ " "
1044 #if defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 8
1046 #elif defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 4
1049 #if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1052 #if defined(__PIE__) || defined(__pie__)
1055 #if defined(__PIC__) || defined(__pic__)
1058 #if defined(__aarch64__)
1060 #elif defined(__arm__)
1064 "v" MKSTR(__ARM_ARCH) " "
1069 #if defined(__AVX__)
1071 #elif defined(__SSSE3__)
1073 #elif defined(__ARM_NEON) || defined(__ARM_NEON__)
1076 #if defined(__ARM_FEATURE_SVE) && __ARM_FEATURE_SVE
1079 #if defined(LIGHTREC)
1081 #elif !defined(DRC_DISABLE)
1084 "gpu=" MKSTR(BUILTIN_GPU);