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.
29 char CdromId[10] = "";
30 char CdromLabel[33] = "";
32 // PSX Executable types
38 #define ISODCL(from, to) (to - from + 1)
40 struct iso_directory_record {
41 char length [ISODCL (1, 1)]; /* 711 */
42 char ext_attr_length [ISODCL (2, 2)]; /* 711 */
43 char extent [ISODCL (3, 10)]; /* 733 */
44 char size [ISODCL (11, 18)]; /* 733 */
45 char date [ISODCL (19, 25)]; /* 7 by 711 */
46 char flags [ISODCL (26, 26)];
47 char file_unit_size [ISODCL (27, 27)]; /* 711 */
48 char interleave [ISODCL (28, 28)]; /* 711 */
49 char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
50 unsigned char name_len [ISODCL (33, 33)]; /* 711 */
54 void mmssdd( char *b, char *p )
57 #if defined(__BIGENDIAN__)
58 int block = (b[0] & 0xff) | ((b[1] & 0xff) << 8) | ((b[2] & 0xff) << 16) | (b[3] << 24);
60 int block = *((int*)b);
64 m = block / 4500; // minutes
65 block = block - m * 4500; // minutes rest
66 s = block / 75; // seconds
67 d = block - s * 75; // seconds rest
69 m = ((m / 10) << 4) | m % 10;
70 s = ((s / 10) << 4) | s % 10;
71 d = ((d / 10) << 4) | d % 10;
79 time[0] = btoi(time[0]); time[1] = btoi(time[1]); time[2] = btoi(time[2]); \
84 if (time[1] == 60) { \
89 time[0] = itob(time[0]); time[1] = itob(time[1]); time[2] = itob(time[2]);
92 if (CDR_readTrack(time) == -1) return -1; \
93 buf = CDR_getBuffer(); \
94 if (buf == NULL) return -1; else CheckPPFCache(buf, time[0], time[1], time[2]);
96 #define READDIR(_dir) \
98 memcpy(_dir, buf + 12, 2048); \
102 memcpy(_dir + 2048, buf + 12, 2048);
104 int GetCdromFile(u8 *mdir, u8 *time, s8 *filename) {
105 struct iso_directory_record *dir;
110 // only try to scan if a filename is given
111 if (!strlen(filename)) return -1;
115 dir = (struct iso_directory_record*) &mdir[i];
116 if (dir->length[0] == 0) {
121 if (dir->flags[0] & 0x2) { // it's a dir
122 if (!strnicmp((char *)&dir->name[0], filename, dir->name_len[0])) {
123 if (filename[dir->name_len[0]] != '\\') continue;
125 filename += dir->name_len[0] + 1;
127 mmssdd(dir->extent, (char *)time);
133 if (!strnicmp((char *)&dir->name[0], filename, strlen(filename))) {
134 mmssdd(dir->extent, (char *)time);
144 struct iso_directory_record *dir;
150 psxRegs.pc = psxRegs.GPR.n.ra;
154 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
158 // skip head and sub, and go to the root directory record
159 dir = (struct iso_directory_record*) &buf[12+156];
161 mmssdd(dir->extent, (char*)time);
165 // Load SYSTEM.CNF and scan for the main executable
166 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
167 // if SYSTEM.CNF is missing, start an existing PSX.EXE
168 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
173 // read the SYSTEM.CNF
176 sscanf((char *)buf + 12, "BOOT = cdrom:\\%256s", exename);
177 if (GetCdromFile(mdir, time, exename) == -1) {
178 sscanf((char *)buf + 12, "BOOT = cdrom:%256s", exename);
179 if (GetCdromFile(mdir, time, exename) == -1) {
180 char *ptr = strstr(buf + 12, "cdrom:");
183 while (*ptr == '\\' || *ptr == '/') ptr++;
184 strncpy(exename, ptr, 255);
187 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
189 if (GetCdromFile(mdir, time, exename) == -1)
196 // Read the EXE-Header
200 memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
202 psxRegs.pc = SWAP32(tmpHead.pc0);
203 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
204 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
205 if (psxRegs.GPR.n.sp == 0) psxRegs.GPR.n.sp = 0x801fff00;
207 tmpHead.t_size = SWAP32(tmpHead.t_size);
208 tmpHead.t_addr = SWAP32(tmpHead.t_addr);
210 // Read the rest of the main executable
211 while (tmpHead.t_size) {
212 void *ptr = (void *)PSXM(tmpHead.t_addr);
217 if (ptr != NULL) memcpy(ptr, buf+12, 2048);
219 tmpHead.t_size -= 2048;
220 tmpHead.t_addr += 2048;
226 int LoadCdromFile(const char *filename, EXE_HEADER *head) {
227 struct iso_directory_record *dir;
229 u8 mdir[4096], exename[256];
232 sscanf(filename, "cdrom:\\%256s", exename);
234 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
238 // skip head and sub, and go to the root directory record
239 dir = (struct iso_directory_record *)&buf[12 + 156];
241 mmssdd(dir->extent, (char*)time);
245 if (GetCdromFile(mdir, time, exename) == -1) return -1;
249 memcpy(head, buf + 12, sizeof(EXE_HEADER));
257 memcpy((void *)PSXM(addr), buf + 12, 2048);
267 struct iso_directory_record *dir;
268 unsigned char time[4], *buf;
269 unsigned char mdir[4096];
277 time[2] = itob(0x10);
281 CdromLabel[0] = '\0';
284 strncpy(CdromLabel, buf + 52, 32);
286 // skip head and sub, and go to the root directory record
287 dir = (struct iso_directory_record *)&buf[12 + 156];
289 mmssdd(dir->extent, (char *)time);
293 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
296 sscanf((char *)buf + 12, "BOOT = cdrom:\\%256s", exename);
297 if (GetCdromFile(mdir, time, exename) == -1) {
298 sscanf((char *)buf + 12, "BOOT = cdrom:%256s", exename);
299 if (GetCdromFile(mdir, time, exename) == -1) {
300 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
303 while (*ptr == '\\' || *ptr == '/') ptr++;
304 strncpy(exename, ptr, 255);
307 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
309 if (GetCdromFile(mdir, time, exename) == -1)
310 return -1; // main executable not found
315 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
316 strcpy(exename, "PSX.EXE;1");
317 strcpy(CdromId, "SLUS99999");
319 return -1; // SYSTEM.CNF and PSX.EXE not found
321 if (CdromId[0] == '\0') {
324 if (exename[i - 2] == ';') i-= 2;
326 while (i >= 0 && c >= 0) {
327 if (isalnum(exename[i])) CdromId[c--] = exename[i];
333 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
334 if (strstr(exename, "ES") != NULL)
335 Config.PsxType = PSX_TYPE_PAL; // pal
336 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
339 if (CdromLabel[0] == ' ') {
340 strncpy(CdromLabel, CdromId, 9);
342 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
343 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
350 static int PSXGetFileType(FILE *f) {
351 unsigned long current;
357 fseek(f, 0L, SEEK_SET);
358 fread(mybuf, 2048, 1, f);
359 fseek(f, current, SEEK_SET);
361 exe_hdr = (EXE_HEADER *)mybuf;
362 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
365 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
368 coff_hdr = (FILHDR *)mybuf;
369 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
375 int Load(const char *ExePath) {
381 u32 section_address, section_size;
383 strncpy(CdromId, "SLUS99999", 9);
384 strncpy(CdromLabel, "SLUS_999.99", 11);
386 tmpFile = fopen(ExePath, "rb");
387 if (tmpFile == NULL) {
388 SysPrintf(_("Error opening file: %s.\n"), ExePath);
391 type = PSXGetFileType(tmpFile);
394 fread(&tmpHead,sizeof(EXE_HEADER),1,tmpFile);
395 fseek(tmpFile, 0x800, SEEK_SET);
396 fread((void *)PSXM(SWAP32(tmpHead.t_addr)), SWAP32(tmpHead.t_size),1,tmpFile);
398 psxRegs.pc = SWAP32(tmpHead.pc0);
399 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
400 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
401 if (psxRegs.GPR.n.sp == 0)
402 psxRegs.GPR.n.sp = 0x801fff00;
406 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
408 fread(&opcode, 1, 1, tmpFile);
410 case 1: /* Section loading */
411 fread(§ion_address, 4, 1, tmpFile);
412 fread(§ion_size, 4, 1, tmpFile);
413 section_address = SWAPu32(section_address);
414 section_size = SWAPu32(section_size);
416 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
418 fread(PSXM(section_address), section_size, 1, tmpFile);
420 case 3: /* register loading (PC only?) */
421 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
422 fread(&psxRegs.pc, 4, 1, tmpFile);
423 psxRegs.pc = SWAPu32(psxRegs.pc);
425 case 0: /* End of file */
428 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
432 } while (opcode != 0 && retval == 0);
435 SysPrintf(_("COFF files not supported.\n"));
439 SysPrintf(_("This file does not appear to be a valid PSX file.\n"));
447 CdromLabel[0] = '\0';
455 static const char PcsxHeader[32] = "STv4 PCSX v" PACKAGE_VERSION;
457 // Savestate Versioning!
458 // If you make changes to the savestate version, please increment the value below.
459 static const u32 SaveVersion = 0x8b410004;
461 int SaveState(const char *file) {
468 f = gzopen(file, "wb");
469 if (f == NULL) return -1;
471 gzwrite(f, (void *)PcsxHeader, 32);
472 gzwrite(f, (void *)&SaveVersion, sizeof(u32));
473 gzwrite(f, (void *)&Config.HLE, sizeof(boolean));
475 pMem = (unsigned char *)malloc(128 * 96 * 3);
476 if (pMem == NULL) return -1;
477 GPU_getScreenPic(pMem);
478 gzwrite(f, pMem, 128 * 96 * 3);
484 gzwrite(f, psxM, 0x00200000);
485 gzwrite(f, psxR, 0x00080000);
486 gzwrite(f, psxH, 0x00010000);
487 gzwrite(f, (void *)&psxRegs, sizeof(psxRegs));
490 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
491 gpufP->ulFreezeVersion = 1;
492 GPU_freeze(1, gpufP);
493 gzwrite(f, gpufP, sizeof(GPUFreeze_t));
497 spufP = (SPUFreeze_t *) malloc(16);
498 SPU_freeze(2, spufP);
499 Size = spufP->Size; gzwrite(f, &Size, 4);
501 spufP = (SPUFreeze_t *) malloc(Size);
502 SPU_freeze(1, spufP);
503 gzwrite(f, spufP, Size);
517 int LoadState(const char *file) {
526 f = gzopen(file, "rb");
527 if (f == NULL) return -1;
529 gzread(f, header, sizeof(header));
530 gzread(f, &version, sizeof(u32));
531 gzread(f, &hle, sizeof(boolean));
533 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion || hle != Config.HLE) {
539 gzseek(f, 128 * 96 * 3, SEEK_CUR);
541 gzread(f, psxM, 0x00200000);
542 gzread(f, psxR, 0x00080000);
543 gzread(f, psxH, 0x00010000);
544 gzread(f, (void *)&psxRegs, sizeof(psxRegs));
550 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
551 gzread(f, gpufP, sizeof(GPUFreeze_t));
552 GPU_freeze(0, gpufP);
557 spufP = (SPUFreeze_t *)malloc(Size);
558 gzread(f, spufP, Size);
559 SPU_freeze(0, spufP);
573 int CheckState(const char *file) {
579 f = gzopen(file, "rb");
580 if (f == NULL) return -1;
582 gzread(f, header, sizeof(header));
583 gzread(f, &version, sizeof(u32));
584 gzread(f, &hle, sizeof(boolean));
588 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion || hle != Config.HLE)
594 // NET Function Helpers
597 if (NET_recvData == NULL || NET_sendData == NULL)
600 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
601 NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
602 NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
603 NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
604 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
605 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
613 if (NET_recvData == NULL || NET_sendData == NULL)
616 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
617 NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
618 NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
619 NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
620 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
625 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
626 if (tmp != Config.Cpu) {
629 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
630 else psxCpu = &psxRec;
634 if (psxCpu->Init() == -1) {
635 SysClose(); return -1;
643 // remove the leading and trailing spaces in a string
644 void trim(char *str) {
648 // skip leading blanks
649 while (str[pos] <= ' ' && str[pos] > 0)
653 *(dest++) = str[pos];
657 *(dest--) = '\0'; // store the null
659 // remove trailing blanks
660 while (dest >= str && *dest <= ' ' && *dest > 0)
664 // lookup table for crc calculation
665 static unsigned short crctab[256] = {
666 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
667 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
668 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
669 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
670 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
671 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
672 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
673 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
674 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
675 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
676 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
677 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
678 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
679 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
680 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
681 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
682 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
683 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
684 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
685 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
686 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
687 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
688 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
689 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
690 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
691 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
692 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
693 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
694 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
697 u16 calcCrc(u8 *d, int len) {
701 for (i = 0; i < len; i++) {
702 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);