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 file.\n"));
517 CdromLabel[0] = '\0';
525 static const char PcsxHeader[32] = "STv4 PCSX v" PACKAGE_VERSION;
527 // Savestate Versioning!
528 // If you make changes to the savestate version, please increment the value below.
529 static const u32 SaveVersion = 0x8b410006;
531 int SaveState(const char *file) {
538 f = gzopen(file, "wb");
539 if (f == NULL) return -1;
543 gzwrite(f, (void *)PcsxHeader, 32);
544 gzwrite(f, (void *)&SaveVersion, sizeof(u32));
545 gzwrite(f, (void *)&Config.HLE, sizeof(boolean));
547 pMem = (unsigned char *)malloc(128 * 96 * 3);
548 if (pMem == NULL) return -1;
549 GPU_getScreenPic(pMem);
550 gzwrite(f, pMem, 128 * 96 * 3);
556 gzwrite(f, psxM, 0x00200000);
557 gzwrite(f, psxR, 0x00080000);
558 gzwrite(f, psxH, 0x00010000);
559 gzwrite(f, (void *)&psxRegs, sizeof(psxRegs));
562 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
563 gpufP->ulFreezeVersion = 1;
564 GPU_freeze(1, gpufP);
565 gzwrite(f, gpufP, sizeof(GPUFreeze_t));
569 spufP = (SPUFreeze_t *) malloc(16);
570 SPU_freeze(2, spufP);
571 Size = spufP->Size; gzwrite(f, &Size, 4);
573 spufP = (SPUFreeze_t *) malloc(Size);
574 SPU_freeze(1, spufP);
575 gzwrite(f, spufP, Size);
586 new_dyna_after_save();
591 int LoadState(const char *file) {
600 f = gzopen(file, "rb");
601 if (f == NULL) return -1;
603 gzread(f, header, sizeof(header));
604 gzread(f, &version, sizeof(u32));
605 gzread(f, &hle, sizeof(boolean));
607 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
617 gzseek(f, 128 * 96 * 3, SEEK_CUR);
619 gzread(f, psxM, 0x00200000);
620 gzread(f, psxR, 0x00080000);
621 gzread(f, psxH, 0x00010000);
622 gzread(f, (void *)&psxRegs, sizeof(psxRegs));
628 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
629 gzread(f, gpufP, sizeof(GPUFreeze_t));
630 GPU_freeze(0, gpufP);
632 if (HW_GPU_STATUS == 0)
633 HW_GPU_STATUS = GPU_readStatus();
637 spufP = (SPUFreeze_t *)malloc(Size);
638 gzread(f, spufP, Size);
639 SPU_freeze(0, spufP);
654 int CheckState(const char *file) {
660 f = gzopen(file, "rb");
661 if (f == NULL) return -1;
663 gzread(f, header, sizeof(header));
664 gzread(f, &version, sizeof(u32));
665 gzread(f, &hle, sizeof(boolean));
669 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
675 // NET Function Helpers
678 if (NET_recvData == NULL || NET_sendData == NULL)
681 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
682 NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
683 NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
684 NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
685 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
686 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
694 if (NET_recvData == NULL || NET_sendData == NULL)
697 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
698 NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
699 NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
700 NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
701 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
706 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
707 if (tmp != Config.Cpu) {
710 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
711 else psxCpu = &psxRec;
715 if (psxCpu->Init() == -1) {
716 SysClose(); return -1;
724 // remove the leading and trailing spaces in a string
725 void trim(char *str) {
729 // skip leading blanks
730 while (str[pos] <= ' ' && str[pos] > 0)
734 *(dest++) = str[pos];
738 *(dest--) = '\0'; // store the null
740 // remove trailing blanks
741 while (dest >= str && *dest <= ' ' && *dest > 0)
745 // lookup table for crc calculation
746 static unsigned short crctab[256] = {
747 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
748 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
749 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
750 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
751 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
752 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
753 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
754 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
755 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
756 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
757 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
758 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
759 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
760 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
761 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
762 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
763 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
764 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
765 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
766 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
767 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
768 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
769 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
770 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
771 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
772 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
773 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
774 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
775 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
778 u16 calcCrc(u8 *d, int len) {
782 for (i = 0; i < len; i++) {
783 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);