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.
30 char CdromId[10] = "";
31 char CdromLabel[33] = "";
33 // PSX Executable types
39 #define ISODCL(from, to) (to - from + 1)
41 struct iso_directory_record {
42 char length [ISODCL (1, 1)]; /* 711 */
43 char ext_attr_length [ISODCL (2, 2)]; /* 711 */
44 char extent [ISODCL (3, 10)]; /* 733 */
45 char size [ISODCL (11, 18)]; /* 733 */
46 char date [ISODCL (19, 25)]; /* 7 by 711 */
47 char flags [ISODCL (26, 26)];
48 char file_unit_size [ISODCL (27, 27)]; /* 711 */
49 char interleave [ISODCL (28, 28)]; /* 711 */
50 char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
51 unsigned char name_len [ISODCL (33, 33)]; /* 711 */
55 void mmssdd( char *b, char *p )
59 int block = (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0];
60 #elif defined(__BIGENDIAN__)
61 int block = (b[0] & 0xff) | ((b[1] & 0xff) << 8) | ((b[2] & 0xff) << 16) | (b[3] << 24);
63 int block = *((int*)b);
67 m = block / 4500; // minutes
68 block = block - m * 4500; // minutes rest
69 s = block / 75; // seconds
70 d = block - s * 75; // seconds rest
72 m = ((m / 10) << 4) | m % 10;
73 s = ((s / 10) << 4) | s % 10;
74 d = ((d / 10) << 4) | d % 10;
82 time[0] = btoi(time[0]); time[1] = btoi(time[1]); time[2] = btoi(time[2]); \
87 if (time[1] == 60) { \
92 time[0] = itob(time[0]); time[1] = itob(time[1]); time[2] = itob(time[2]);
95 if (CDR_readTrack(time) == -1) return -1; \
96 buf = (void *)CDR_getBuffer(); \
97 if (buf == NULL) return -1; \
98 else CheckPPFCache((u8 *)buf, time[0], time[1], time[2]);
100 #define READDIR(_dir) \
102 memcpy(_dir, buf + 12, 2048); \
106 memcpy(_dir + 2048, buf + 12, 2048);
108 int GetCdromFile(u8 *mdir, u8 *time, char *filename) {
109 struct iso_directory_record *dir;
114 // only try to scan if a filename is given
115 if (!strlen(filename)) return -1;
119 dir = (struct iso_directory_record*) &mdir[i];
120 if (dir->length[0] == 0) {
125 if (dir->flags[0] & 0x2) { // it's a dir
126 if (!strnicmp((char *)&dir->name[0], filename, dir->name_len[0])) {
127 if (filename[dir->name_len[0]] != '\\') continue;
129 filename += dir->name_len[0] + 1;
131 mmssdd(dir->extent, (char *)time);
137 if (!strnicmp((char *)&dir->name[0], filename, strlen(filename))) {
138 mmssdd(dir->extent, (char *)time);
146 static const unsigned int gpu_ctl_def[] = {
147 0x00000000, 0x01000000, 0x03000000, 0x04000000,
148 0x05000800, 0x06c60260, 0x0703fc10, 0x08000027,
151 static const unsigned int gpu_data_def[] = {
152 0xe100360b, 0xe2000000, 0xe3000800, 0xe4077e7f,
153 0xe5001000, 0xe6000000,
154 0x02000000, 0x00000000, 0x01ff03ff,
157 static void fake_bios_gpu_setup(void)
161 for (i = 0; i < sizeof(gpu_ctl_def) / sizeof(gpu_ctl_def[0]); i++)
162 GPU_writeStatus(gpu_ctl_def[i]);
164 for (i = 0; i < sizeof(gpu_data_def) / sizeof(gpu_data_def[0]); i++)
165 GPU_writeData(gpu_data_def[i]);
170 struct iso_directory_record *dir;
175 // not the best place to do it, but since BIOS boot logo killer
176 // is just below, do it here
177 fake_bios_gpu_setup();
181 psxRegs.pc = psxRegs.GPR.n.ra;
185 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
189 // skip head and sub, and go to the root directory record
190 dir = (struct iso_directory_record*) &buf[12+156];
192 mmssdd(dir->extent, (char*)time);
196 // Load SYSTEM.CNF and scan for the main executable
197 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
198 // if SYSTEM.CNF is missing, start an existing PSX.EXE
199 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
204 // read the SYSTEM.CNF
207 sscanf((char *)buf + 12, "BOOT = cdrom:\\%256s", exename);
208 if (GetCdromFile(mdir, time, exename) == -1) {
209 sscanf((char *)buf + 12, "BOOT = cdrom:%256s", exename);
210 if (GetCdromFile(mdir, time, exename) == -1) {
211 char *ptr = strstr((char *)buf + 12, "cdrom:");
214 while (*ptr == '\\' || *ptr == '/') ptr++;
215 strncpy(exename, ptr, 255);
218 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
220 if (GetCdromFile(mdir, time, exename) == -1)
227 // Read the EXE-Header
231 memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
233 psxRegs.pc = SWAP32(tmpHead.pc0);
234 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
235 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
236 if (psxRegs.GPR.n.sp == 0) psxRegs.GPR.n.sp = 0x801fff00;
238 tmpHead.t_size = SWAP32(tmpHead.t_size);
239 tmpHead.t_addr = SWAP32(tmpHead.t_addr);
241 psxCpu->Clear(tmpHead.t_addr, tmpHead.t_size / 4);
243 // Read the rest of the main executable
244 while (tmpHead.t_size & ~2047) {
245 void *ptr = (void *)PSXM(tmpHead.t_addr);
250 if (ptr != NULL) memcpy(ptr, buf+12, 2048);
252 tmpHead.t_size -= 2048;
253 tmpHead.t_addr += 2048;
259 int LoadCdromFile(const char *filename, EXE_HEADER *head) {
260 struct iso_directory_record *dir;
267 sscanf(filename, "cdrom:\\%256s", exename);
269 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
273 // skip head and sub, and go to the root directory record
274 dir = (struct iso_directory_record *)&buf[12 + 156];
276 mmssdd(dir->extent, (char*)time);
280 if (GetCdromFile(mdir, time, exename) == -1) return -1;
284 memcpy(head, buf + 12, sizeof(EXE_HEADER));
288 psxCpu->Clear(addr, size / 4);
290 while (size & ~2047) {
296 memcpy(mem, buf + 12, 2048);
306 struct iso_directory_record *dir;
307 unsigned char time[4];
309 unsigned char mdir[4096];
317 time[2] = itob(0x10);
321 CdromLabel[0] = '\0';
324 strncpy(CdromLabel, buf + 52, 32);
326 // skip head and sub, and go to the root directory record
327 dir = (struct iso_directory_record *)&buf[12 + 156];
329 mmssdd(dir->extent, (char *)time);
333 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
336 sscanf(buf + 12, "BOOT = cdrom:\\%256s", exename);
337 if (GetCdromFile(mdir, time, exename) == -1) {
338 sscanf(buf + 12, "BOOT = cdrom:%256s", exename);
339 if (GetCdromFile(mdir, time, exename) == -1) {
340 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
343 while (*ptr == '\\' || *ptr == '/') ptr++;
344 strncpy(exename, ptr, 255);
347 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
349 if (GetCdromFile(mdir, time, exename) == -1)
350 return -1; // main executable not found
355 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
356 strcpy(exename, "PSX.EXE;1");
357 strcpy(CdromId, "SLUS99999");
359 return -1; // SYSTEM.CNF and PSX.EXE not found
361 if (CdromId[0] == '\0') {
364 if (exename[i - 2] == ';') i-= 2;
366 while (i >= 0 && c >= 0) {
367 if (isalnum(exename[i])) CdromId[c--] = exename[i];
373 if (CdromId[0] == '\0')
374 strcpy(CdromId, "SLUS99999");
376 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
377 if (CdromId[2] == 'e' || CdromId[2] == 'E')
378 Config.PsxType = PSX_TYPE_PAL; // pal
379 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
382 if (CdromLabel[0] == ' ') {
383 strncpy(CdromLabel, CdromId, 9);
385 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
386 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
393 static int PSXGetFileType(FILE *f) {
394 unsigned long current;
400 fseek(f, 0L, SEEK_SET);
401 fread(mybuf, 2048, 1, f);
402 fseek(f, current, SEEK_SET);
404 exe_hdr = (EXE_HEADER *)mybuf;
405 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
408 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
411 coff_hdr = (FILHDR *)mybuf;
412 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
418 // temporary pandora workaround..
420 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
425 tmp = malloc(size * nmemb);
427 ret = fread(tmp, size, nmemb, stream);
428 memcpy(ptr, tmp, size * nmemb);
434 int Load(const char *ExePath) {
440 u32 section_address, section_size;
443 strncpy(CdromId, "SLUS99999", 9);
444 strncpy(CdromLabel, "SLUS_999.99", 11);
446 tmpFile = fopen(ExePath, "rb");
447 if (tmpFile == NULL) {
448 SysPrintf(_("Error opening file: %s.\n"), ExePath);
451 type = PSXGetFileType(tmpFile);
454 fread(&tmpHead,sizeof(EXE_HEADER),1,tmpFile);
455 section_address = SWAP32(tmpHead.t_addr);
456 section_size = SWAP32(tmpHead.t_size);
457 mem = PSXM(section_address);
459 fseek(tmpFile, 0x800, SEEK_SET);
460 fread_to_ram(mem, section_size, 1, tmpFile);
461 psxCpu->Clear(section_address, section_size / 4);
464 psxRegs.pc = SWAP32(tmpHead.pc0);
465 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
466 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
467 if (psxRegs.GPR.n.sp == 0)
468 psxRegs.GPR.n.sp = 0x801fff00;
472 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
474 fread(&opcode, 1, 1, tmpFile);
476 case 1: /* Section loading */
477 fread(§ion_address, 4, 1, tmpFile);
478 fread(§ion_size, 4, 1, tmpFile);
479 section_address = SWAPu32(section_address);
480 section_size = SWAPu32(section_size);
482 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
484 mem = PSXM(section_address);
486 fread_to_ram(mem, section_size, 1, tmpFile);
487 psxCpu->Clear(section_address, section_size / 4);
490 case 3: /* register loading (PC only?) */
491 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
492 fread(&psxRegs.pc, 4, 1, tmpFile);
493 psxRegs.pc = SWAPu32(psxRegs.pc);
495 case 0: /* End of file */
498 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
502 } while (opcode != 0 && retval == 0);
505 SysPrintf(_("COFF files not supported.\n"));
509 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
510 SysPrintf(_("(did you forget -cdfile ?)\n"));
518 CdromLabel[0] = '\0';
526 static const char PcsxHeader[32] = "STv4 PCSX v" PACKAGE_VERSION;
528 // Savestate Versioning!
529 // If you make changes to the savestate version, please increment the value below.
530 static const u32 SaveVersion = 0x8b410006;
532 int SaveState(const char *file) {
539 f = gzopen(file, "wb");
540 if (f == NULL) return -1;
544 gzwrite(f, (void *)PcsxHeader, 32);
545 gzwrite(f, (void *)&SaveVersion, sizeof(u32));
546 gzwrite(f, (void *)&Config.HLE, sizeof(boolean));
548 pMem = (unsigned char *)malloc(128 * 96 * 3);
549 if (pMem == NULL) return -1;
550 GPU_getScreenPic(pMem);
551 gzwrite(f, pMem, 128 * 96 * 3);
557 gzwrite(f, psxM, 0x00200000);
558 gzwrite(f, psxR, 0x00080000);
559 gzwrite(f, psxH, 0x00010000);
560 gzwrite(f, (void *)&psxRegs, sizeof(psxRegs));
563 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
564 gpufP->ulFreezeVersion = 1;
565 GPU_freeze(1, gpufP);
566 gzwrite(f, gpufP, sizeof(GPUFreeze_t));
570 spufP = (SPUFreeze_t *) malloc(16);
571 SPU_freeze(2, spufP);
572 Size = spufP->Size; gzwrite(f, &Size, 4);
574 spufP = (SPUFreeze_t *) malloc(Size);
575 SPU_freeze(1, spufP);
576 gzwrite(f, spufP, Size);
587 new_dyna_after_save();
592 int LoadState(const char *file) {
601 f = gzopen(file, "rb");
602 if (f == NULL) return -1;
604 gzread(f, header, sizeof(header));
605 gzread(f, &version, sizeof(u32));
606 gzread(f, &hle, sizeof(boolean));
608 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
618 gzseek(f, 128 * 96 * 3, SEEK_CUR);
620 gzread(f, psxM, 0x00200000);
621 gzread(f, psxR, 0x00080000);
622 gzread(f, psxH, 0x00010000);
623 gzread(f, (void *)&psxRegs, sizeof(psxRegs));
629 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
630 gzread(f, gpufP, sizeof(GPUFreeze_t));
631 GPU_freeze(0, gpufP);
633 if (HW_GPU_STATUS == 0)
634 HW_GPU_STATUS = GPU_readStatus();
638 spufP = (SPUFreeze_t *)malloc(Size);
639 gzread(f, spufP, Size);
640 SPU_freeze(0, spufP);
655 int CheckState(const char *file) {
661 f = gzopen(file, "rb");
662 if (f == NULL) return -1;
664 gzread(f, header, sizeof(header));
665 gzread(f, &version, sizeof(u32));
666 gzread(f, &hle, sizeof(boolean));
670 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
676 // NET Function Helpers
679 if (NET_recvData == NULL || NET_sendData == NULL)
682 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
683 NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
684 NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
685 NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
686 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
687 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
695 if (NET_recvData == NULL || NET_sendData == NULL)
698 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
699 NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
700 NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
701 NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
702 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
707 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
708 if (tmp != Config.Cpu) {
711 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
712 else psxCpu = &psxRec;
716 if (psxCpu->Init() == -1) {
717 SysClose(); return -1;
725 // remove the leading and trailing spaces in a string
726 void trim(char *str) {
730 // skip leading blanks
731 while (str[pos] <= ' ' && str[pos] > 0)
735 *(dest++) = str[pos];
739 *(dest--) = '\0'; // store the null
741 // remove trailing blanks
742 while (dest >= str && *dest <= ' ' && *dest > 0)
746 // lookup table for crc calculation
747 static unsigned short crctab[256] = {
748 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
749 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
750 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
751 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
752 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
753 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
754 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
755 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
756 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
757 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
758 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
759 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
760 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
761 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
762 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
763 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
764 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
765 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
766 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
767 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
768 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
769 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
770 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
771 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
772 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
773 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
774 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
775 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
776 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
779 u16 calcCrc(u8 *d, int len) {
783 for (i = 0; i < len; i++) {
784 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);