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 char CdromId[10] = "";
32 char CdromLabel[33] = "";
34 // PSX Executable types
40 #define ISODCL(from, to) (to - from + 1)
42 struct iso_directory_record {
43 char length [ISODCL (1, 1)]; /* 711 */
44 char ext_attr_length [ISODCL (2, 2)]; /* 711 */
45 char extent [ISODCL (3, 10)]; /* 733 */
46 char size [ISODCL (11, 18)]; /* 733 */
47 char date [ISODCL (19, 25)]; /* 7 by 711 */
48 char flags [ISODCL (26, 26)];
49 char file_unit_size [ISODCL (27, 27)]; /* 711 */
50 char interleave [ISODCL (28, 28)]; /* 711 */
51 char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
52 unsigned char name_len [ISODCL (33, 33)]; /* 711 */
56 void mmssdd( char *b, char *p )
60 unsigned char *u = (void *)b;
61 int block = (u[3] << 24) | (u[2] << 16) | (u[1] << 8) | u[0];
62 #elif defined(__BIGENDIAN__)
63 int block = (b[0] & 0xff) | ((b[1] & 0xff) << 8) | ((b[2] & 0xff) << 16) | (b[3] << 24);
65 int block = *((int*)b);
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) == -1) 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 (!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 const unsigned int gpu_ctl_def[] = {
151 0x00000000, 0x01000000, 0x03000000, 0x04000000,
152 0x05000800, 0x06c60260, 0x0703fc10, 0x08000027,
155 static const unsigned int gpu_data_def[] = {
156 0xe100360b, 0xe2000000, 0xe3000800, 0xe4077e7f,
157 0xe5001000, 0xe6000000,
158 0x02000000, 0x00000000, 0x01ff03ff,
161 static void fake_bios_gpu_setup(void)
165 for (i = 0; i < sizeof(gpu_ctl_def) / sizeof(gpu_ctl_def[0]); i++)
166 GPU_writeStatus(gpu_ctl_def[i]);
168 for (i = 0; i < sizeof(gpu_data_def) / sizeof(gpu_data_def[0]); i++)
169 GPU_writeData(gpu_data_def[i]);
174 struct iso_directory_record *dir;
179 // not the best place to do it, but since BIOS boot logo killer
180 // is just below, do it here
181 fake_bios_gpu_setup();
185 psxRegs.pc = psxRegs.GPR.n.ra;
189 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
193 // skip head and sub, and go to the root directory record
194 dir = (struct iso_directory_record*) &buf[12+156];
196 mmssdd(dir->extent, (char*)time);
200 // Load SYSTEM.CNF and scan for the main executable
201 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
202 // if SYSTEM.CNF is missing, start an existing PSX.EXE
203 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
208 // read the SYSTEM.CNF
211 sscanf((char *)buf + 12, "BOOT = cdrom:\\%256s", exename);
212 if (GetCdromFile(mdir, time, exename) == -1) {
213 sscanf((char *)buf + 12, "BOOT = cdrom:%256s", exename);
214 if (GetCdromFile(mdir, time, exename) == -1) {
215 char *ptr = strstr((char *)buf + 12, "cdrom:");
218 while (*ptr == '\\' || *ptr == '/') ptr++;
219 strncpy(exename, ptr, 255);
222 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
224 if (GetCdromFile(mdir, time, exename) == -1)
231 // Read the EXE-Header
235 memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
237 psxRegs.pc = SWAP32(tmpHead.pc0);
238 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
239 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
240 if (psxRegs.GPR.n.sp == 0) psxRegs.GPR.n.sp = 0x801fff00;
242 tmpHead.t_size = SWAP32(tmpHead.t_size);
243 tmpHead.t_addr = SWAP32(tmpHead.t_addr);
245 psxCpu->Clear(tmpHead.t_addr, tmpHead.t_size / 4);
247 // Read the rest of the main executable
248 while (tmpHead.t_size & ~2047) {
249 void *ptr = (void *)PSXM(tmpHead.t_addr);
254 if (ptr != NULL) memcpy(ptr, buf+12, 2048);
256 tmpHead.t_size -= 2048;
257 tmpHead.t_addr += 2048;
263 int LoadCdromFile(const char *filename, EXE_HEADER *head) {
264 struct iso_directory_record *dir;
271 sscanf(filename, "cdrom:\\%256s", exename);
273 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
277 // skip head and sub, and go to the root directory record
278 dir = (struct iso_directory_record *)&buf[12 + 156];
280 mmssdd(dir->extent, (char*)time);
284 if (GetCdromFile(mdir, time, exename) == -1) return -1;
288 memcpy(head, buf + 12, sizeof(EXE_HEADER));
292 psxCpu->Clear(addr, size / 4);
294 while (size & ~2047) {
300 memcpy(mem, buf + 12, 2048);
310 struct iso_directory_record *dir;
311 unsigned char time[4];
313 unsigned char mdir[4096];
321 time[2] = itob(0x10);
325 CdromLabel[0] = '\0';
328 strncpy(CdromLabel, buf + 52, 32);
330 // skip head and sub, and go to the root directory record
331 dir = (struct iso_directory_record *)&buf[12 + 156];
333 mmssdd(dir->extent, (char *)time);
337 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
340 sscanf(buf + 12, "BOOT = cdrom:\\%256s", exename);
341 if (GetCdromFile(mdir, time, exename) == -1) {
342 sscanf(buf + 12, "BOOT = cdrom:%256s", exename);
343 if (GetCdromFile(mdir, time, exename) == -1) {
344 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
347 while (*ptr == '\\' || *ptr == '/') ptr++;
348 strncpy(exename, ptr, 255);
351 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
353 if (GetCdromFile(mdir, time, exename) == -1)
354 return -1; // main executable not found
359 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
360 strcpy(exename, "PSX.EXE;1");
361 strcpy(CdromId, "SLUS99999");
363 return -1; // SYSTEM.CNF and PSX.EXE not found
365 if (CdromId[0] == '\0') {
368 if (exename[i - 2] == ';') i-= 2;
370 while (i >= 0 && c >= 0) {
371 if (isalnum(exename[i])) CdromId[c--] = exename[i];
377 if (CdromId[0] == '\0')
378 strcpy(CdromId, "SLUS99999");
380 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
381 if (CdromId[2] == 'e' || CdromId[2] == 'E')
382 Config.PsxType = PSX_TYPE_PAL; // pal
383 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
386 if (CdromLabel[0] == ' ') {
387 strncpy(CdromLabel, CdromId, 9);
389 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
390 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
397 static int PSXGetFileType(FILE *f) {
398 unsigned long current;
404 fseek(f, 0L, SEEK_SET);
405 fread(mybuf, 2048, 1, f);
406 fseek(f, current, SEEK_SET);
408 exe_hdr = (EXE_HEADER *)mybuf;
409 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
412 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
415 coff_hdr = (FILHDR *)mybuf;
416 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
422 // temporary pandora workaround..
424 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
429 tmp = malloc(size * nmemb);
431 ret = fread(tmp, size, nmemb, stream);
432 memcpy(ptr, tmp, size * nmemb);
438 int Load(const char *ExePath) {
444 u32 section_address, section_size;
447 strncpy(CdromId, "SLUS99999", 9);
448 strncpy(CdromLabel, "SLUS_999.99", 11);
450 tmpFile = fopen(ExePath, "rb");
451 if (tmpFile == NULL) {
452 SysPrintf(_("Error opening file: %s.\n"), ExePath);
455 type = PSXGetFileType(tmpFile);
458 fread(&tmpHead,sizeof(EXE_HEADER),1,tmpFile);
459 section_address = SWAP32(tmpHead.t_addr);
460 section_size = SWAP32(tmpHead.t_size);
461 mem = PSXM(section_address);
463 fseek(tmpFile, 0x800, SEEK_SET);
464 fread_to_ram(mem, section_size, 1, tmpFile);
465 psxCpu->Clear(section_address, section_size / 4);
468 psxRegs.pc = SWAP32(tmpHead.pc0);
469 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
470 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
471 if (psxRegs.GPR.n.sp == 0)
472 psxRegs.GPR.n.sp = 0x801fff00;
476 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
478 fread(&opcode, 1, 1, tmpFile);
480 case 1: /* Section loading */
481 fread(§ion_address, 4, 1, tmpFile);
482 fread(§ion_size, 4, 1, tmpFile);
483 section_address = SWAPu32(section_address);
484 section_size = SWAPu32(section_size);
486 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
488 mem = PSXM(section_address);
490 fread_to_ram(mem, section_size, 1, tmpFile);
491 psxCpu->Clear(section_address, section_size / 4);
494 case 3: /* register loading (PC only?) */
495 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
496 fread(&psxRegs.pc, 4, 1, tmpFile);
497 psxRegs.pc = SWAPu32(psxRegs.pc);
499 case 0: /* End of file */
502 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
506 } while (opcode != 0 && retval == 0);
509 SysPrintf(_("COFF files not supported.\n"));
513 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
514 SysPrintf(_("(did you forget -cdfile ?)\n"));
522 CdromLabel[0] = '\0';
530 static void *zlib_open(const char *name, const char *mode)
532 return gzopen(name, mode);
535 static int zlib_read(void *file, void *buf, u32 len)
537 return gzread(file, buf, len);
540 static int zlib_write(void *file, const void *buf, u32 len)
542 return gzwrite(file, buf, len);
545 static long zlib_seek(void *file, long offs, int whence)
547 return gzseek(file, offs, whence);
550 static void zlib_close(void *file)
555 struct PcsxSaveFuncs SaveFuncs = {
556 zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
559 static const char PcsxHeader[32] = "STv4 PCSX v" PACKAGE_VERSION;
561 // Savestate Versioning!
562 // If you make changes to the savestate version, please increment the value below.
563 static const u32 SaveVersion = 0x8b410006;
565 int SaveState(const char *file) {
572 f = SaveFuncs.open(file, "wb");
573 if (f == NULL) return -1;
577 SaveFuncs.write(f, (void *)PcsxHeader, 32);
578 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
579 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
581 pMem = (unsigned char *)malloc(128 * 96 * 3);
582 if (pMem == NULL) return -1;
583 GPU_getScreenPic(pMem);
584 SaveFuncs.write(f, pMem, 128 * 96 * 3);
590 SaveFuncs.write(f, psxM, 0x00200000);
591 SaveFuncs.write(f, psxR, 0x00080000);
592 SaveFuncs.write(f, psxH, 0x00010000);
593 SaveFuncs.write(f, (void *)&psxRegs, sizeof(psxRegs));
596 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
597 gpufP->ulFreezeVersion = 1;
598 GPU_freeze(1, gpufP);
599 SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
603 spufP = (SPUFreeze_t *) malloc(16);
604 SPU_freeze(2, spufP, psxRegs.cycle);
605 Size = spufP->Size; SaveFuncs.write(f, &Size, 4);
607 spufP = (SPUFreeze_t *) malloc(Size);
608 SPU_freeze(1, spufP, psxRegs.cycle);
609 SaveFuncs.write(f, spufP, Size);
620 new_dyna_after_save();
625 int LoadState(const char *file) {
634 f = SaveFuncs.open(file, "rb");
635 if (f == NULL) return -1;
637 SaveFuncs.read(f, header, sizeof(header));
638 SaveFuncs.read(f, &version, sizeof(u32));
639 SaveFuncs.read(f, &hle, sizeof(boolean));
641 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
651 SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
653 SaveFuncs.read(f, psxM, 0x00200000);
654 SaveFuncs.read(f, psxR, 0x00080000);
655 SaveFuncs.read(f, psxH, 0x00010000);
656 SaveFuncs.read(f, (void *)&psxRegs, sizeof(psxRegs));
662 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
663 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
664 GPU_freeze(0, gpufP);
666 if (HW_GPU_STATUS == 0)
667 HW_GPU_STATUS = GPU_readStatus();
670 SaveFuncs.read(f, &Size, 4);
671 spufP = (SPUFreeze_t *)malloc(Size);
672 SaveFuncs.read(f, spufP, Size);
673 SPU_freeze(0, spufP, psxRegs.cycle);
688 int CheckState(const char *file) {
694 f = SaveFuncs.open(file, "rb");
695 if (f == NULL) return -1;
697 SaveFuncs.read(f, header, sizeof(header));
698 SaveFuncs.read(f, &version, sizeof(u32));
699 SaveFuncs.read(f, &hle, sizeof(boolean));
703 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
709 // NET Function Helpers
712 if (NET_recvData == NULL || NET_sendData == NULL)
715 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
716 NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
717 NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
718 NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
719 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
720 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
728 if (NET_recvData == NULL || NET_sendData == NULL)
731 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
732 NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
733 NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
734 NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
735 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
740 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
741 if (tmp != Config.Cpu) {
744 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
745 else psxCpu = &psxRec;
749 if (psxCpu->Init() == -1) {
750 SysClose(); return -1;
758 // remove the leading and trailing spaces in a string
759 void trim(char *str) {
763 // skip leading blanks
764 while (str[pos] <= ' ' && str[pos] > 0)
768 *(dest++) = str[pos];
772 *(dest--) = '\0'; // store the null
774 // remove trailing blanks
775 while (dest >= str && *dest <= ' ' && *dest > 0)
779 // lookup table for crc calculation
780 static unsigned short crctab[256] = {
781 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
782 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
783 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
784 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
785 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
786 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
787 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
788 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
789 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
790 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
791 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
792 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
793 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
794 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
795 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
796 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
797 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
798 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
799 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
800 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
801 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
802 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
803 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
804 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
805 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
806 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
807 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
808 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
809 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
812 u16 calcCrc(u8 *d, int len) {
816 for (i = 0; i < len; i++) {
817 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);