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 = 0x8b410004;
463 int SaveState(const char *file) {
470 f = gzopen(file, "wb");
471 if (f == NULL) return -1;
473 gzwrite(f, (void *)PcsxHeader, 32);
474 gzwrite(f, (void *)&SaveVersion, sizeof(u32));
475 gzwrite(f, (void *)&Config.HLE, sizeof(boolean));
477 pMem = (unsigned char *)malloc(128 * 96 * 3);
478 if (pMem == NULL) return -1;
479 GPU_getScreenPic(pMem);
480 gzwrite(f, pMem, 128 * 96 * 3);
486 gzwrite(f, psxM, 0x00200000);
487 gzwrite(f, psxR, 0x00080000);
488 gzwrite(f, psxH, 0x00010000);
489 gzwrite(f, (void *)&psxRegs, sizeof(psxRegs));
492 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
493 gpufP->ulFreezeVersion = 1;
494 GPU_freeze(1, gpufP);
495 gzwrite(f, gpufP, sizeof(GPUFreeze_t));
499 spufP = (SPUFreeze_t *) malloc(16);
500 SPU_freeze(2, spufP);
501 Size = spufP->Size; gzwrite(f, &Size, 4);
503 spufP = (SPUFreeze_t *) malloc(Size);
504 SPU_freeze(1, spufP);
505 gzwrite(f, spufP, Size);
519 int LoadState(const char *file) {
528 f = gzopen(file, "rb");
529 if (f == NULL) return -1;
531 gzread(f, header, sizeof(header));
532 gzread(f, &version, sizeof(u32));
533 gzread(f, &hle, sizeof(boolean));
535 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion || hle != Config.HLE) {
541 gzseek(f, 128 * 96 * 3, SEEK_CUR);
543 gzread(f, psxM, 0x00200000);
544 gzread(f, psxR, 0x00080000);
545 gzread(f, psxH, 0x00010000);
546 gzread(f, (void *)&psxRegs, sizeof(psxRegs));
552 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
553 gzread(f, gpufP, sizeof(GPUFreeze_t));
554 GPU_freeze(0, gpufP);
559 spufP = (SPUFreeze_t *)malloc(Size);
560 gzread(f, spufP, Size);
561 SPU_freeze(0, spufP);
575 int CheckState(const char *file) {
581 f = gzopen(file, "rb");
582 if (f == NULL) return -1;
584 gzread(f, header, sizeof(header));
585 gzread(f, &version, sizeof(u32));
586 gzread(f, &hle, sizeof(boolean));
590 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion || hle != Config.HLE)
596 // NET Function Helpers
599 if (NET_recvData == NULL || NET_sendData == NULL)
602 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
603 NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
604 NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
605 NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
606 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
607 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
615 if (NET_recvData == NULL || NET_sendData == NULL)
618 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
619 NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
620 NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
621 NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
622 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
627 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
628 if (tmp != Config.Cpu) {
631 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
632 else psxCpu = &psxRec;
636 if (psxCpu->Init() == -1) {
637 SysClose(); return -1;
645 // remove the leading and trailing spaces in a string
646 void trim(char *str) {
650 // skip leading blanks
651 while (str[pos] <= ' ' && str[pos] > 0)
655 *(dest++) = str[pos];
659 *(dest--) = '\0'; // store the null
661 // remove trailing blanks
662 while (dest >= str && *dest <= ' ' && *dest > 0)
666 // lookup table for crc calculation
667 static unsigned short crctab[256] = {
668 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
669 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
670 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
671 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
672 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
673 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
674 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
675 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
676 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
677 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
678 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
679 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
680 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
681 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
682 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
683 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
684 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
685 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
686 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
687 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
688 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
689 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
690 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
691 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
692 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
693 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
694 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
695 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
696 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
699 u16 calcCrc(u8 *d, int len) {
703 for (i = 0; i < len; i++) {
704 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);