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.
36 char CdromId[10] = "";
37 char CdromLabel[33] = "";
38 int CdromFrontendId; // for frontend use
40 // PSX Executable types
46 #define ISODCL(from, to) (to - from + 1)
48 struct iso_directory_record {
49 char length [ISODCL (1, 1)]; /* 711 */
50 char ext_attr_length [ISODCL (2, 2)]; /* 711 */
51 char extent [ISODCL (3, 10)]; /* 733 */
52 char size [ISODCL (11, 18)]; /* 733 */
53 char date [ISODCL (19, 25)]; /* 7 by 711 */
54 char flags [ISODCL (26, 26)];
55 char file_unit_size [ISODCL (27, 27)]; /* 711 */
56 char interleave [ISODCL (28, 28)]; /* 711 */
57 char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
58 unsigned char name_len [ISODCL (33, 33)]; /* 711 */
62 static void mmssdd( char *b, char *p )
65 unsigned char *ub = (void *)b;
66 int block = (ub[3] << 24) | (ub[2] << 16) | (ub[1] << 8) | ub[0];
69 m = block / 4500; // minutes
70 block = block - m * 4500; // minutes rest
71 s = block / 75; // seconds
72 d = block - s * 75; // seconds rest
74 m = ((m / 10) << 4) | m % 10;
75 s = ((s / 10) << 4) | s % 10;
76 d = ((d / 10) << 4) | d % 10;
84 time[0] = btoi(time[0]); time[1] = btoi(time[1]); time[2] = btoi(time[2]); \
89 if (time[1] == 60) { \
94 time[0] = itob(time[0]); time[1] = itob(time[1]); time[2] = itob(time[2]);
97 if (!CDR_readTrack(time)) return -1; \
98 buf = (void *)CDR_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 void BiosBootBypass() {
167 assert(psxRegs.pc == 0x80030000);
169 // skip BIOS logos and region check
170 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
171 psxRegs.pc = psxRegs.GPR.n.ra;
174 static void getFromCnf(char *buf, const char *key, u32 *val)
176 buf = strstr(buf, key);
178 buf = strchr(buf, '=');
182 v = strtoul(buf + 1, NULL, 16);
191 u32 d[sizeof(EXE_HEADER) / sizeof(u32)];
193 struct iso_directory_record *dir;
206 if (psxRegs.pc != 0x80030000) // BiosBootBypass'ed or custom BIOS?
212 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
216 // skip head and sub, and go to the root directory record
217 dir = (struct iso_directory_record*) &buf[12+156];
219 mmssdd(dir->extent, (char*)time);
223 // Load SYSTEM.CNF and scan for the main executable
224 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
225 // if SYSTEM.CNF is missing, start an existing PSX.EXE
226 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
227 strcpy(exename, "PSX.EXE;1");
232 // read the SYSTEM.CNF
236 ret = sscanf((char *)buf + 12, "BOOT = cdrom:\\%255s", exename);
237 if (ret < 1 || GetCdromFile(mdir, time, exename) == -1) {
238 ret = sscanf((char *)buf + 12, "BOOT = cdrom:%255s", exename);
239 if (ret < 1 || GetCdromFile(mdir, time, exename) == -1) {
240 char *ptr = strstr((char *)buf + 12, "cdrom:");
243 while (*ptr == '\\' || *ptr == '/') ptr++;
244 strncpy(exename, ptr, 255);
247 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
249 if (GetCdromFile(mdir, time, exename) == -1)
255 getFromCnf((char *)buf + 12, "TCB", &cnf_tcb);
256 getFromCnf((char *)buf + 12, "EVENT", &cnf_event);
257 getFromCnf((char *)buf + 12, "STACK", &cnf_stack);
259 psxBiosCnfLoaded(cnf_tcb, cnf_event, cnf_stack);
261 // Read the EXE-Header
265 memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
266 for (i = 2; i < sizeof(tmpHead.d) / sizeof(tmpHead.d[0]); i++)
267 tmpHead.d[i] = SWAP32(tmpHead.d[i]);
269 SysPrintf("manual booting '%s' pc=%x\n", exename, tmpHead.h.pc0);
270 sp = tmpHead.h.s_addr;
273 SetBootRegs(tmpHead.h.pc0, tmpHead.h.gp0, sp);
275 // Read the rest of the main executable
276 for (t_addr = tmpHead.h.t_addr, t_size = tmpHead.h.t_size; t_size & ~2047; ) {
277 void *ptr = (void *)PSXM(t_addr);
282 if (ptr != INVALID_PTR) memcpy(ptr, buf+12, 2048);
288 psxCpu->Clear(tmpHead.h.t_addr, tmpHead.h.t_size / 4);
292 psxBiosCheckExe(tmpHead.h.t_addr, tmpHead.h.t_size, 0);
297 int LoadCdromFile(const char *filename, EXE_HEADER *head) {
298 struct iso_directory_record *dir;
306 if (filename == INVALID_PTR)
310 if ((p2 = strchr(p1, ':')))
314 snprintf(exename, sizeof(exename), "%s", p1);
316 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
320 // skip head and sub, and go to the root directory record
321 dir = (struct iso_directory_record *)&buf[12 + 156];
323 mmssdd(dir->extent, (char*)time);
327 if (GetCdromFile(mdir, time, exename) == -1) return -1;
331 memcpy(head, buf + 12, sizeof(EXE_HEADER));
332 size = SWAP32(head->t_size);
333 addr = SWAP32(head->t_addr);
335 psxCpu->Clear(addr, size / 4);
338 while (size & ~2047) {
343 if (mem != INVALID_PTR)
344 memcpy(mem, buf + 12, 2048);
354 struct iso_directory_record *dir;
355 unsigned char time[4];
357 unsigned char mdir[4096];
365 time[2] = itob(0x10);
369 memset(CdromLabel, 0, sizeof(CdromLabel));
370 memset(CdromId, 0, sizeof(CdromId));
371 memset(exename, 0, sizeof(exename));
373 strncpy(CdromLabel, buf + 52, 32);
375 // skip head and sub, and go to the root directory record
376 dir = (struct iso_directory_record *)&buf[12 + 156];
378 mmssdd(dir->extent, (char *)time);
382 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
385 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
386 if (GetCdromFile(mdir, time, exename) == -1) {
387 sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
388 if (GetCdromFile(mdir, time, exename) == -1) {
389 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
392 while (*ptr == '\\' || *ptr == '/') ptr++;
393 strncpy(exename, ptr, 255);
396 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
398 if (GetCdromFile(mdir, time, exename) == -1)
399 return -1; // main executable not found
404 /* Workaround for Wild Arms EU/US which has non-standard string causing incorrect region detection */
405 if (exename[0] == 'E' && exename[1] == 'X' && exename[2] == 'E' && exename[3] == '\\') {
407 size_t i, len = strlen(exename) - offset;
408 for (i = 0; i < len; i++)
409 exename[i] = exename[i + offset];
412 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
413 strcpy(exename, "PSX.EXE;1");
414 strcpy(CdromId, "SLUS99999");
416 return -1; // SYSTEM.CNF and PSX.EXE not found
418 if (CdromId[0] == '\0') {
419 len = strlen(exename);
421 for (i = 0; i < len; ++i) {
422 if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
424 if (isalnum(exename[i]))
425 CdromId[c++] = exename[i];
429 if (CdromId[0] == '\0')
430 strcpy(CdromId, "SLUS99999");
432 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
434 /* Make sure Wild Arms SCUS-94608 is not detected as a PAL game. */
435 ((CdromId[0] == 's' || CdromId[0] == 'S') && (CdromId[2] == 'e' || CdromId[2] == 'E')) ||
436 !strncmp(CdromId, "DTLS3035", 8) ||
437 !strncmp(CdromId, "PBPX95001", 9) || // according to redump.org, these PAL
438 !strncmp(CdromId, "PBPX95007", 9) || // discs have a non-standard ID;
439 !strncmp(CdromId, "PBPX95008", 9)) // add more serials if they are discovered.
440 Config.PsxType = PSX_TYPE_PAL; // pal
441 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
444 if (CdromLabel[0] == ' ') {
445 strncpy(CdromLabel, CdromId, 9);
447 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
448 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
449 SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
458 static int PSXGetFileType(FILE *f) {
459 unsigned long current;
465 fseek(f, 0L, SEEK_SET);
466 if (fread(&mybuf, 1, sizeof(mybuf), f) != sizeof(mybuf))
469 fseek(f, current, SEEK_SET);
471 exe_hdr = (EXE_HEADER *)mybuf;
472 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
475 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
478 coff_hdr = (FILHDR *)mybuf;
479 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
486 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
491 // temporary pandora workaround..
493 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
498 tmp = malloc(size * nmemb);
500 ret = fread(tmp, size, nmemb, stream);
501 memcpy(ptr, tmp, size * nmemb);
507 int Load(const char *ExePath) {
513 u32 section_address, section_size;
516 strcpy(CdromId, "SLUS99999");
517 strcpy(CdromLabel, "SLUS_999.99");
519 tmpFile = fopen(ExePath, "rb");
520 if (tmpFile == NULL) {
521 SysPrintf(_("Error opening file: %s.\n"), ExePath);
524 type = PSXGetFileType(tmpFile);
527 if (fread(&tmpHead, 1, sizeof(EXE_HEADER), tmpFile) != sizeof(EXE_HEADER))
529 section_address = SWAP32(tmpHead.t_addr);
530 section_size = SWAP32(tmpHead.t_size);
531 mem = PSXM(section_address);
532 if (mem != INVALID_PTR) {
533 fseek(tmpFile, 0x800, SEEK_SET);
534 fread_to_ram(mem, section_size, 1, tmpFile);
535 psxCpu->Clear(section_address, section_size / 4);
537 SetBootRegs(SWAP32(tmpHead.pc0), SWAP32(tmpHead.gp0),
538 SWAP32(tmpHead.s_addr));
542 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
544 if (fread(&opcode, 1, sizeof(opcode), tmpFile) != sizeof(opcode))
547 case 1: /* Section loading */
548 if (fread(§ion_address, 1, sizeof(section_address), tmpFile) != sizeof(section_address))
550 if (fread(§ion_size, 1, sizeof(section_size), tmpFile) != sizeof(section_size))
552 section_address = SWAPu32(section_address);
553 section_size = SWAPu32(section_size);
555 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
557 mem = PSXM(section_address);
558 if (mem != INVALID_PTR) {
559 fread_to_ram(mem, section_size, 1, tmpFile);
560 psxCpu->Clear(section_address, section_size / 4);
563 case 3: /* register loading (PC only?) */
564 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
565 if (fread(&psxRegs.pc, 1, sizeof(psxRegs.pc), tmpFile) != sizeof(psxRegs.pc))
567 psxRegs.pc = SWAPu32(psxRegs.pc);
569 case 0: /* End of file */
572 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
576 } while (opcode != 0 && retval == 0);
579 SysPrintf(_("COFF files not supported.\n"));
583 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
584 SysPrintf(_("(did you forget -cdfile ?)\n"));
592 CdromLabel[0] = '\0';
601 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
609 static void *zlib_open(const char *name, const char *mode)
611 return gzopen(name, mode);
614 static int zlib_read(void *file, void *buf, u32 len)
616 return gzread(file, buf, len);
619 static int zlib_write(void *file, const void *buf, u32 len)
621 return gzwrite(file, buf, len);
624 static long zlib_seek(void *file, long offs, int whence)
626 return gzseek(file, offs, whence);
629 static void zlib_close(void *file)
634 struct PcsxSaveFuncs SaveFuncs = {
635 zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
638 static const char PcsxHeader[32] = "STv4 PCSX v" PCSX_VERSION;
640 // Savestate Versioning!
641 // If you make changes to the savestate version, please increment the value below.
642 static const u32 SaveVersion = 0x8b410006;
644 #define MISC_MAGIC 0x4353494d
645 struct misc_save_data {
657 int SaveState(const char *file) {
658 struct misc_save_data *misc = (void *)(psxH + 0xf000);
660 GPUFreeze_t *gpufP = NULL;
661 SPUFreezeHdr_t spufH;
662 SPUFreeze_t *spufP = NULL;
663 unsigned char *pMem = NULL;
667 assert(!psxRegs.branching);
668 assert(!psxRegs.cpuInRecursion);
669 assert(!misc->magic);
670 misc->magic = MISC_MAGIC;
671 misc->gteBusyCycle = psxRegs.gteBusyCycle;
672 misc->muldivBusyCycle = psxRegs.muldivBusyCycle;
673 misc->biuReg = psxRegs.biuReg;
674 misc->biosBranchCheck = psxRegs.biosBranchCheck;
675 misc->gpuIdleAfter = psxRegs.gpuIdleAfter;
676 misc->gpuSr = HW_GPU_STATUS;
677 misc->frame_counter = frame_counter;
678 misc->CdromFrontendId = CdromFrontendId;
680 f = SaveFuncs.open(file, "wb");
681 if (f == NULL) return -1;
683 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
685 SaveFuncs.write(f, (void *)PcsxHeader, 32);
686 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
687 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
689 pMem = (unsigned char *)malloc(128 * 96 * 3);
690 if (pMem == NULL) goto cleanup;
691 GPU_getScreenPic(pMem);
692 SaveFuncs.write(f, pMem, 128 * 96 * 3);
698 SaveFuncs.write(f, psxM, 0x00200000);
699 SaveFuncs.write(f, psxR, 0x00080000);
700 SaveFuncs.write(f, psxH, 0x00010000);
701 // only partial save of psxRegisters to maintain savestate compat
702 SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
705 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
706 if (gpufP == NULL) goto cleanup;
707 gpufP->ulFreezeVersion = 1;
708 GPU_freeze(1, gpufP);
709 SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
710 free(gpufP); gpufP = NULL;
713 SPU_freeze(2, (SPUFreeze_t *)&spufH, psxRegs.cycle);
714 Size = spufH.Size; SaveFuncs.write(f, &Size, 4);
715 spufP = (SPUFreeze_t *) malloc(Size);
716 if (spufP == NULL) goto cleanup;
717 SPU_freeze(1, spufP, psxRegs.cycle);
718 SaveFuncs.write(f, spufP, Size);
719 free(spufP); spufP = NULL;
726 new_dyna_freeze(f, 1);
731 memset(misc, 0, sizeof(*misc));
736 int LoadState(const char *file) {
737 struct misc_save_data *misc = (void *)(psxH + 0xf000);
738 u32 biosBranchCheckOld = psxRegs.biosBranchCheck;
740 GPUFreeze_t *gpufP = NULL;
741 SPUFreeze_t *spufP = NULL;
748 f = SaveFuncs.open(file, "rb");
749 if (f == NULL) return -1;
751 SaveFuncs.read(f, header, sizeof(header));
752 SaveFuncs.read(f, &version, sizeof(u32));
753 SaveFuncs.read(f, &hle, sizeof(boolean));
755 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
756 SysPrintf("incompatible savestate version %x\n", version);
764 SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
765 SaveFuncs.read(f, psxM, 0x00200000);
766 SaveFuncs.read(f, psxR, 0x00080000);
767 SaveFuncs.read(f, psxH, 0x00010000);
768 SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
769 psxRegs.gteBusyCycle = psxRegs.cycle;
770 psxRegs.biosBranchCheck = ~0;
771 psxRegs.gpuIdleAfter = psxRegs.cycle - 1;
772 HW_GPU_STATUS &= SWAP32(~PSXGPU_nBUSY);
773 if (misc->magic == MISC_MAGIC) {
774 psxRegs.gteBusyCycle = misc->gteBusyCycle;
775 psxRegs.muldivBusyCycle = misc->muldivBusyCycle;
776 psxRegs.biuReg = misc->biuReg;
777 psxRegs.biosBranchCheck = misc->biosBranchCheck;
778 psxRegs.gpuIdleAfter = misc->gpuIdleAfter;
779 HW_GPU_STATUS = misc->gpuSr;
780 frame_counter = misc->frame_counter;
781 CdromFrontendId = misc->CdromFrontendId;
784 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
790 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
791 if (gpufP == NULL) goto cleanup;
792 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
793 GPU_freeze(0, gpufP);
798 SaveFuncs.read(f, &Size, 4);
799 spufP = (SPUFreeze_t *)malloc(Size);
800 if (spufP == NULL) goto cleanup;
801 SaveFuncs.read(f, spufP, Size);
802 SPU_freeze(0, spufP, psxRegs.cycle);
810 new_dyna_freeze(f, 0);
815 psxBiosCheckExe(biosBranchCheckOld, 0x60, 1);
819 memset(misc, 0, sizeof(*misc));
824 int CheckState(const char *file) {
830 f = SaveFuncs.open(file, "rb");
831 if (f == NULL) return -1;
833 SaveFuncs.read(f, header, sizeof(header));
834 SaveFuncs.read(f, &version, sizeof(u32));
835 SaveFuncs.read(f, &hle, sizeof(boolean));
839 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
845 // NET Function Helpers
848 if (NET_recvData == NULL || NET_sendData == NULL)
852 boolean SpuIrq_old = 0;
853 boolean RCntFix_old = 0;
854 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
855 NET_sendData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
856 NET_sendData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
857 NET_sendData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
858 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
859 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
867 if (NET_recvData == NULL || NET_sendData == NULL)
871 boolean SpuIrq_old = 0;
872 boolean RCntFix_old = 0;
873 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
874 NET_recvData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
875 NET_recvData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
876 NET_recvData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
877 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
880 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
881 if (tmp != Config.Cpu) {
884 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
885 else psxCpu = &psxRec;
889 if (psxCpu->Init() == -1) {
890 SysClose(); return -1;
893 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
899 // remove the leading and trailing spaces in a string
900 void trim(char *str) {
904 // skip leading blanks
905 while (str[pos] <= ' ' && str[pos] > 0)
909 *(dest++) = str[pos];
913 *(dest--) = '\0'; // store the null
915 // remove trailing blanks
916 while (dest >= str && *dest <= ' ' && *dest > 0)
920 // lookup table for crc calculation
921 static unsigned short crctab[256] = {
922 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
923 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
924 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
925 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
926 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
927 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
928 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
929 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
930 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
931 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
932 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
933 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
934 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
935 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
936 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
937 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
938 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
939 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
940 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
941 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
942 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
943 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
944 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
945 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
946 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
947 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
948 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
949 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
950 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
953 u16 calcCrc(u8 *d, int len) {
957 for (i = 0; i < len; i++) {
958 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);