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 )
58 int block = (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0];
59 #elif defined(__BIGENDIAN__)
60 int block = (b[0] & 0xff) | ((b[1] & 0xff) << 8) | ((b[2] & 0xff) << 16) | (b[3] << 24);
62 int block = *((int*)b);
66 m = block / 4500; // minutes
67 block = block - m * 4500; // minutes rest
68 s = block / 75; // seconds
69 d = block - s * 75; // seconds rest
71 m = ((m / 10) << 4) | m % 10;
72 s = ((s / 10) << 4) | s % 10;
73 d = ((d / 10) << 4) | d % 10;
81 time[0] = btoi(time[0]); time[1] = btoi(time[1]); time[2] = btoi(time[2]); \
86 if (time[1] == 60) { \
91 time[0] = itob(time[0]); time[1] = itob(time[1]); time[2] = itob(time[2]);
94 if (CDR_readTrack(time) == -1) return -1; \
95 buf = CDR_getBuffer(); \
96 if (buf == NULL) return -1; else CheckPPFCache(buf, time[0], time[1], time[2]);
98 #define READDIR(_dir) \
100 memcpy(_dir, buf + 12, 2048); \
104 memcpy(_dir + 2048, buf + 12, 2048);
106 int GetCdromFile(u8 *mdir, u8 *time, s8 *filename) {
107 struct iso_directory_record *dir;
112 // only try to scan if a filename is given
113 if (!strlen(filename)) return -1;
117 dir = (struct iso_directory_record*) &mdir[i];
118 if (dir->length[0] == 0) {
123 if (dir->flags[0] & 0x2) { // it's a dir
124 if (!strnicmp((char *)&dir->name[0], filename, dir->name_len[0])) {
125 if (filename[dir->name_len[0]] != '\\') continue;
127 filename += dir->name_len[0] + 1;
129 mmssdd(dir->extent, (char *)time);
135 if (!strnicmp((char *)&dir->name[0], filename, strlen(filename))) {
136 mmssdd(dir->extent, (char *)time);
146 struct iso_directory_record *dir;
152 psxRegs.pc = psxRegs.GPR.n.ra;
156 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
160 // skip head and sub, and go to the root directory record
161 dir = (struct iso_directory_record*) &buf[12+156];
163 mmssdd(dir->extent, (char*)time);
167 // Load SYSTEM.CNF and scan for the main executable
168 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
169 // if SYSTEM.CNF is missing, start an existing PSX.EXE
170 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
175 // read the SYSTEM.CNF
178 sscanf((char *)buf + 12, "BOOT = cdrom:\\%256s", exename);
179 if (GetCdromFile(mdir, time, exename) == -1) {
180 sscanf((char *)buf + 12, "BOOT = cdrom:%256s", exename);
181 if (GetCdromFile(mdir, time, exename) == -1) {
182 char *ptr = strstr(buf + 12, "cdrom:");
185 while (*ptr == '\\' || *ptr == '/') ptr++;
186 strncpy(exename, ptr, 255);
189 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
191 if (GetCdromFile(mdir, time, exename) == -1)
198 // Read the EXE-Header
202 memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
204 psxRegs.pc = SWAP32(tmpHead.pc0);
205 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
206 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
207 if (psxRegs.GPR.n.sp == 0) psxRegs.GPR.n.sp = 0x801fff00;
209 tmpHead.t_size = SWAP32(tmpHead.t_size);
210 tmpHead.t_addr = SWAP32(tmpHead.t_addr);
212 // Read the rest of the main executable
213 while (tmpHead.t_size) {
214 void *ptr = (void *)PSXM(tmpHead.t_addr);
219 if (ptr != NULL) memcpy(ptr, buf+12, 2048);
221 tmpHead.t_size -= 2048;
222 tmpHead.t_addr += 2048;
228 int LoadCdromFile(const char *filename, EXE_HEADER *head) {
229 struct iso_directory_record *dir;
231 u8 mdir[4096], exename[256];
234 sscanf(filename, "cdrom:\\%256s", exename);
236 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
240 // skip head and sub, and go to the root directory record
241 dir = (struct iso_directory_record *)&buf[12 + 156];
243 mmssdd(dir->extent, (char*)time);
247 if (GetCdromFile(mdir, time, exename) == -1) return -1;
251 memcpy(head, buf + 12, sizeof(EXE_HEADER));
259 memcpy((void *)PSXM(addr), buf + 12, 2048);
269 struct iso_directory_record *dir;
270 unsigned char time[4], *buf;
271 unsigned char mdir[4096];
279 time[2] = itob(0x10);
283 CdromLabel[0] = '\0';
286 strncpy(CdromLabel, buf + 52, 32);
288 // skip head and sub, and go to the root directory record
289 dir = (struct iso_directory_record *)&buf[12 + 156];
291 mmssdd(dir->extent, (char *)time);
295 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
298 sscanf((char *)buf + 12, "BOOT = cdrom:\\%256s", exename);
299 if (GetCdromFile(mdir, time, exename) == -1) {
300 sscanf((char *)buf + 12, "BOOT = cdrom:%256s", exename);
301 if (GetCdromFile(mdir, time, exename) == -1) {
302 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
305 while (*ptr == '\\' || *ptr == '/') ptr++;
306 strncpy(exename, ptr, 255);
309 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
311 if (GetCdromFile(mdir, time, exename) == -1)
312 return -1; // main executable not found
317 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
318 strcpy(exename, "PSX.EXE;1");
319 strcpy(CdromId, "SLUS99999");
321 return -1; // SYSTEM.CNF and PSX.EXE not found
323 if (CdromId[0] == '\0') {
326 if (exename[i - 2] == ';') i-= 2;
328 while (i >= 0 && c >= 0) {
329 if (isalnum(exename[i])) CdromId[c--] = exename[i];
335 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
336 if (strstr(exename, "ES") != NULL)
337 Config.PsxType = PSX_TYPE_PAL; // pal
338 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
341 if (CdromLabel[0] == ' ') {
342 strncpy(CdromLabel, CdromId, 9);
344 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
345 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
352 static int PSXGetFileType(FILE *f) {
353 unsigned long current;
359 fseek(f, 0L, SEEK_SET);
360 fread(mybuf, 2048, 1, f);
361 fseek(f, current, SEEK_SET);
363 exe_hdr = (EXE_HEADER *)mybuf;
364 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
367 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
370 coff_hdr = (FILHDR *)mybuf;
371 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
377 int Load(const char *ExePath) {
383 u32 section_address, section_size;
385 strncpy(CdromId, "SLUS99999", 9);
386 strncpy(CdromLabel, "SLUS_999.99", 11);
388 tmpFile = fopen(ExePath, "rb");
389 if (tmpFile == NULL) {
390 SysPrintf(_("Error opening file: %s.\n"), ExePath);
393 type = PSXGetFileType(tmpFile);
396 fread(&tmpHead,sizeof(EXE_HEADER),1,tmpFile);
397 fseek(tmpFile, 0x800, SEEK_SET);
398 fread((void *)PSXM(SWAP32(tmpHead.t_addr)), SWAP32(tmpHead.t_size),1,tmpFile);
400 psxRegs.pc = SWAP32(tmpHead.pc0);
401 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
402 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
403 if (psxRegs.GPR.n.sp == 0)
404 psxRegs.GPR.n.sp = 0x801fff00;
408 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
410 fread(&opcode, 1, 1, tmpFile);
412 case 1: /* Section loading */
413 fread(§ion_address, 4, 1, tmpFile);
414 fread(§ion_size, 4, 1, tmpFile);
415 section_address = SWAPu32(section_address);
416 section_size = SWAPu32(section_size);
418 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
420 fread(PSXM(section_address), section_size, 1, tmpFile);
422 case 3: /* register loading (PC only?) */
423 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
424 fread(&psxRegs.pc, 4, 1, tmpFile);
425 psxRegs.pc = SWAPu32(psxRegs.pc);
427 case 0: /* End of file */
430 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
434 } while (opcode != 0 && retval == 0);
437 SysPrintf(_("COFF files not supported.\n"));
441 SysPrintf(_("This file does not appear to be a valid PSX file.\n"));
449 CdromLabel[0] = '\0';
457 static const char PcsxHeader[32] = "STv4 PCSX v" PACKAGE_VERSION;
459 // Savestate Versioning!
460 // If you make changes to the savestate version, please increment the value below.
461 static const u32 SaveVersion = 0x8b410006;
463 int SaveState(const char *file) {
470 f = gzopen(file, "wb");
471 if (f == NULL) return -1;
475 gzwrite(f, (void *)PcsxHeader, 32);
476 gzwrite(f, (void *)&SaveVersion, sizeof(u32));
477 gzwrite(f, (void *)&Config.HLE, sizeof(boolean));
479 pMem = (unsigned char *)malloc(128 * 96 * 3);
480 if (pMem == NULL) return -1;
481 GPU_getScreenPic(pMem);
482 gzwrite(f, pMem, 128 * 96 * 3);
488 gzwrite(f, psxM, 0x00200000);
489 gzwrite(f, psxR, 0x00080000);
490 gzwrite(f, psxH, 0x00010000);
491 gzwrite(f, (void *)&psxRegs, sizeof(psxRegs));
494 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
495 gpufP->ulFreezeVersion = 1;
496 GPU_freeze(1, gpufP);
497 gzwrite(f, gpufP, sizeof(GPUFreeze_t));
501 spufP = (SPUFreeze_t *) malloc(16);
502 SPU_freeze(2, spufP);
503 Size = spufP->Size; gzwrite(f, &Size, 4);
505 spufP = (SPUFreeze_t *) malloc(Size);
506 SPU_freeze(1, spufP);
507 gzwrite(f, spufP, Size);
521 int LoadState(const char *file) {
530 f = gzopen(file, "rb");
531 if (f == NULL) return -1;
533 gzread(f, header, sizeof(header));
534 gzread(f, &version, sizeof(u32));
535 gzread(f, &hle, sizeof(boolean));
537 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion || hle != Config.HLE) {
543 gzseek(f, 128 * 96 * 3, SEEK_CUR);
545 gzread(f, psxM, 0x00200000);
546 gzread(f, psxR, 0x00080000);
547 gzread(f, psxH, 0x00010000);
548 gzread(f, (void *)&psxRegs, sizeof(psxRegs));
554 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
555 gzread(f, gpufP, sizeof(GPUFreeze_t));
556 GPU_freeze(0, gpufP);
561 spufP = (SPUFreeze_t *)malloc(Size);
562 gzread(f, spufP, Size);
563 SPU_freeze(0, spufP);
578 int CheckState(const char *file) {
584 f = gzopen(file, "rb");
585 if (f == NULL) return -1;
587 gzread(f, header, sizeof(header));
588 gzread(f, &version, sizeof(u32));
589 gzread(f, &hle, sizeof(boolean));
593 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion || hle != Config.HLE)
599 // NET Function Helpers
602 if (NET_recvData == NULL || NET_sendData == NULL)
605 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
606 NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
607 NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
608 NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
609 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
610 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
618 if (NET_recvData == NULL || NET_sendData == NULL)
621 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
622 NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
623 NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
624 NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
625 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
630 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
631 if (tmp != Config.Cpu) {
634 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
635 else psxCpu = &psxRec;
639 if (psxCpu->Init() == -1) {
640 SysClose(); return -1;
648 // remove the leading and trailing spaces in a string
649 void trim(char *str) {
653 // skip leading blanks
654 while (str[pos] <= ' ' && str[pos] > 0)
658 *(dest++) = str[pos];
662 *(dest--) = '\0'; // store the null
664 // remove trailing blanks
665 while (dest >= str && *dest <= ' ' && *dest > 0)
669 // lookup table for crc calculation
670 static unsigned short crctab[256] = {
671 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
672 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
673 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
674 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
675 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
676 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
677 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
678 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
679 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
680 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
681 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
682 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
683 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
684 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
685 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
686 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
687 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
688 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
689 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
690 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
691 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
692 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
693 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
694 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
695 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
696 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
697 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
698 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
699 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
702 u16 calcCrc(u8 *d, int len) {
706 for (i = 0; i < len; i++) {
707 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);