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 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;
213 if (psxRegs.pc != 0x80030000) // BiosBootBypass'ed or custom BIOS?
219 time[0] = itob(0); time[1] = itob(2); time[2] = itob(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) {
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] = itob(0); time[1] = itob(2); time[2] = itob(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;
338 memcpy(head, buf + 12, sizeof(EXE_HEADER));
339 size = SWAP32(head->t_size);
340 addr = SWAP32(head->t_addr);
342 psxCpu->Clear(addr, size / 4);
345 while (size & ~2047) {
350 if (mem != INVALID_PTR)
351 memcpy(mem, buf + 12, 2048);
361 struct iso_directory_record *dir;
362 unsigned char time[4];
364 unsigned char mdir[4096];
372 time[2] = itob(0x10);
376 memset(CdromLabel, 0, sizeof(CdromLabel));
377 memset(CdromId, 0, sizeof(CdromId));
378 memset(exename, 0, sizeof(exename));
380 strncpy(CdromLabel, buf + 52, 32);
382 // skip head and sub, and go to the root directory record
383 dir = (struct iso_directory_record *)&buf[12 + 156];
385 mmssdd(dir->extent, (char *)time);
389 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
392 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
393 if (GetCdromFile(mdir, time, exename) == -1) {
394 sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
395 if (GetCdromFile(mdir, time, exename) == -1) {
396 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
399 while (*ptr == '\\' || *ptr == '/') ptr++;
400 strncpy(exename, ptr, 255);
403 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
405 if (GetCdromFile(mdir, time, exename) == -1)
406 return -1; // main executable not found
411 /* Workaround for Wild Arms EU/US which has non-standard string causing incorrect region detection */
412 if (exename[0] == 'E' && exename[1] == 'X' && exename[2] == 'E' && exename[3] == '\\') {
414 size_t i, len = strlen(exename) - offset;
415 for (i = 0; i < len; i++)
416 exename[i] = exename[i + offset];
419 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
420 strcpy(exename, "PSX.EXE;1");
421 strcpy(CdromId, "SLUS99999");
423 return -1; // SYSTEM.CNF and PSX.EXE not found
425 if (CdromId[0] == '\0') {
426 len = strlen(exename);
428 for (i = 0; i < len; ++i) {
429 if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
431 if (isalnum(exename[i]))
432 CdromId[c++] = exename[i];
436 if (CdromId[0] == '\0')
437 strcpy(CdromId, "SLUS99999");
439 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
441 /* Make sure Wild Arms SCUS-94608 is not detected as a PAL game. */
442 ((CdromId[0] == 's' || CdromId[0] == 'S') && (CdromId[2] == 'e' || CdromId[2] == 'E')) ||
443 !strncmp(CdromId, "DTLS3035", 8) ||
444 !strncmp(CdromId, "PBPX95001", 9) || // according to redump.org, these PAL
445 !strncmp(CdromId, "PBPX95007", 9) || // discs have a non-standard ID;
446 !strncmp(CdromId, "PBPX95008", 9)) // add more serials if they are discovered.
447 Config.PsxType = PSX_TYPE_PAL; // pal
448 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
451 if (CdromLabel[0] == ' ') {
452 strncpy(CdromLabel, CdromId, 9);
454 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
455 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
456 SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
465 static int PSXGetFileType(FILE *f) {
466 unsigned long current;
472 fseek(f, 0L, SEEK_SET);
473 if (fread(&mybuf, 1, sizeof(mybuf), f) != sizeof(mybuf))
476 fseek(f, current, SEEK_SET);
478 exe_hdr = (EXE_HEADER *)mybuf;
479 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
482 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
485 coff_hdr = (FILHDR *)mybuf;
486 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
493 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
498 // temporary pandora workaround..
500 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
505 tmp = malloc(size * nmemb);
507 ret = fread(tmp, size, nmemb, stream);
508 memcpy(ptr, tmp, size * nmemb);
514 int Load(const char *ExePath) {
520 u32 section_address, section_size;
523 strcpy(CdromId, "SLUS99999");
524 strcpy(CdromLabel, "SLUS_999.99");
526 tmpFile = fopen(ExePath, "rb");
527 if (tmpFile == NULL) {
528 SysPrintf(_("Error opening file: %s.\n"), ExePath);
531 type = PSXGetFileType(tmpFile);
534 if (fread(&tmpHead, 1, sizeof(EXE_HEADER), tmpFile) != sizeof(EXE_HEADER))
536 section_address = SWAP32(tmpHead.t_addr);
537 section_size = SWAP32(tmpHead.t_size);
538 mem = PSXM(section_address);
539 if (mem != INVALID_PTR) {
540 fseek(tmpFile, 0x800, SEEK_SET);
541 fread_to_ram(mem, section_size, 1, tmpFile);
542 psxCpu->Clear(section_address, section_size / 4);
544 SetBootRegs(SWAP32(tmpHead.pc0), SWAP32(tmpHead.gp0),
545 SWAP32(tmpHead.s_addr));
549 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
551 if (fread(&opcode, 1, sizeof(opcode), tmpFile) != sizeof(opcode))
554 case 1: /* Section loading */
555 if (fread(§ion_address, 1, sizeof(section_address), tmpFile) != sizeof(section_address))
557 if (fread(§ion_size, 1, sizeof(section_size), tmpFile) != sizeof(section_size))
559 section_address = SWAPu32(section_address);
560 section_size = SWAPu32(section_size);
562 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
564 mem = PSXM(section_address);
565 if (mem != INVALID_PTR) {
566 fread_to_ram(mem, section_size, 1, tmpFile);
567 psxCpu->Clear(section_address, section_size / 4);
570 case 3: /* register loading (PC only?) */
571 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
572 if (fread(&psxRegs.pc, 1, sizeof(psxRegs.pc), tmpFile) != sizeof(psxRegs.pc))
574 psxRegs.pc = SWAPu32(psxRegs.pc);
576 case 0: /* End of file */
579 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
583 } while (opcode != 0 && retval == 0);
586 SysPrintf(_("COFF files not supported.\n"));
590 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
591 SysPrintf(_("(did you forget -cdfile ?)\n"));
599 CdromLabel[0] = '\0';
608 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
616 static void *zlib_open(const char *name, const char *mode)
618 return gzopen(name, mode);
621 static int zlib_read(void *file, void *buf, u32 len)
623 return gzread(file, buf, len);
626 static int zlib_write(void *file, const void *buf, u32 len)
628 return gzwrite(file, buf, len);
631 static long zlib_seek(void *file, long offs, int whence)
633 return gzseek(file, offs, whence);
636 static void zlib_close(void *file)
641 struct PcsxSaveFuncs SaveFuncs = {
642 zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
645 static const char PcsxHeader[32] = "STv4 PCSX v" PCSX_VERSION;
647 // Savestate Versioning!
648 // If you make changes to the savestate version, please increment the value below.
649 static const u32 SaveVersion = 0x8b410006;
651 #define MISC_MAGIC 0x4353494d
652 struct misc_save_data {
664 int SaveState(const char *file) {
665 struct misc_save_data *misc = (void *)(psxH + 0xf000);
667 GPUFreeze_t *gpufP = NULL;
668 SPUFreezeHdr_t spufH;
669 SPUFreeze_t *spufP = NULL;
670 unsigned char *pMem = NULL;
674 assert(!psxRegs.branching);
675 assert(!psxRegs.cpuInRecursion);
676 assert(!misc->magic);
677 misc->magic = MISC_MAGIC;
678 misc->gteBusyCycle = psxRegs.gteBusyCycle;
679 misc->muldivBusyCycle = psxRegs.muldivBusyCycle;
680 misc->biuReg = psxRegs.biuReg;
681 misc->biosBranchCheck = psxRegs.biosBranchCheck;
682 misc->gpuIdleAfter = psxRegs.gpuIdleAfter;
683 misc->gpuSr = HW_GPU_STATUS;
684 misc->frame_counter = frame_counter;
685 misc->CdromFrontendId = CdromFrontendId;
687 f = SaveFuncs.open(file, "wb");
688 if (f == NULL) return -1;
690 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
692 SaveFuncs.write(f, (void *)PcsxHeader, 32);
693 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
694 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
696 pMem = (unsigned char *)malloc(128 * 96 * 3);
697 if (pMem == NULL) goto cleanup;
698 GPU_getScreenPic(pMem);
699 SaveFuncs.write(f, pMem, 128 * 96 * 3);
705 SaveFuncs.write(f, psxM, 0x00200000);
706 SaveFuncs.write(f, psxR, 0x00080000);
707 SaveFuncs.write(f, psxH, 0x00010000);
708 // only partial save of psxRegisters to maintain savestate compat
709 SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
712 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
713 if (gpufP == NULL) goto cleanup;
714 gpufP->ulFreezeVersion = 1;
715 GPU_freeze(1, gpufP);
716 SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
717 free(gpufP); gpufP = NULL;
720 SPU_freeze(2, (SPUFreeze_t *)&spufH, psxRegs.cycle);
721 Size = spufH.Size; SaveFuncs.write(f, &Size, 4);
722 spufP = (SPUFreeze_t *) malloc(Size);
723 if (spufP == NULL) goto cleanup;
724 SPU_freeze(1, spufP, psxRegs.cycle);
725 SaveFuncs.write(f, spufP, Size);
726 free(spufP); spufP = NULL;
733 new_dyna_freeze(f, 1);
738 memset(misc, 0, sizeof(*misc));
743 int LoadState(const char *file) {
744 struct misc_save_data *misc = (void *)(psxH + 0xf000);
745 u32 biosBranchCheckOld = psxRegs.biosBranchCheck;
747 GPUFreeze_t *gpufP = NULL;
748 SPUFreeze_t *spufP = NULL;
755 f = SaveFuncs.open(file, "rb");
756 if (f == NULL) return -1;
758 SaveFuncs.read(f, header, sizeof(header));
759 SaveFuncs.read(f, &version, sizeof(u32));
760 SaveFuncs.read(f, &hle, sizeof(boolean));
762 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
763 SysPrintf("incompatible savestate version %x\n", version);
771 SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
772 SaveFuncs.read(f, psxM, 0x00200000);
773 SaveFuncs.read(f, psxR, 0x00080000);
774 SaveFuncs.read(f, psxH, 0x00010000);
775 SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
776 psxRegs.gteBusyCycle = psxRegs.cycle;
777 psxRegs.biosBranchCheck = ~0;
778 psxRegs.gpuIdleAfter = psxRegs.cycle - 1;
779 HW_GPU_STATUS &= SWAP32(~PSXGPU_nBUSY);
780 if (misc->magic == MISC_MAGIC) {
781 psxRegs.gteBusyCycle = misc->gteBusyCycle;
782 psxRegs.muldivBusyCycle = misc->muldivBusyCycle;
783 psxRegs.biuReg = misc->biuReg;
784 psxRegs.biosBranchCheck = misc->biosBranchCheck;
785 psxRegs.gpuIdleAfter = misc->gpuIdleAfter;
786 HW_GPU_STATUS = misc->gpuSr;
787 frame_counter = misc->frame_counter;
788 CdromFrontendId = misc->CdromFrontendId;
791 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
797 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
798 if (gpufP == NULL) goto cleanup;
799 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
800 GPU_freeze(0, gpufP);
805 SaveFuncs.read(f, &Size, 4);
806 spufP = (SPUFreeze_t *)malloc(Size);
807 if (spufP == NULL) goto cleanup;
808 SaveFuncs.read(f, spufP, Size);
809 SPU_freeze(0, spufP, psxRegs.cycle);
817 new_dyna_freeze(f, 0);
822 psxBiosCheckExe(biosBranchCheckOld, 0x60, 1);
826 memset(misc, 0, sizeof(*misc));
831 int CheckState(const char *file) {
837 f = SaveFuncs.open(file, "rb");
838 if (f == NULL) return -1;
840 SaveFuncs.read(f, header, sizeof(header));
841 SaveFuncs.read(f, &version, sizeof(u32));
842 SaveFuncs.read(f, &hle, sizeof(boolean));
846 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
852 // NET Function Helpers
855 if (NET_recvData == NULL || NET_sendData == NULL)
859 boolean SpuIrq_old = 0;
860 boolean RCntFix_old = 0;
861 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
862 NET_sendData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
863 NET_sendData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
864 NET_sendData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
865 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
866 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
874 if (NET_recvData == NULL || NET_sendData == NULL)
878 boolean SpuIrq_old = 0;
879 boolean RCntFix_old = 0;
880 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
881 NET_recvData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
882 NET_recvData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
883 NET_recvData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
884 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
887 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
888 if (tmp != Config.Cpu) {
891 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
892 else psxCpu = &psxRec;
896 if (psxCpu->Init() == -1) {
897 SysClose(); return -1;
900 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
906 // remove the leading and trailing spaces in a string
907 void trim(char *str) {
911 // skip leading blanks
912 while (str[pos] <= ' ' && str[pos] > 0)
916 *(dest++) = str[pos];
920 *(dest--) = '\0'; // store the null
922 // remove trailing blanks
923 while (dest >= str && *dest <= ' ' && *dest > 0)
927 // lookup table for crc calculation
928 static unsigned short crctab[256] = {
929 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
930 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
931 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
932 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
933 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
934 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
935 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
936 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
937 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
938 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
939 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
940 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
941 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
942 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
943 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
944 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
945 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
946 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
947 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
948 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
949 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
950 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
951 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
952 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
953 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
954 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
955 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
956 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
957 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
960 u16 calcCrc(u8 *d, int len) {
964 for (i = 0; i < len; i++) {
965 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);