AxibugEmuOnline/virtuanessrc097-master/NES/ROM.cpp

486 lines
24 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//////////////////////////////////////////////////////////////////////////
// //
// NES ROM Cartridge class //
// Norix //
// written 2001/02/20 //
// last modify ----/--/-- //
//////////////////////////////////////////////////////////////////////////
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <mbstring.h>
#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);
}
}