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, 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] = 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;
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 memcpy(time_bcd_out, time, 3);
364 struct iso_directory_record *dir;
365 struct CdrStat stat = { 0, 0, };
366 unsigned char time[4];
368 unsigned char mdir[4096];
373 memset(CdromLabel, 0, sizeof(CdromLabel));
374 memset(CdromId, 0, sizeof(CdromId));
375 memset(exename, 0, sizeof(exename));
379 time[2] = itob(0x10);
381 if (!Config.HLE && Config.SlowBoot) {
382 // boot to BIOS in case of CDDA or lid is open
383 CDR_getStatus(&stat);
384 if ((stat.Status & 0x10) || stat.Type == 2 || !CDR_readTrack(time))
389 strncpy(CdromLabel, buf + 52, 32);
391 // skip head and sub, and go to the root directory record
392 dir = (struct iso_directory_record *)&buf[12 + 156];
394 mmssdd(dir->extent, (char *)time);
398 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
401 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
402 if (GetCdromFile(mdir, time, exename) == -1) {
403 sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
404 if (GetCdromFile(mdir, time, exename) == -1) {
405 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
408 while (*ptr == '\\' || *ptr == '/') ptr++;
409 strncpy(exename, ptr, 255);
412 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
414 if (GetCdromFile(mdir, time, exename) == -1)
415 return -1; // main executable not found
420 /* Workaround for Wild Arms EU/US which has non-standard string causing incorrect region detection */
421 if (exename[0] == 'E' && exename[1] == 'X' && exename[2] == 'E' && exename[3] == '\\') {
423 size_t i, len = strlen(exename) - offset;
424 for (i = 0; i < len; i++)
425 exename[i] = exename[i + offset];
428 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
429 strcpy(exename, "PSX.EXE;1");
430 strcpy(CdromId, "SLUS99999");
432 return -1; // SYSTEM.CNF and PSX.EXE not found
434 if (CdromId[0] == '\0') {
435 len = strlen(exename);
437 for (i = 0; i < len; ++i) {
438 if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
440 if (isalnum(exename[i]))
441 CdromId[c++] = exename[i];
445 if (CdromId[0] == '\0')
446 strcpy(CdromId, "SLUS99999");
448 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
450 /* Make sure Wild Arms SCUS-94608 is not detected as a PAL game. */
451 ((CdromId[0] == 's' || CdromId[0] == 'S') && (CdromId[2] == 'e' || CdromId[2] == 'E')) ||
452 !strncmp(CdromId, "DTLS3035", 8) ||
453 !strncmp(CdromId, "PBPX95001", 9) || // according to redump.org, these PAL
454 !strncmp(CdromId, "PBPX95007", 9) || // discs have a non-standard ID;
455 !strncmp(CdromId, "PBPX95008", 9)) // add more serials if they are discovered.
456 Config.PsxType = PSX_TYPE_PAL; // pal
457 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
460 if (CdromLabel[0] == ' ') {
461 strncpy(CdromLabel, CdromId, 9);
463 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
464 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
465 SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
474 static int PSXGetFileType(FILE *f) {
475 unsigned long current;
481 fseek(f, 0L, SEEK_SET);
482 if (fread(&mybuf, 1, sizeof(mybuf), f) != sizeof(mybuf))
485 fseek(f, current, SEEK_SET);
487 exe_hdr = (EXE_HEADER *)mybuf;
488 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
491 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
494 coff_hdr = (FILHDR *)mybuf;
495 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
502 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
507 // temporary pandora workaround..
509 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
514 tmp = malloc(size * nmemb);
516 ret = fread(tmp, size, nmemb, stream);
517 memcpy(ptr, tmp, size * nmemb);
523 int Load(const char *ExePath) {
529 u32 section_address, section_size;
532 strcpy(CdromId, "SLUS99999");
533 strcpy(CdromLabel, "SLUS_999.99");
535 tmpFile = fopen(ExePath, "rb");
536 if (tmpFile == NULL) {
537 SysPrintf(_("Error opening file: %s.\n"), ExePath);
540 type = PSXGetFileType(tmpFile);
543 if (fread(&tmpHead, 1, sizeof(EXE_HEADER), tmpFile) != sizeof(EXE_HEADER))
545 section_address = SWAP32(tmpHead.t_addr);
546 section_size = SWAP32(tmpHead.t_size);
547 mem = PSXM(section_address);
548 if (mem != INVALID_PTR) {
549 fseek(tmpFile, 0x800, SEEK_SET);
550 fread_to_ram(mem, section_size, 1, tmpFile);
551 psxCpu->Clear(section_address, section_size / 4);
553 SetBootRegs(SWAP32(tmpHead.pc0), SWAP32(tmpHead.gp0),
554 SWAP32(tmpHead.s_addr));
558 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
560 if (fread(&opcode, 1, sizeof(opcode), tmpFile) != sizeof(opcode))
563 case 1: /* Section loading */
564 if (fread(§ion_address, 1, sizeof(section_address), tmpFile) != sizeof(section_address))
566 if (fread(§ion_size, 1, sizeof(section_size), tmpFile) != sizeof(section_size))
568 section_address = SWAPu32(section_address);
569 section_size = SWAPu32(section_size);
571 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
573 mem = PSXM(section_address);
574 if (mem != INVALID_PTR) {
575 fread_to_ram(mem, section_size, 1, tmpFile);
576 psxCpu->Clear(section_address, section_size / 4);
579 case 3: /* register loading (PC only?) */
580 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
581 if (fread(&psxRegs.pc, 1, sizeof(psxRegs.pc), tmpFile) != sizeof(psxRegs.pc))
583 psxRegs.pc = SWAPu32(psxRegs.pc);
585 case 0: /* End of file */
588 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
592 } while (opcode != 0 && retval == 0);
595 SysPrintf(_("COFF files not supported.\n"));
599 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
600 SysPrintf(_("(did you forget -cdfile ?)\n"));
608 CdromLabel[0] = '\0';
617 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
625 static void *zlib_open(const char *name, const char *mode)
627 return gzopen(name, mode);
630 static int zlib_read(void *file, void *buf, u32 len)
632 return gzread(file, buf, len);
635 static int zlib_write(void *file, const void *buf, u32 len)
637 return gzwrite(file, buf, len);
640 static long zlib_seek(void *file, long offs, int whence)
642 return gzseek(file, offs, whence);
645 static void zlib_close(void *file)
650 struct PcsxSaveFuncs SaveFuncs = {
651 zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
654 static const char PcsxHeader[32] = "STv4 PCSX v" PCSX_VERSION;
656 // Savestate Versioning!
657 // If you make changes to the savestate version, please increment the value below.
658 static const u32 SaveVersion = 0x8b410006;
660 #define MISC_MAGIC 0x4353494d
661 struct misc_save_data {
673 int SaveState(const char *file) {
674 struct misc_save_data *misc = (void *)(psxH + 0xf000);
676 GPUFreeze_t *gpufP = NULL;
677 SPUFreezeHdr_t spufH;
678 SPUFreeze_t *spufP = NULL;
679 unsigned char *pMem = NULL;
683 assert(!psxRegs.branching);
684 assert(!psxRegs.cpuInRecursion);
685 assert(!misc->magic);
687 f = SaveFuncs.open(file, "wb");
688 if (f == NULL) return -1;
690 misc->magic = MISC_MAGIC;
691 misc->gteBusyCycle = psxRegs.gteBusyCycle;
692 misc->muldivBusyCycle = psxRegs.muldivBusyCycle;
693 misc->biuReg = psxRegs.biuReg;
694 misc->biosBranchCheck = psxRegs.biosBranchCheck;
695 misc->gpuIdleAfter = psxRegs.gpuIdleAfter;
696 misc->gpuSr = HW_GPU_STATUS;
697 misc->frame_counter = frame_counter;
698 misc->CdromFrontendId = CdromFrontendId;
700 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
702 SaveFuncs.write(f, (void *)PcsxHeader, 32);
703 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
704 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
706 pMem = (unsigned char *)malloc(128 * 96 * 3);
707 if (pMem == NULL) goto cleanup;
708 GPU_getScreenPic(pMem);
709 SaveFuncs.write(f, pMem, 128 * 96 * 3);
715 SaveFuncs.write(f, psxM, 0x00200000);
716 SaveFuncs.write(f, psxR, 0x00080000);
717 SaveFuncs.write(f, psxH, 0x00010000);
718 // only partial save of psxRegisters to maintain savestate compat
719 SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
722 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
723 if (gpufP == NULL) goto cleanup;
724 gpufP->ulFreezeVersion = 1;
725 GPU_freeze(1, gpufP);
726 SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
727 free(gpufP); gpufP = NULL;
730 SPU_freeze(2, (SPUFreeze_t *)&spufH, psxRegs.cycle);
731 Size = spufH.Size; SaveFuncs.write(f, &Size, 4);
732 spufP = (SPUFreeze_t *) malloc(Size);
733 if (spufP == NULL) goto cleanup;
734 SPU_freeze(1, spufP, psxRegs.cycle);
735 SaveFuncs.write(f, spufP, Size);
736 free(spufP); spufP = NULL;
743 new_dyna_freeze(f, 1);
748 memset(misc, 0, sizeof(*misc));
753 int LoadState(const char *file) {
754 struct misc_save_data *misc = (void *)(psxH + 0xf000);
755 u32 biosBranchCheckOld = psxRegs.biosBranchCheck;
757 GPUFreeze_t *gpufP = NULL;
758 SPUFreeze_t *spufP = NULL;
765 f = SaveFuncs.open(file, "rb");
766 if (f == NULL) return -1;
768 SaveFuncs.read(f, header, sizeof(header));
769 SaveFuncs.read(f, &version, sizeof(u32));
770 SaveFuncs.read(f, &hle, sizeof(boolean));
772 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
773 SysPrintf("incompatible savestate version %x\n", version);
781 SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
782 SaveFuncs.read(f, psxM, 0x00200000);
783 SaveFuncs.read(f, psxR, 0x00080000);
784 SaveFuncs.read(f, psxH, 0x00010000);
785 SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
786 psxRegs.gteBusyCycle = psxRegs.cycle;
787 psxRegs.biosBranchCheck = ~0;
788 psxRegs.gpuIdleAfter = psxRegs.cycle - 1;
789 HW_GPU_STATUS &= SWAP32(~PSXGPU_nBUSY);
790 if (misc->magic == MISC_MAGIC) {
791 psxRegs.gteBusyCycle = misc->gteBusyCycle;
792 psxRegs.muldivBusyCycle = misc->muldivBusyCycle;
793 psxRegs.biuReg = misc->biuReg;
794 psxRegs.biosBranchCheck = misc->biosBranchCheck;
795 psxRegs.gpuIdleAfter = misc->gpuIdleAfter;
796 HW_GPU_STATUS = misc->gpuSr;
797 frame_counter = misc->frame_counter;
798 CdromFrontendId = misc->CdromFrontendId;
801 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
807 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
808 if (gpufP == NULL) goto cleanup;
809 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
810 GPU_freeze(0, gpufP);
815 SaveFuncs.read(f, &Size, 4);
816 spufP = (SPUFreeze_t *)malloc(Size);
817 if (spufP == NULL) goto cleanup;
818 SaveFuncs.read(f, spufP, Size);
819 SPU_freeze(0, spufP, psxRegs.cycle);
827 new_dyna_freeze(f, 0);
832 psxBiosCheckExe(biosBranchCheckOld, 0x60, 1);
836 memset(misc, 0, sizeof(*misc));
841 int CheckState(const char *file) {
847 f = SaveFuncs.open(file, "rb");
848 if (f == NULL) return -1;
850 SaveFuncs.read(f, header, sizeof(header));
851 SaveFuncs.read(f, &version, sizeof(u32));
852 SaveFuncs.read(f, &hle, sizeof(boolean));
856 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
862 // NET Function Helpers
865 if (NET_recvData == NULL || NET_sendData == NULL)
869 boolean SpuIrq_old = 0;
870 boolean RCntFix_old = 0;
871 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
872 NET_sendData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
873 NET_sendData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
874 NET_sendData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
875 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
876 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
884 if (NET_recvData == NULL || NET_sendData == NULL)
888 boolean SpuIrq_old = 0;
889 boolean RCntFix_old = 0;
890 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
891 NET_recvData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
892 NET_recvData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
893 NET_recvData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
894 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
897 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
898 if (tmp != Config.Cpu) {
901 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
902 else psxCpu = &psxRec;
906 if (psxCpu->Init() == -1) {
907 SysClose(); return -1;
910 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
916 // remove the leading and trailing spaces in a string
917 void trim(char *str) {
921 // skip leading blanks
922 while (str[pos] <= ' ' && str[pos] > 0)
926 *(dest++) = str[pos];
930 *(dest--) = '\0'; // store the null
932 // remove trailing blanks
933 while (dest >= str && *dest <= ' ' && *dest > 0)
937 // lookup table for crc calculation
938 static unsigned short crctab[256] = {
939 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
940 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
941 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
942 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
943 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
944 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
945 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
946 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
947 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
948 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
949 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
950 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
951 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
952 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
953 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
954 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
955 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
956 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
957 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
958 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
959 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
960 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
961 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
962 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
963 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
964 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
965 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
966 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
967 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
970 u16 calcCrc(u8 *d, int len) {
974 for (i = 0; i < len; i++) {
975 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);