////////////////////////////////////////////////////////////////////////// // // // NES ROM Cartridge class // // Norix // // written 2001/02/20 // // last modify ----/--/-- // ////////////////////////////////////////////////////////////////////////// #define WIN32_LEAN_AND_MEAN #include #include #include #include #include "typedef.h" #include "macro.h" #include "VirtuaNESres.h" #include "DebugOut.h" #include "App.h" #include "Plugin.h" #include "Pathlib.h" #include "Crclib.h" #include "Config.h" #include "Archive.h" #include "rom.h" #include "romdb.h" #include "mmu.h" #include "ips.h" // // コンストラクタ // ROM::ROM( const char* fname ) { FILE *fp = NULL; LPBYTE temp = NULL; LPBYTE bios = NULL; LONG FileSize; ZEROMEMORY( &header, sizeof(header) ); ZEROMEMORY( path, sizeof(path) ); ZEROMEMORY( name, sizeof(name) ); bPAL = FALSE; bNSF = FALSE; NSF_PAGE_SIZE = 0; lpPRG = lpCHR = lpTrainer = lpDiskBios = lpDisk = NULL; crc = crcall = 0; mapper = 0; diskno = 0; try { if( !(fp = ::fopen( fname, "rb" )) ) { // xxx ファイルを開けません LPCSTR szErrStr = CApp::GetErrorString( IDS_ERROR_OPEN ); ::wsprintf( szErrorString, szErrStr, fname ); throw szErrorString; } // ファイルサイズ取得 ::fseek( fp, 0, SEEK_END ); FileSize = ::ftell( fp ); ::fseek( fp, 0, SEEK_SET ); // ファイルサイズチェック(NESヘッダ+1バイト以上か?) if( FileSize < 17 ) { // ファイルサイズが小さすぎます throw CApp::GetErrorString( IDS_ERROR_SMALLFILE ); } // テンポラリメモリ確保 if( !(temp = (LPBYTE)::malloc( FileSize )) ) { // メモリを確保出来ません throw CApp::GetErrorString( IDS_ERROR_OUTOFMEMORY ); } // サイズ分読み込み if( ::fread( temp, FileSize, 1, fp ) != 1 ) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString( IDS_ERROR_READ ); } FCLOSE( fp ); // ヘッダコピー ::memcpy( &header, temp, sizeof(NESHEADER) ); if( header.ID[0] == 'N' && header.ID[1] == 'E' && header.ID[2] == 'S' && header.ID[3] == 0x1A ) { // ヘッダコピー memcpy( &header, temp, sizeof(NESHEADER) ); } else if( header.ID[0] == 'F' && header.ID[1] == 'D' && header.ID[2] == 'S' && header.ID[3] == 0x1A ) { // ヘッダコピー memcpy( &header, temp, sizeof(NESHEADER) ); } else if( header.ID[0] == 'N' && header.ID[1] == 'E' && header.ID[2] == 'S' && header.ID[3] == 'M') { // ヘッダコピー memcpy( &header, temp, sizeof(NESHEADER) ); } else { FREE( temp ); if( !UnCompress( fname, &temp, (LPDWORD)&FileSize ) ) { // 未対応形式です throw CApp::GetErrorString( IDS_ERROR_UNSUPPORTFORMAT ); } // ヘッダコピー ::memcpy( &header, temp, sizeof(NESHEADER) ); } // Since the zip/fds/nes is defrosted and raw, now apply the patch if( Config.emulator.bAutoIPS ) { LPBYTE ipstemp = NULL; if( !(ipstemp = (LPBYTE)::malloc( FileSize )) ) { // メモリを確保出来ません throw CApp::GetErrorString( IDS_ERROR_OUTOFMEMORY ); } ::memcpy( ipstemp, temp, FileSize ); if( ApplyIPS( fname, ipstemp, FileSize ) ) { ::memcpy( &header, ipstemp, sizeof(NESHEADER) ); ::memcpy( temp, ipstemp, FileSize ); } FREE( ipstemp ); } DWORD PRGoffset, CHRoffset; LONG PRGsize, CHRsize; if( header.ID[0] == 'N' && header.ID[1] == 'E' && header.ID[2] == 'S' && header.ID[3] == 0x1A ) { // 普通のNESファイル PRGsize = (LONG)header.PRG_PAGE_SIZE*0x4000; CHRsize = (LONG)header.CHR_PAGE_SIZE*0x2000; PRGoffset = sizeof(NESHEADER); CHRoffset = PRGoffset + PRGsize; if( IsTRAINER() ) { PRGoffset += 512; CHRoffset += 512; } if( PRGsize <= 0 || (PRGsize+CHRsize) > FileSize ) { // NESヘッダが異常です throw CApp::GetErrorString( IDS_ERROR_INVALIDNESHEADER ); } // PRG BANK if( !(lpPRG = (LPBYTE)malloc( PRGsize )) ) { // メモリを確保出来ません throw CApp::GetErrorString( IDS_ERROR_OUTOFMEMORY ); } ::memcpy( lpPRG, temp+PRGoffset, PRGsize ); // CHR BANK if( CHRsize > 0 ) { if( !(lpCHR = (LPBYTE)malloc( CHRsize )) ) { // メモリを確保出来ません throw CApp::GetErrorString( IDS_ERROR_OUTOFMEMORY ); } if( FileSize >= CHRoffset+CHRsize ) { memcpy( lpCHR, temp+CHRoffset, CHRsize ); } else { // CHRバンク少ない… CHRsize -= (CHRoffset+CHRsize - FileSize); memcpy( lpCHR, temp+CHRoffset, CHRsize ); } } else { lpCHR = NULL; } // Trainer if( IsTRAINER() ) { if( !(lpTrainer = (LPBYTE)malloc( 512 )) ) { // メモリを確保出来ません throw CApp::GetErrorString( IDS_ERROR_OUTOFMEMORY ); } memcpy( lpTrainer, temp+sizeof(NESHEADER), 512 ); } else { lpTrainer = NULL; } } else if( header.ID[0] == 'F' && header.ID[1] == 'D' && header.ID[2] == 'S' && header.ID[3] == 0x1A ) { // FDS(Nintendo Disk System) // ディスクサイズ diskno = header.PRG_PAGE_SIZE; if( FileSize < (16+65500*diskno) ) { // ディスクサイズが異常です throw CApp::GetErrorString( IDS_ERROR_ILLEGALDISKSIZE ); } if( diskno > 8 ) { // 8面より多いディスクは対応していません throw CApp::GetErrorString( IDS_ERROR_UNSUPPORTDISK ); } ZEROMEMORY( &header, sizeof(NESHEADER) ); // ダミーヘッダを作る header.ID[0] = 'N'; header.ID[1] = 'E'; header.ID[2] = 'S'; header.ID[3] = 0x1A; header.PRG_PAGE_SIZE = (BYTE)diskno*4; header.CHR_PAGE_SIZE = 0; header.control1 = 0x40; header.control2 = 0x10; PRGsize = sizeof(NESHEADER)+65500*(LONG)diskno; // PRG BANK if( !(lpPRG = (LPBYTE)malloc( PRGsize )) ) { // メモリを確保出来ません throw CApp::GetErrorString( IDS_ERROR_OUTOFMEMORY ); } // データのバックアップ用 if( !(lpDisk = (LPBYTE)malloc( PRGsize )) ) { // メモリを確保出来ません throw CApp::GetErrorString( IDS_ERROR_OUTOFMEMORY ); } // CHR BANK lpCHR = NULL; ::memcpy( lpPRG, &header, sizeof(NESHEADER) ); ::memcpy( lpPRG+sizeof(NESHEADER), temp+sizeof(NESHEADER), 65500*(LONG)diskno ); // データの書き換え場所特定用 ZEROMEMORY( lpDisk, PRGsize ); // memcpy( lpDisk, &header, sizeof(NESHEADER) ); // memcpy( lpDisk+sizeof(NESHEADER), temp+sizeof(NESHEADER), PRGsize-sizeof(NESHEADER) ); lpPRG[0] = 'F'; lpPRG[1] = 'D'; lpPRG[2] = 'S'; lpPRG[3] = 0x1A; lpPRG[4] = (BYTE)diskno; // DISKSYSTEM BIOSのロード string Path = CPathlib::MakePathExt( CApp::GetModulePath(), "DISKSYS", "ROM" ); if( !(fp = fopen( Path.c_str(), "rb" )) ) { // DISKSYS.ROMがありません throw CApp::GetErrorString( IDS_ERROR_NODISKBIOS ); } ::fseek( fp, 0, SEEK_END ); FileSize = ::ftell( fp ); ::fseek( fp, 0, SEEK_SET ); if( FileSize < 17 ) { // ファイルサイズが小さすぎます throw CApp::GetErrorString( IDS_ERROR_SMALLFILE ); } if( !(bios = (LPBYTE)malloc( FileSize )) ) { // メモリを確保出来ません throw CApp::GetErrorString( IDS_ERROR_OUTOFMEMORY ); } if( fread( bios, FileSize, 1, fp ) != 1 ) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString( IDS_ERROR_READ ); } FCLOSE( fp ); if( !(lpDiskBios = (LPBYTE)malloc( 8*1024 )) ) { // メモリを確保出来ません throw CApp::GetErrorString( IDS_ERROR_OUTOFMEMORY ); } if( bios[0] == 'N' && bios[1] == 'E' && bios[2] == 'S' && bios[3] == 0x1A ) { // NES形式BIOS ::memcpy( lpDiskBios, bios+0x6010, 8*1024 ); } else { // 生BIOS ::memcpy( lpDiskBios, bios, 8*1024 ); } FREE( bios ); } else if( header.ID[0] == 'N' && header.ID[1] == 'E' && header.ID[2] == 'S' && header.ID[3] == 'M') { // NSF bNSF = TRUE; ZEROMEMORY( &header, sizeof(NESHEADER) ); // ヘッダコピー memcpy( &nsfheader, temp, sizeof(NSFHEADER) ); PRGsize = FileSize-sizeof(NSFHEADER); DEBUGOUT( "PRGSIZE:%d\n", PRGsize ); PRGsize = (PRGsize+0x0FFF)&~0x0FFF; DEBUGOUT( "PRGSIZE:%d\n", PRGsize ); if( !(lpPRG = (LPBYTE)malloc( PRGsize )) ) { // メモリを確保出来ません throw CApp::GetErrorString( IDS_ERROR_OUTOFMEMORY ); throw szErrorString; } ZEROMEMORY( lpPRG, PRGsize ); memcpy( lpPRG, temp+sizeof(NSFHEADER), FileSize-sizeof(NSFHEADER) ); NSF_PAGE_SIZE = PRGsize>>12; DEBUGOUT( "PAGESIZE:%d\n", NSF_PAGE_SIZE ); } else { // 未対応形式です throw CApp::GetErrorString( IDS_ERROR_UNSUPPORTFORMAT ); } // パス/ファイル名取得 { string tempstr; tempstr = CPathlib::SplitPath( fname ); ::strcpy( path, tempstr.c_str() ); tempstr = CPathlib::SplitFname( fname ); ::strcpy( name, tempstr.c_str() ); // オリジナルファイル名(フルパス) ::strcpy( fullpath, fname ); } // マッパ設定 if( !bNSF ) { mapper = (header.control1>>4)|(header.control2&0xF0); crc = crcall = crcvrom = 0; if( mapper != 20 ) { // PRG crcの計算(NesToyのPRG CRCと同じ) if( IsTRAINER() ) { crcall = CRC::CrcRev( 512+PRGsize+CHRsize, temp+sizeof(NESHEADER) ); crc = CRC::CrcRev( 512+PRGsize, temp+sizeof(NESHEADER) ); if( CHRsize ) crcvrom = CRC::CrcRev( CHRsize, temp+PRGsize+512+sizeof(NESHEADER) ); } else { crcall = CRC::CrcRev( PRGsize+CHRsize, temp+sizeof(NESHEADER) ); crc = CRC::CrcRev( PRGsize, temp+sizeof(NESHEADER) ); if( CHRsize ) crcvrom = CRC::CrcRev( CHRsize, temp+PRGsize+sizeof(NESHEADER) ); } FilenameCheck( name ); romdatabase.HeaderCorrect( header, crcall, crc ); #include "ROM_Patch.cpp" fdsmakerID = fdsgameID = 0; } else { crc = crcall = crcvrom = 0; fdsmakerID = lpPRG[0x1F]; fdsgameID = (lpPRG[0x20]<<24)|(lpPRG[0x21]<<16)|(lpPRG[0x22]<<8)|(lpPRG[0x23]<<0); } } else { // NSF mapper = 0x0100; // Private mapper crc = crcall = crcvrom = 0; fdsmakerID = fdsgameID = 0; } FREE( temp ); } catch( CHAR* str ) { // 原因がわかっているエラー処理 FCLOSE( fp ); FREE( temp ); FREE( bios ); FREE( lpPRG ); FREE( lpCHR ); FREE( lpTrainer ); FREE( lpDiskBios ); FREE( lpDisk ); throw str; #ifndef _DEBUG } catch(...) { // 一般保護エラーとか出したく無いので...(^^; FCLOSE( fp ); FREE( temp ); FREE( bios ); FREE( lpPRG ); FREE( lpCHR ); FREE( lpTrainer ); FREE( lpDiskBios ); FREE( lpDisk ); #ifdef _DATATRACE // For dis... FREE( PROM_ACCESS ); #endif // 不明なエラーが発生しました throw CApp::GetErrorString( IDS_ERROR_UNKNOWN ); #endif // !_DEBUG } } // // デストラクタ // ROM::~ROM() { FREE( lpPRG ); FREE( lpCHR ); FREE( lpTrainer ); FREE( lpDiskBios ); FREE( lpDisk ); } // // ROMファイルチェック // INT ROM::IsRomFile( const char* fname ) { FILE* fp = NULL; NESHEADER header; if( !(fp = fopen( fname, "rb" )) ) return IDS_ERROR_OPEN; // サイズ分読み込み if( fread( &header, sizeof(header), 1, fp ) != 1 ) { FCLOSE( fp ); return IDS_ERROR_READ; } FCLOSE( fp ); if( header.ID[0] == 'N' && header.ID[1] == 'E' && header.ID[2] == 'S' && header.ID[3] == 0x1A ) { for( INT i = 0; i < 8; i++ ) { if( header.reserved[i] ) return IDS_ERROR_ILLEGALHEADER; } return 0; } else if( header.ID[0] == 'F' && header.ID[1] == 'D' && header.ID[2] == 'S' && header.ID[3] == 0x1A ) { return 0; } else if( header.ID[0] == 'N' && header.ID[1] == 'E' && header.ID[2] == 'S' && header.ID[3] == 'M') { return 0; } else { LPBYTE temp = NULL; LONG size; if( !UnCompress( fname, &temp, (LPDWORD)&size ) ) return IDS_ERROR_UNSUPPORTFORMAT; memcpy( &header, temp, sizeof(NESHEADER) ); FREE( temp ); if( header.ID[0] == 'N' && header.ID[1] == 'E' && header.ID[2] == 'S' && header.ID[3] == 0x1A ) { for( INT i = 0; i < 8; i++ ) { if( header.reserved[i] ) return IDS_ERROR_ILLEGALHEADER; } return 0; } else if( header.ID[0] == 'F' && header.ID[1] == 'D' && header.ID[2] == 'S' && header.ID[3] == 0x1A ) { return 0; } else if( header.ID[0] == 'N' && header.ID[1] == 'E' && header.ID[2] == 'S' && header.ID[3] == 'M') { return 0; } } return IDS_ERROR_UNSUPPORTFORMAT; } // // ROMファイル名のチェック(PALを自動判別) // void ROM::FilenameCheck( const char* fname ) { unsigned char* p = (unsigned char*)fname; while( *p != (unsigned char)'\0' ) { if( *p == (unsigned char)'(' ) { if( _mbsnbicmp( p, (unsigned char*)"(E)", 3 ) == 0 ) { bPAL = TRUE; return; } } p = _mbsinc(p); } }