2014-12-03 06:14:59 +00:00
using System ;
using System.IO ;
using System.Collections.Generic ;
using BizHawk.Common ;
//HOW TO USE
//we dont expect anyone to use this fully yet. It's just over-engineered for future use.
//for now, just use it when you truly dont know what to do with a file.
//This system depends heavily on the provided extension. We're not going to exhaustively try every format all the time. If someone loads a cue which is named .sfc, we cant cope with that.
//However, common mistakes will be handled, on an as-needed basis.
//TODO - check for archives too? further, check archive contents (probably just based on filename)?
//TODO - parameter to enable checks vs firmware, game databases
//TODO (in client) - costly hashes could happen only once the file type is known (and a hash for that filetype could be used)
namespace BizHawk.Emulation.Cores
{
/// <summary>
/// Each of these should ideally represent a single file type.
/// However for now they just may resemble a console, and a core would know how to parse some set of those after making its own determination.
/// If formats are very similar but with small differences, and that determination can be made, then it will be in the ExtraInfo in the FileIDResult
/// </summary>
public enum FileIDType
{
None ,
Multiple , //dont think this makes sense. shouldnt the multiple options be returned?
Disc , //an unknown disc
2014-12-14 10:36:03 +00:00
PSX , PSX_EXE , PSF ,
2014-12-11 08:30:37 +00:00
PSP ,
2014-12-03 06:14:59 +00:00
Saturn , MegaCD ,
PCE , SGX , TurboCD ,
2015-03-11 09:46:27 +00:00
INES , FDS , UNIF , NSF ,
2014-12-03 06:14:59 +00:00
SFC , N64 ,
GB , GBC , GBA , NDS ,
COL ,
SG , SMS , GG , S32X ,
SMD , //http://en.wikibooks.org/wiki/Genesis_Programming#ROM_header
WS , WSC , NGC ,
C64 ,
INT ,
A26 , A52 , A78 , LNX ,
2014-12-14 10:36:03 +00:00
JAD , SBI ,
2015-01-19 22:06:17 +00:00
M3U ,
//audio codec formats
WAV , APE , MPC , FLAC ,
MP3 , //can't be ID'd very readily..
//misc disc-related files:
ECM
2014-12-03 06:14:59 +00:00
}
public class FileIDResult
{
public FileIDResult ( )
{
}
public FileIDResult ( FileIDType type , int confidence )
{
FileIDType = type ;
Confidence = confidence ;
}
public FileIDResult ( FileIDType type )
{
FileIDType = type ;
}
/// <summary>
/// a percentage between 0 and 100 assessing the confidence of this result
/// </summary>
public int Confidence ;
/// <summary>
///
/// </summary>
public FileIDType FileIDType ;
/// <summary>
/// extra information which could be easily gotten during the file ID (region, suspected homebrew, CRC invalid, etc.)
/// </summary>
public Dictionary < string , object > ExtraInfo = new Dictionary < string , object > ( ) ;
}
public class FileIDResults : List < FileIDResult >
{
public FileIDResults ( ) { }
public FileIDResults ( FileIDResult item )
{
base . Add ( item ) ;
}
public new void Sort ( )
{
base . Sort ( ( x , y ) = > x . Confidence . CompareTo ( y . Confidence ) ) ;
}
/// <summary>
/// indicates whether the client should try again after mounting the disc image for further inspection
/// </summary>
public bool ShouldTryDisc ;
}
public class FileID
{
/// <summary>
/// parameters for an Identify job
/// </summary>
public class IdentifyParams
{
/// <summary>
/// The extension of the original file (with or without the .)
/// </summary>
public string Extension ;
/// <summary>
/// a seekable stream which can be used
/// </summary>
public Stream SeekableStream ;
/// <summary>
/// the file in question mounted as a disc
/// </summary>
public DiscSystem . Disc Disc ;
}
class IdentifyJob
{
public Stream Stream ;
public string Extension ;
public DiscSystem . Disc Disc ;
}
/// <summary>
/// performs wise heuristics to identify a file.
/// this will attempt to return early if a confident result can be produced.
/// </summary>
public FileIDResults Identify ( IdentifyParams p )
{
IdentifyJob job = new IdentifyJob ( ) {
Stream = p . SeekableStream ,
Disc = p . Disc
} ;
//if we have a disc, that's a separate codepath
if ( job . Disc ! = null )
return IdentifyDisc ( job ) ;
FileIDResults ret = new FileIDResults ( ) ;
string ext = p . Extension ;
if ( ext ! = null )
{
ext = ext . TrimStart ( '.' ) . ToUpper ( ) ;
job . Extension = ext ;
}
if ( job . Extension = = "CUE" )
{
ret . ShouldTryDisc = true ;
return ret ;
}
if ( job . Extension ! = null )
{
//first test everything associated with this extension
ExtensionInfo handler = null ;
if ( ExtensionHandlers . TryGetValue ( ext , out handler ) )
{
foreach ( var del in handler . Testers )
{
var fidr = del ( job ) ;
if ( fidr . FileIDType = = FileIDType . None )
continue ;
ret . Add ( fidr ) ;
}
ret . Sort ( ) ;
//add a low confidence result just based on extension, if it doesnt exist
if ( ret . Find ( ( x ) = > x . FileIDType = = handler . DefaultForExtension ) = = null )
{
var fidr = new FileIDResult ( handler . DefaultForExtension , 5 ) ;
ret . Add ( fidr ) ;
}
}
}
ret . Sort ( ) ;
//if we didnt find anything high confidence, try all the testers (TODO)
return ret ;
}
/// <summary>
/// performs wise heuristics to identify a file (simple version)
/// </summary>
public FileIDType IdentifySimple ( IdentifyParams p )
{
var ret = Identify ( p ) ;
if ( ret . ShouldTryDisc )
return FileIDType . Disc ;
if ( ret . Count = = 0 )
return FileIDType . None ;
else if ( ret . Count = = 1 )
return ret [ 0 ] . FileIDType ;
else if ( ret [ 0 ] . Confidence = = ret [ 1 ] . Confidence )
return FileIDType . Multiple ;
else return ret [ 0 ] . FileIDType ;
}
FileIDResults IdentifyDisc ( IdentifyJob job )
{
2015-07-03 09:11:07 +00:00
var discIdentifier = new DiscSystem . DiscIdentifier ( job . Disc ) ;
2014-12-03 06:14:59 +00:00
//DiscSystem could use some newer approaches from this file (instead of parsing ISO filesystem... maybe?)
2015-07-03 09:11:07 +00:00
switch ( discIdentifier . DetectDiscType ( ) )
2014-12-03 06:14:59 +00:00
{
case DiscSystem . DiscType . SegaSaturn :
return new FileIDResults ( new FileIDResult ( FileIDType . Saturn , 100 ) ) ;
case DiscSystem . DiscType . SonyPSP :
return new FileIDResults ( new FileIDResult ( FileIDType . PSP , 100 ) ) ;
case DiscSystem . DiscType . SonyPSX :
return new FileIDResults ( new FileIDResult ( FileIDType . PSX , 100 ) ) ;
case DiscSystem . DiscType . MegaCD :
return new FileIDResults ( new FileIDResult ( FileIDType . MegaCD , 100 ) ) ;
case DiscSystem . DiscType . TurboCD :
return new FileIDResults ( new FileIDResult ( FileIDType . TurboCD , 5 ) ) ;
case DiscSystem . DiscType . UnknownCDFS :
case DiscSystem . DiscType . UnknownFormat :
default :
return new FileIDResults ( new FileIDResult ( ) ) ;
}
}
class SimpleMagicRecord
{
public int Offset ;
public string Key ;
public int Length = - 1 ;
2015-01-19 22:06:17 +00:00
public Func < Stream , bool > ExtraCheck ;
2014-12-03 06:14:59 +00:00
}
//some of these (NES, UNIF for instance) should be lower confidence probably...
//if you change some of the Length arguments for longer keys, please make notes about why
static class SimpleMagics
{
public static SimpleMagicRecord INES = new SimpleMagicRecord { Offset = 0 , Key = "NES" } ;
2015-03-11 09:46:27 +00:00
public static SimpleMagicRecord UNIF = new SimpleMagicRecord { Offset = 0 , Key = "UNIF" } ;
public static SimpleMagicRecord NSF = new SimpleMagicRecord { Offset = 0 , Key = "NESM\x1A" } ;
2014-12-03 06:14:59 +00:00
2015-01-19 22:06:17 +00:00
public static SimpleMagicRecord FDS_HEADERLESS = new SimpleMagicRecord { Offset = 0 , Key = "\x01*NINTENDO-HVC*" } ;
public static SimpleMagicRecord FDS_HEADER = new SimpleMagicRecord { Offset = 0 , Key = "FDS\x1A" } ;
2014-12-03 06:14:59 +00:00
//the GBA nintendo logo.. we'll only use 16 bytes of it but theyre all here, for reference
//we cant expect these roms to be normally sized, but we may be able to find other features of the header to use for extra checks
public static SimpleMagicRecord GBA = new SimpleMagicRecord { Offset = 4 , Length = 16 , Key = "\x24\xFF\xAE\x51\x69\x9A\xA2\x21\x3D\x84\x82\x0A\x84\xE4\x09\xAD\x11\x24\x8B\x98\xC0\x81\x7F\x21\xA3\x52\xBE\x19\x93\x09\xCE\x20\x10\x46\x4A\x4A\xF8\x27\x31\xEC\x58\xC7\xE8\x33\x82\xE3\xCE\xBF\x85\xF4\xDF\x94\xCE\x4B\x09\xC1\x94\x56\x8A\xC0\x13\x72\xA7\xFC\x9F\x84\x4D\x73\xA3\xCA\x9A\x61\x58\x97\xA3\x27\xFC\x03\x98\x76\x23\x1D\xC7\x61\x03\x04\xAE\x56\xBF\x38\x84\x00\x40\xA7\x0E\xFD\xFF\x52\xFE\x03\x6F\x95\x30\xF1\x97\xFB\xC0\x85\x60\xD6\x80\x25\xA9\x63\xBE\x03\x01\x4E\x38\xE2\xF9\xA2\x34\xFF\xBB\x3E\x03\x44\x78\x00\x90\xCB\x88\x11\x3A\x94\x65\xC0\x7C\x63\x87\xF0\x3C\xAF\xD6\x25\xE4\x8B\x38\x0A\xAC\x72\x21\xD4\xF8\x07" } ;
public static SimpleMagicRecord NDS = new SimpleMagicRecord { Offset = 0xC0 , Length = 16 , Key = "\x24\xFF\xAE\x51\x69\x9A\xA2\x21\x3D\x84\x82\x0A\x84\xE4\x09\xAD\x11\x24\x8B\x98\xC0\x81\x7F\x21\xA3\x52\xBE\x19\x93\x09\xCE\x20\x10\x46\x4A\x4A\xF8\x27\x31\xEC\x58\xC7\xE8\x33\x82\xE3\xCE\xBF\x85\xF4\xDF\x94\xCE\x4B\x09\xC1\x94\x56\x8A\xC0\x13\x72\xA7\xFC\x9F\x84\x4D\x73\xA3\xCA\x9A\x61\x58\x97\xA3\x27\xFC\x03\x98\x76\x23\x1D\xC7\x61\x03\x04\xAE\x56\xBF\x38\x84\x00\x40\xA7\x0E\xFD\xFF\x52\xFE\x03\x6F\x95\x30\xF1\x97\xFB\xC0\x85\x60\xD6\x80\x25\xA9\x63\xBE\x03\x01\x4E\x38\xE2\xF9\xA2\x34\xFF\xBB\x3E\x03\x44\x78\x00\x90\xCB\x88\x11\x3A\x94\x65\xC0\x7C\x63\x87\xF0\x3C\xAF\xD6\x25\xE4\x8B\x38\x0A\xAC\x72\x21\xD4\xF8\x07" } ;
public static SimpleMagicRecord GB = new SimpleMagicRecord { Offset = 0x104 , Length = 16 , Key = "\xCE\xED\x66\x66\xCC\x0D\x00\x0B\x03\x73\x00\x83\x00\x0C\x00\x0D\x00\x08\x11\x1F\x88\x89\x00\x0E\xDC\xCC\x6E\xE6\xDD\xDD\xD9\x99\xBB\xBB\x67\x63\x6E\x0E\xEC\xCC\xDD\xDC\x99\x9F\xBB\xB9\x33\x3E" } ;
public static SimpleMagicRecord S32X = new SimpleMagicRecord { Offset = 0x100 , Key = "SEGA 32X" } ;
public static SimpleMagicRecord SEGAGENESIS = new SimpleMagicRecord { Offset = 0x100 , Key = "SEGA GENESIS" } ;
public static SimpleMagicRecord SEGAMEGADRIVE = new SimpleMagicRecord { Offset = 0x100 , Key = "SEGA MEGA DRIVE" } ;
public static SimpleMagicRecord SEGASATURN = new SimpleMagicRecord { Offset = 0 , Key = "SEGA SEGASATURN" } ;
public static SimpleMagicRecord SEGADISCSYSTEM = new SimpleMagicRecord { Offset = 0 , Key = "SEGADISCSYSTEM" } ;
2014-12-11 08:30:37 +00:00
public static SimpleMagicRecord PSX = new SimpleMagicRecord { Offset = 0x24E0 , Key = " Licensed by Sony Computer Entertainment" } ; //there might be other ideas for checking in mednafen sources, if we need them
public static SimpleMagicRecord PSX_EXE = new SimpleMagicRecord { Key = "PS-X EXE\0" } ;
2014-12-03 06:14:59 +00:00
public static SimpleMagicRecord PSP = new SimpleMagicRecord { Offset = 0x8000 , Key = "\x01CD001\x01\0x00PSP GAME" } ;
//https://sites.google.com/site/atari7800wiki/a78-header
public static SimpleMagicRecord A78 = new SimpleMagicRecord { Offset = 0 , Key = "\x01ATARI7800" } ;
//could be at various offsets?
public static SimpleMagicRecord TMR_SEGA = new SimpleMagicRecord { Offset = 0x7FF0 , Key = "TMR SEGA" } ;
2014-12-14 10:36:03 +00:00
public static SimpleMagicRecord SBI = new SimpleMagicRecord { Key = "SBI\0" } ;
public static SimpleMagicRecord M3U = new SimpleMagicRecord { Key = "#EXTM3U" } ; //note: M3U may not have this. EXTM3U only has it. We'll still catch it by extension though.
2015-01-19 22:06:17 +00:00
public static SimpleMagicRecord ECM = new SimpleMagicRecord { Key = "ECM\0" } ;
public static SimpleMagicRecord FLAC = new SimpleMagicRecord { Key = "fLaC" } ;
public static SimpleMagicRecord MPC = new SimpleMagicRecord { Key = "MP+" , ExtraCheck = ( s ) = > { s . Position + = 3 ; return s . ReadByte ( ) > = 7 ; } } ;
public static SimpleMagicRecord APE = new SimpleMagicRecord { Key = "MAC " } ;
public static SimpleMagicRecord [ ] WAV = new SimpleMagicRecord [ ] {
new SimpleMagicRecord { Offset = 0 , Key = "RIFF" } ,
new SimpleMagicRecord { Offset = 8 , Key = "WAVEfmt " }
} ;
2014-12-03 06:14:59 +00:00
}
class ExtensionInfo
{
public ExtensionInfo ( FileIDType defaultForExtension , FormatTester tester )
{
Testers = new List < FormatTester > ( 1 ) ;
if ( tester ! = null )
Testers . Add ( tester ) ;
DefaultForExtension = defaultForExtension ;
}
public FileIDType DefaultForExtension ;
public List < FormatTester > Testers ;
}
/// <summary>
/// testers to try for each extension, along with a default for the extension
/// </summary>
static Dictionary < string , ExtensionInfo > ExtensionHandlers = new Dictionary < string , ExtensionInfo > {
{ "NES" , new ExtensionInfo ( FileIDType . INES , Test_INES ) } ,
2015-01-19 22:06:17 +00:00
{ "FDS" , new ExtensionInfo ( FileIDType . FDS , Test_FDS ) } ,
2014-12-03 06:14:59 +00:00
{ "GBA" , new ExtensionInfo ( FileIDType . GBA , ( j ) = > Test_Simple ( j , FileIDType . GBA , SimpleMagics . GBA ) ) } ,
{ "NDS" , new ExtensionInfo ( FileIDType . NDS , ( j ) = > Test_Simple ( j , FileIDType . NDS , SimpleMagics . NDS ) ) } ,
{ "UNF" , new ExtensionInfo ( FileIDType . UNIF , Test_UNIF ) } ,
{ "UNIF" , new ExtensionInfo ( FileIDType . UNIF , Test_UNIF ) } ,
{ "GB" , new ExtensionInfo ( FileIDType . GB , Test_GB_GBC ) } ,
{ "GBC" , new ExtensionInfo ( FileIDType . GBC , Test_GB_GBC ) } ,
{ "N64" , new ExtensionInfo ( FileIDType . N64 , Test_N64 ) } ,
{ "Z64" , new ExtensionInfo ( FileIDType . N64 , Test_N64 ) } ,
{ "V64" , new ExtensionInfo ( FileIDType . N64 , Test_N64 ) } ,
{ "A78" , new ExtensionInfo ( FileIDType . A78 , Test_A78 ) } ,
{ "SMS" , new ExtensionInfo ( FileIDType . SMS , Test_SMS ) } ,
{ "BIN" , new ExtensionInfo ( FileIDType . Multiple , Test_BIN_ISO ) } ,
{ "ISO" , new ExtensionInfo ( FileIDType . Multiple , Test_BIN_ISO ) } ,
2014-12-14 10:36:03 +00:00
{ "M3U" , new ExtensionInfo ( FileIDType . M3U , ( j ) = > Test_Simple ( j , FileIDType . M3U , SimpleMagics . M3U ) ) } ,
2014-12-03 06:14:59 +00:00
{ "JAD" , new ExtensionInfo ( FileIDType . Multiple , Test_JAD_JAC ) } ,
{ "JAC" , new ExtensionInfo ( FileIDType . Multiple , Test_JAD_JAC ) } ,
2014-12-14 10:36:03 +00:00
{ "SBI" , new ExtensionInfo ( FileIDType . SBI , ( j ) = > Test_Simple ( j , FileIDType . SBI , SimpleMagics . SBI ) ) } ,
2014-12-03 06:14:59 +00:00
2014-12-11 08:30:37 +00:00
{ "EXE" , new ExtensionInfo ( FileIDType . PSX_EXE , ( j ) = > Test_Simple ( j , FileIDType . PSX_EXE , SimpleMagics . PSX_EXE ) ) } ,
2014-12-03 06:14:59 +00:00
//royal mess
{ "MD" , new ExtensionInfo ( FileIDType . SMD , null ) } ,
{ "SMD" , new ExtensionInfo ( FileIDType . SMD , null ) } ,
{ "GEN" , new ExtensionInfo ( FileIDType . SMD , null ) } ,
//nothing yet...
{ "PSF" , new ExtensionInfo ( FileIDType . PSF , null ) } ,
{ "INT" , new ExtensionInfo ( FileIDType . INT , null ) } ,
{ "SFC" , new ExtensionInfo ( FileIDType . SFC , null ) } ,
{ "SMC" , new ExtensionInfo ( FileIDType . SFC , null ) } ,
{ "LNX" , new ExtensionInfo ( FileIDType . LNX , null ) } ,
{ "SG" , new ExtensionInfo ( FileIDType . SG , null ) } ,
{ "SGX" , new ExtensionInfo ( FileIDType . SGX , null ) } ,
{ "COL" , new ExtensionInfo ( FileIDType . COL , null ) } ,
{ "A52" , new ExtensionInfo ( FileIDType . A52 , null ) } ,
{ "A26" , new ExtensionInfo ( FileIDType . A26 , null ) } ,
{ "PCE" , new ExtensionInfo ( FileIDType . PCE , null ) } ,
{ "GG" , new ExtensionInfo ( FileIDType . GG , null ) } ,
{ "WS" , new ExtensionInfo ( FileIDType . WS , null ) } ,
{ "WSC" , new ExtensionInfo ( FileIDType . WSC , null ) } ,
{ "NGC" , new ExtensionInfo ( FileIDType . NGC , null ) } ,
{ "32X" , new ExtensionInfo ( FileIDType . S32X , ( j ) = > Test_Simple ( j , FileIDType . S32X , SimpleMagics . S32X ) ) } ,
//various C64 formats.. can we distinguish between these?
{ "PRG" , new ExtensionInfo ( FileIDType . C64 , null ) } ,
{ "D64" , new ExtensionInfo ( FileIDType . C64 , null ) } ,
{ "T64" , new ExtensionInfo ( FileIDType . C64 , null ) } ,
{ "G64" , new ExtensionInfo ( FileIDType . C64 , null ) } ,
{ "CRT" , new ExtensionInfo ( FileIDType . C64 , null ) } ,
{ "NIB" , new ExtensionInfo ( FileIDType . C64 , null ) } , //not supported yet
//for now
{ "ROM" , new ExtensionInfo ( FileIDType . Multiple , null ) } , //could be MSX too
2015-01-19 22:06:17 +00:00
{ "MP3" , new ExtensionInfo ( FileIDType . MP3 , null ) } ,
{ "WAV" , new ExtensionInfo ( FileIDType . WAV , ( j ) = > Test_Simple ( j , FileIDType . WAV , SimpleMagics . WAV ) ) } ,
{ "APE" , new ExtensionInfo ( FileIDType . APE , ( j ) = > Test_Simple ( j , FileIDType . APE , SimpleMagics . APE ) ) } ,
{ "MPC" , new ExtensionInfo ( FileIDType . MPC , ( j ) = > Test_Simple ( j , FileIDType . MPC , SimpleMagics . MPC ) ) } ,
{ "FLAC" , new ExtensionInfo ( FileIDType . FLAC , ( j ) = > Test_Simple ( j , FileIDType . FLAC , SimpleMagics . FLAC ) ) } ,
{ "ECM" , new ExtensionInfo ( FileIDType . ECM , ( j ) = > Test_Simple ( j , FileIDType . ECM , SimpleMagics . ECM ) ) } ,
2014-12-03 06:14:59 +00:00
} ;
delegate FileIDResult FormatTester ( IdentifyJob job ) ;
2015-01-19 22:06:17 +00:00
static int [ ] no_offsets = new int [ ] { 0 } ;
2014-12-03 06:14:59 +00:00
/// <summary>
/// checks for the magic string (bytewise ASCII check) at the given address
/// </summary>
2015-01-19 22:06:17 +00:00
static bool CheckMagic ( Stream stream , IEnumerable < SimpleMagicRecord > recs , params int [ ] offsets )
2014-12-03 06:14:59 +00:00
{
if ( offsets . Length = = 0 )
2015-01-19 22:06:17 +00:00
offsets = no_offsets ;
foreach ( int n in offsets )
{
bool ok = true ;
foreach ( var r in recs )
{
if ( ! CheckMagicOne ( stream , r , n ) )
{
ok = false ;
break ;
}
}
if ( ok ) return true ;
}
2014-12-03 06:14:59 +00:00
return false ;
}
2015-01-19 22:06:17 +00:00
static bool CheckMagic ( Stream stream , SimpleMagicRecord rec , params int [ ] offsets )
{
return CheckMagic ( stream , new SimpleMagicRecord [ ] { rec } , offsets ) ;
}
2014-12-03 06:14:59 +00:00
static bool CheckMagicOne ( Stream stream , SimpleMagicRecord rec , int offset )
{
stream . Position = rec . Offset + offset ;
string key = rec . Key ;
int len = rec . Length ;
if ( len = = - 1 )
len = key . Length ;
for ( int i = 0 ; i < len ; i + + )
{
int n = stream . ReadByte ( ) ;
if ( n = = - 1 ) return false ;
if ( n ! = key [ i ] )
return false ;
}
2015-01-19 22:06:17 +00:00
if ( rec . ExtraCheck ! = null )
{
stream . Position = rec . Offset + offset ;
return rec . ExtraCheck ( stream ) ;
}
else return true ;
2014-12-03 06:14:59 +00:00
}
static int ReadByte ( Stream stream , int ofs )
{
stream . Position = ofs ;
return stream . ReadByte ( ) ;
}
static FileIDResult Test_INES ( IdentifyJob job )
{
if ( ! CheckMagic ( job . Stream , SimpleMagics . INES ) )
return new FileIDResult ( ) ;
var ret = new FileIDResult ( FileIDType . INES , 100 ) ;
//an INES file should be a multiple of 8k, with the 16 byte header.
//if it isnt.. this is fishy.
if ( ( ( job . Stream . Length - 16 ) & ( 8 * 1024 - 1 ) ) ! = 0 )
ret . Confidence = 50 ;
return ret ;
}
2015-01-19 22:06:17 +00:00
static FileIDResult Test_FDS ( IdentifyJob job )
{
if ( CheckMagic ( job . Stream , SimpleMagics . FDS_HEADER ) )
return new FileIDResult ( FileIDType . FDS , 90 ) ; //kind of a small header..
if ( CheckMagic ( job . Stream , SimpleMagics . FDS_HEADERLESS ) )
return new FileIDResult ( FileIDType . FDS , 95 ) ;
return new FileIDResult ( ) ;
}
/// <summary>
/// all magics must pass
/// </summary>
static FileIDResult Test_Simple ( IdentifyJob job , FileIDType type , SimpleMagicRecord [ ] magics )
{
var ret = new FileIDResult ( type ) ;
if ( CheckMagic ( job . Stream , magics ) )
return new FileIDResult ( type , 100 ) ;
else
return new FileIDResult ( ) ;
}
2014-12-03 06:14:59 +00:00
static FileIDResult Test_Simple ( IdentifyJob job , FileIDType type , SimpleMagicRecord magic )
{
var ret = new FileIDResult ( type ) ;
if ( CheckMagic ( job . Stream , magic ) )
return new FileIDResult ( type , 100 ) ;
else
return new FileIDResult ( ) ;
}
static FileIDResult Test_UNIF ( IdentifyJob job )
{
if ( ! CheckMagic ( job . Stream , SimpleMagics . UNIF ) )
return new FileIDResult ( ) ;
//TODO - simple parser (for starters, check for a known chunk being next, see http://wiki.nesdev.com/w/index.php/UNIF)
var ret = new FileIDResult ( FileIDType . UNIF , 100 ) ;
return ret ;
}
static FileIDResult Test_GB_GBC ( IdentifyJob job )
{
if ( ! CheckMagic ( job . Stream , SimpleMagics . GB ) )
return new FileIDResult ( ) ;
var ret = new FileIDResult ( FileIDType . GB , 100 ) ;
int type = ReadByte ( job . Stream , 0x143 ) ;
if ( ( type & 0x80 ) ! = 0 )
ret . FileIDType = FileIDType . GBC ;
//could check cart type and rom size for extra info if necessary
return ret ;
}
static FileIDResult Test_SMS ( IdentifyJob job )
{
//http://www.smspower.org/Development/ROMHeader
//actually, not sure how to handle this yet
return new FileIDResult ( ) ;
}
static FileIDResult Test_N64 ( IdentifyJob job )
{
// .Z64 = No swapping
// .N64 = Word Swapped
// .V64 = Byte Swapped
//not sure how to check for these yet...
var ret = new FileIDResult ( FileIDType . N64 , 5 ) ;
if ( job . Extension = = "V64" ) ret . ExtraInfo [ "byteswap" ] = true ;
if ( job . Extension = = "N64" ) ret . ExtraInfo [ "wordswap" ] = true ;
return ret ;
}
static FileIDResult Test_A78 ( IdentifyJob job )
{
int len = ( int ) job . Stream . Length ;
//we may have a header to analyze
if ( len % 1024 = = 128 )
{
if ( CheckMagic ( job . Stream , SimpleMagics . A78 ) )
new FileIDResult ( FileIDType . A78 , 100 ) ;
}
else if ( len % 1024 = = 0 )
{
}
else { }
return new FileIDResult ( 0 ) ;
}
static FileIDResult Test_BIN_ISO ( IdentifyJob job )
{
//ok, this is complicated.
//there are lots of mislabeled bins/isos so lets just treat them the same (mostly)
//if the BIN cant be recognized, but it is small, it is more likely some other rom BIN than a disc (turbocd or other)
if ( job . Extension = = "BIN" )
{
//first we can check for SMD magic words.
//since this extension is ambiguous, we can't be completely sure about it. but it's almost surely accurate
if ( CheckMagic ( job . Stream , SimpleMagics . SEGAGENESIS ) )
{
var ret = new FileIDResult ( FileIDType . SMD , 95 ) ;
ret . ExtraInfo [ "type" ] = "genesis" ;
return ret ;
}
if ( CheckMagic ( job . Stream , SimpleMagics . SEGAMEGADRIVE ) )
{
var ret = new FileIDResult ( FileIDType . SMD , 95 ) ;
ret . ExtraInfo [ "type" ] = "megadrive" ;
}
}
//well... guess it's a disc.
//since it's just a bin, we dont need the user to provide a DiscSystem disc.
//lets just analyze this as best we can.
//for PSX, we have a magic word to look for.
//it's at 0x24E0 with a mode2 (2352 byte) track 1.
//what if its 2048 byte?
//i found a ".iso" which was actually 2352 byte sectors..
//found a hilarious ".bin.iso" which was actually 2352 byte sectors
//so, I think it's possible that every valid PSX disc is mode2 in the track 1
if ( CheckMagic ( job . Stream , SimpleMagics . PSX ) )
{
var ret = new FileIDResult ( FileIDType . PSX , 95 ) ;
//this is an unreliable way to get a PSX game!
ret . ExtraInfo [ "unreliable" ] = true ;
return ret ;
}
//it's not proven that this is reliable. this is actually part of the mode1 CDFS header. perhaps it's mobile?
//if it's mobile, we'll need to mount it as an ISO file here via discsystem
if ( CheckMagic ( job . Stream , SimpleMagics . PSP ) )
return new FileIDResult ( FileIDType . PSP , 95 ) ;
//if this was an ISO, we might discover the magic word at offset 0...
//if it was a mode2/2352 bin, we might discover it at offset 16 (after the sync)
if ( CheckMagic ( job . Stream , SimpleMagics . SEGASATURN , 0 ) )
return new FileIDResult ( FileIDType . Saturn , job . Extension = = "ISO" ? 95 : 90 ) ;
if ( CheckMagic ( job . Stream , SimpleMagics . SEGASATURN , 16 ) )
return new FileIDResult ( FileIDType . Saturn , job . Extension = = "BIN" ? 95 : 90 ) ;
if ( CheckMagic ( job . Stream , SimpleMagics . SEGADISCSYSTEM , 0 ) )
return new FileIDResult ( FileIDType . MegaCD , job . Extension = = "ISO" ? 95 : 90 ) ;
if ( CheckMagic ( job . Stream , SimpleMagics . SEGADISCSYSTEM , 16 ) )
return new FileIDResult ( FileIDType . MegaCD , job . Extension = = "BIN" ? 95 : 90 ) ;
if ( job . Extension = = "ISO" )
return new FileIDResult ( FileIDType . Disc , 1 ) ;
else
return new FileIDResult ( FileIDType . Multiple , 1 ) ;
}
static FileIDResult Test_JAD_JAC ( IdentifyJob job )
{
//TBD
//just mount it as a disc and send it through the disc checker?
return null ;
}
}
}