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 (CdromId[2] == 'e' || CdromId[2] == 'E')
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) {
547 gzseek(f, 128 * 96 * 3, SEEK_CUR);
549 gzread(f, psxM, 0x00200000);
550 gzread(f, psxR, 0x00080000);
551 gzread(f, psxH, 0x00010000);
552 gzread(f, (void *)&psxRegs, sizeof(psxRegs));
558 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
559 gzread(f, gpufP, sizeof(GPUFreeze_t));
560 GPU_freeze(0, gpufP);
565 spufP = (SPUFreeze_t *)malloc(Size);
566 gzread(f, spufP, Size);
567 SPU_freeze(0, spufP);
582 int CheckState(const char *file) {
588 f = gzopen(file, "rb");
589 if (f == NULL) return -1;
591 gzread(f, header, sizeof(header));
592 gzread(f, &version, sizeof(u32));
593 gzread(f, &hle, sizeof(boolean));
597 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
603 // NET Function Helpers
606 if (NET_recvData == NULL || NET_sendData == NULL)
609 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
610 NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
611 NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
612 NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
613 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
614 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
622 if (NET_recvData == NULL || NET_sendData == NULL)
625 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
626 NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
627 NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
628 NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
629 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
634 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
635 if (tmp != Config.Cpu) {
638 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
639 else psxCpu = &psxRec;
643 if (psxCpu->Init() == -1) {
644 SysClose(); return -1;
652 // remove the leading and trailing spaces in a string
653 void trim(char *str) {
657 // skip leading blanks
658 while (str[pos] <= ' ' && str[pos] > 0)
662 *(dest++) = str[pos];
666 *(dest--) = '\0'; // store the null
668 // remove trailing blanks
669 while (dest >= str && *dest <= ' ' && *dest > 0)
673 // lookup table for crc calculation
674 static unsigned short crctab[256] = {
675 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
676 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
677 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
678 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
679 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
680 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
681 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
682 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
683 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
684 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
685 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
686 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
687 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
688 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
689 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
690 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
691 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
692 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
693 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
694 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
695 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
696 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
697 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
698 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
699 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
700 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
701 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
702 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
703 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
706 u16 calcCrc(u8 *d, int len) {
710 for (i = 0; i < len; i++) {
711 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);