2014-04-14 12:25:57 +00:00
using System.Collections.Generic ;
2013-12-29 23:54:40 +00:00
using System.IO ;
2021-01-24 20:05:53 +00:00
using System.IO.Compression ;
2017-04-14 19:59:01 +00:00
using System.Linq ;
2013-12-25 19:09:53 +00:00
using BizHawk.Common ;
2022-08-15 18:35:57 +00:00
using BizHawk.Common.IOExtensions ;
2020-06-07 16:06:00 +00:00
using BizHawk.Common.StringExtensions ;
2013-12-25 19:09:53 +00:00
using BizHawk.Emulation.Common ;
2014-01-21 23:54:30 +00:00
using BizHawk.Emulation.Cores ;
2020-05-17 16:05:44 +00:00
using BizHawk.Emulation.Cores.Libretro ;
2022-10-03 08:55:14 +00:00
using BizHawk.Emulation.Cores.Nintendo.Sameboy ;
2013-12-29 23:54:40 +00:00
using BizHawk.Emulation.Cores.Nintendo.SNES ;
using BizHawk.Emulation.Cores.Sony.PSX ;
2020-05-17 16:05:44 +00:00
using BizHawk.Emulation.Cores.Arcades.MAME ;
2013-12-29 23:54:40 +00:00
using BizHawk.Emulation.DiscSystem ;
2013-12-25 19:09:53 +00:00
namespace BizHawk.Client.Common
{
public class RomLoader
{
2020-07-12 11:28:06 +00:00
private class DiscAsset : IDiscAsset
2020-07-11 20:01:12 +00:00
{
public Disc DiscData { get ; set ; }
public DiscType DiscType { get ; set ; }
2020-07-11 22:47:46 +00:00
public string DiscName { get ; set ; }
2020-08-22 19:24:17 +00:00
}
2020-07-12 11:28:06 +00:00
private class RomAsset : IRomAsset
2020-07-11 20:01:12 +00:00
{
public byte [ ] RomData { get ; set ; }
public byte [ ] FileData { get ; set ; }
public string Extension { get ; set ; }
2022-04-24 01:52:03 +00:00
public string RomPath { get ; set ; }
2020-07-12 11:28:06 +00:00
public GameInfo Game { get ; set ; }
2020-07-11 20:01:12 +00:00
}
2020-07-12 15:37:06 +00:00
private class CoreInventoryParameters : ICoreInventoryParameters
{
private readonly RomLoader _parent ;
public CoreInventoryParameters ( RomLoader parent )
{
_parent = parent ;
}
public CoreComm Comm { get ; set ; }
public GameInfo Game { get ; set ; }
public List < IRomAsset > Roms { get ; set ; } = new List < IRomAsset > ( ) ;
public List < IDiscAsset > Discs { get ; set ; } = new List < IDiscAsset > ( ) ;
2020-07-12 17:21:10 +00:00
public bool DeterministicEmulationRequested = > _parent . Deterministic ;
2020-07-12 15:37:06 +00:00
public object FetchSettings ( Type emulatorType , Type settingsType )
= > _parent . GetCoreSettings ( emulatorType , settingsType ) ;
public object FetchSyncSettings ( Type emulatorType , Type syncSettingsType )
= > _parent . GetCoreSyncSettings ( emulatorType , syncSettingsType ) ;
}
2020-06-06 19:41:11 +00:00
private readonly Config _config ;
2020-07-12 01:10:23 +00:00
public RomLoader ( Config config )
2020-06-06 19:41:11 +00:00
{
_config = config ;
}
2017-05-09 12:21:38 +00:00
public enum LoadErrorType
{
2020-02-08 16:46:46 +00:00
Unknown , MissingFirmware , Xml , DiscError
2017-05-09 12:21:38 +00:00
}
2014-07-31 21:15:07 +00:00
2013-12-27 04:41:50 +00:00
// helper methods for the settings events
2020-07-11 17:34:41 +00:00
private TSetting GetCoreSettings < TCore , TSetting > ( )
where TCore : IEmulator
2013-12-26 20:19:28 +00:00
{
2020-07-11 17:34:41 +00:00
return ( TSetting ) GetCoreSettings ( typeof ( TCore ) , typeof ( TSetting ) ) ;
2014-08-23 19:06:37 +00:00
}
2020-07-11 17:34:41 +00:00
private TSync GetCoreSyncSettings < TCore , TSync > ( )
where TCore : IEmulator
2014-08-23 19:06:37 +00:00
{
2020-07-11 17:34:41 +00:00
return ( TSync ) GetCoreSyncSettings ( typeof ( TCore ) , typeof ( TSync ) ) ;
2014-08-23 19:06:37 +00:00
}
2020-07-11 17:34:41 +00:00
private object GetCoreSettings ( Type t , Type settingsType )
2014-08-23 19:06:37 +00:00
{
2020-07-11 17:34:41 +00:00
var e = new SettingsLoadArgs ( t , settingsType ) ;
2021-01-12 14:32:58 +00:00
if ( OnLoadSettings = = null )
throw new InvalidOperationException ( "Frontend failed to provide a settings getter" ) ;
OnLoadSettings ( this , e ) ;
if ( e . Settings ! = null & & e . Settings . GetType ( ) ! = settingsType )
throw new InvalidOperationException ( $"Frontend did not provide the requested settings type: Expected {settingsType}, got {e.Settings.GetType()}" ) ;
2013-12-26 20:19:28 +00:00
return e . Settings ;
}
2017-04-14 19:59:01 +00:00
2020-07-11 17:34:41 +00:00
private object GetCoreSyncSettings ( Type t , Type syncSettingsType )
2013-12-26 20:19:28 +00:00
{
2020-07-11 17:34:41 +00:00
var e = new SettingsLoadArgs ( t , syncSettingsType ) ;
2021-01-12 14:32:58 +00:00
if ( OnLoadSyncSettings = = null )
throw new InvalidOperationException ( "Frontend failed to provide a sync settings getter" ) ;
OnLoadSyncSettings ( this , e ) ;
if ( e . Settings ! = null & & e . Settings . GetType ( ) ! = syncSettingsType )
throw new InvalidOperationException ( $"Frontend did not provide the requested sync settings type: Expected {syncSettingsType}, got {e.Settings.GetType()}" ) ;
2013-12-26 20:19:28 +00:00
return e . Settings ;
}
2013-12-25 19:47:44 +00:00
2020-02-08 16:46:46 +00:00
// For not throwing errors but simply outputting information to the screen
2023-04-09 19:08:37 +00:00
public Action < string , int? > MessageCallback { get ; set ; }
2014-08-02 02:41:12 +00:00
2013-12-25 19:09:53 +00:00
// TODO: reconsider the need for exposing these;
public IEmulator LoadedEmulator { get ; private set ; }
public GameInfo Game { get ; private set ; }
public RomGame Rom { get ; private set ; }
public string CanonicalFullPath { get ; private set ; }
2014-05-24 22:06:08 +00:00
public bool Deterministic { get ; set ; }
2013-12-25 19:09:53 +00:00
2013-12-30 01:17:11 +00:00
public class RomErrorArgs : EventArgs
2013-12-25 19:09:53 +00:00
{
// TODO: think about naming here, what to pass, a lot of potential good information about what went wrong could go here!
2014-07-31 21:15:07 +00:00
public RomErrorArgs ( string message , string systemId , LoadErrorType type )
2013-12-25 19:09:53 +00:00
{
Message = message ;
AttemptedCoreLoad = systemId ;
2014-07-31 21:15:07 +00:00
Type = type ;
2013-12-25 19:09:53 +00:00
}
2017-07-09 18:42:37 +00:00
public RomErrorArgs ( string message , string systemId , string path , bool? det , LoadErrorType type )
2015-07-12 19:30:26 +00:00
: this ( message , systemId , type )
{
Deterministic = det ;
RomPath = path ;
}
2020-02-08 16:46:46 +00:00
public string Message { get ; }
public string AttemptedCoreLoad { get ; }
public string RomPath { get ; }
2015-07-12 19:30:26 +00:00
public bool? Deterministic { get ; set ; }
public bool Retry { get ; set ; }
2020-02-08 16:46:46 +00:00
public LoadErrorType Type { get ; }
2013-12-25 19:09:53 +00:00
}
2013-12-30 01:17:11 +00:00
public class SettingsLoadArgs : EventArgs
2013-12-26 20:19:28 +00:00
{
public object Settings { get ; set ; }
2020-02-08 16:46:46 +00:00
public Type Core { get ; }
2020-07-11 17:34:41 +00:00
public Type SettingsType { get ; }
public SettingsLoadArgs ( Type t , Type s )
2013-12-26 20:19:28 +00:00
{
Core = t ;
2020-07-11 17:34:41 +00:00
SettingsType = s ;
2013-12-26 20:19:28 +00:00
Settings = null ;
}
}
2013-12-29 23:54:40 +00:00
2013-12-26 20:19:28 +00:00
public delegate void SettingsLoadEventHandler ( object sender , SettingsLoadArgs e ) ;
public event SettingsLoadEventHandler OnLoadSettings ;
public event SettingsLoadEventHandler OnLoadSyncSettings ;
2015-07-12 19:30:26 +00:00
public delegate void LoadErrorEventHandler ( object sender , RomErrorArgs e ) ;
2013-12-25 19:09:53 +00:00
public event LoadErrorEventHandler OnLoadError ;
2013-12-29 23:54:40 +00:00
public Func < HawkFile , int? > ChooseArchive { get ; set ; }
2013-12-25 19:09:53 +00:00
2014-04-14 01:59:57 +00:00
public Func < RomGame , string > ChoosePlatform { get ; set ; }
2014-12-22 22:21:51 +00:00
// in case we get sent back through the picker more than once, use the same choice the second time
2020-02-08 16:46:46 +00:00
private int? _previousChoice ;
2013-12-25 19:09:53 +00:00
private int? HandleArchive ( HawkFile file )
{
2020-02-08 16:46:46 +00:00
if ( _previousChoice . HasValue )
2017-04-14 19:59:01 +00:00
{
2020-02-08 16:46:46 +00:00
return _previousChoice ;
2017-04-14 19:59:01 +00:00
}
2014-12-22 22:21:51 +00:00
2013-12-25 19:09:53 +00:00
if ( ChooseArchive ! = null )
{
2020-02-08 16:46:46 +00:00
_previousChoice = ChooseArchive ( file ) ;
return _previousChoice ;
2013-12-25 19:09:53 +00:00
}
return null ;
}
2020-02-08 16:46:46 +00:00
// May want to phase out this method in favor of the overload with more parameters
2014-07-31 21:15:07 +00:00
private void DoLoadErrorCallback ( string message , string systemId , LoadErrorType type = LoadErrorType . Unknown )
2013-12-25 19:09:53 +00:00
{
2017-04-14 19:59:01 +00:00
OnLoadError ? . Invoke ( this , new RomErrorArgs ( message , systemId , type ) ) ;
2013-12-25 19:09:53 +00:00
}
2015-07-12 19:30:26 +00:00
private void DoLoadErrorCallback ( string message , string systemId , string path , bool det , LoadErrorType type = LoadErrorType . Unknown )
{
2017-04-14 19:59:01 +00:00
OnLoadError ? . Invoke ( this , new RomErrorArgs ( message , systemId , path , det , type ) ) ;
2015-07-12 19:30:26 +00:00
}
2019-12-06 18:34:20 +00:00
public IOpenAdvanced OpenAdvanced { get ; set ; }
2015-11-06 14:31:50 +00:00
2017-04-14 19:59:01 +00:00
private bool HandleArchiveBinding ( HawkFile file )
2015-11-07 08:29:04 +00:00
{
// try binding normal rom extensions first
if ( ! file . IsBound )
{
2021-08-23 23:56:17 +00:00
file . BindSoleItemOf ( RomFileExtensions . AutoloadFromArchive ) ;
2015-11-07 08:29:04 +00:00
}
2021-08-24 00:20:42 +00:00
// ...including unrecognised extensions that the user has set a platform for
if ( ! file . IsBound )
{
var exts = _config . PreferredPlatformsForExtensions . Where ( static kvp = > ! string . IsNullOrEmpty ( kvp . Value ) )
. Select ( static kvp = > kvp . Key )
. ToList ( ) ;
if ( exts . Count is not 0 ) file . BindSoleItemOf ( exts ) ;
}
2015-11-07 08:29:04 +00:00
// if we have an archive and need to bind something, then pop the dialog
if ( file . IsArchive & & ! file . IsBound )
{
int? result = HandleArchive ( file ) ;
if ( result . HasValue )
{
file . BindArchiveMember ( result . Value ) ;
}
else
{
return false ;
}
}
CanonicalFullPath = file . CanonicalFullPath ;
return true ;
}
2020-10-02 04:32:56 +00:00
private GameInfo MakeGameFromDisc ( Disc disc , string ext , string name )
2020-06-07 08:46:49 +00:00
{
// TODO - use more sophisticated IDer
var discType = new DiscIdentifier ( disc ) . DetectDiscType ( ) ;
var discHasher = new DiscHasher ( disc ) ;
2024-09-14 12:29:01 +00:00
var discHash = discHasher . CalculateBizHash ( discType ) ;
2020-06-07 08:46:49 +00:00
2020-12-04 21:39:52 +00:00
var game = Database . CheckDatabase ( discHash ) ;
2022-08-05 23:21:43 +00:00
if ( game is not null ) return game ;
// else try to use our wizard methods
game = new GameInfo { Name = name , Hash = discHash } ;
Exception NoCoreForSystem ( string sysID )
2020-06-07 08:46:49 +00:00
{
2022-08-05 23:21:43 +00:00
// no supported emulator core for these (yet)
game . System = sysID ;
return new NoAvailableCoreException ( sysID ) ;
}
switch ( discType )
{
case DiscType . SegaSaturn :
game . System = VSystemID . Raw . SAT ;
break ;
case DiscType . MegaCD :
game . System = VSystemID . Raw . GEN ;
break ;
case DiscType . PCFX :
game . System = VSystemID . Raw . PCFX ;
break ;
case DiscType . TurboGECD :
case DiscType . TurboCD :
game . System = VSystemID . Raw . PCE ;
break ;
2023-03-11 09:26:21 +00:00
case DiscType . JaguarCD :
game . System = VSystemID . Raw . Jaguar ;
break ;
2022-08-05 23:21:43 +00:00
case DiscType . Amiga :
throw NoCoreForSystem ( VSystemID . Raw . Amiga ) ;
case DiscType . CDi :
throw NoCoreForSystem ( VSystemID . Raw . PhillipsCDi ) ;
case DiscType . Dreamcast :
throw NoCoreForSystem ( VSystemID . Raw . Dreamcast ) ;
case DiscType . GameCube :
throw NoCoreForSystem ( VSystemID . Raw . GameCube ) ;
case DiscType . NeoGeoCD :
throw NoCoreForSystem ( VSystemID . Raw . NeoGeoCD ) ;
case DiscType . Panasonic3DO :
throw NoCoreForSystem ( VSystemID . Raw . Panasonic3DO ) ;
case DiscType . Playdia :
throw NoCoreForSystem ( VSystemID . Raw . Playdia ) ;
case DiscType . SonyPS2 :
throw NoCoreForSystem ( VSystemID . Raw . PS2 ) ;
case DiscType . SonyPSP :
throw NoCoreForSystem ( VSystemID . Raw . PSP ) ;
case DiscType . Wii :
throw NoCoreForSystem ( VSystemID . Raw . Wii ) ;
case DiscType . AudioDisc :
case DiscType . UnknownCDFS :
case DiscType . UnknownFormat :
game . System = _config . TryGetChosenSystemForFileExt ( ext , out var sysID ) ? sysID : VSystemID . Raw . NULL ;
break ;
default : //"for an unknown disc, default to psx instead of pce-cd, since that is far more likely to be what they are attempting to open" [5e07ab3ec3b8b8de9eae71b489b55d23a3909f55, year 2015]
case DiscType . SonyPSX :
game . System = VSystemID . Raw . PSX ;
break ;
2020-06-07 08:46:49 +00:00
}
2020-10-02 04:32:56 +00:00
return game ;
}
2021-01-14 18:33:14 +00:00
private bool LoadDisc ( string path , CoreComm nextComm , HawkFile file , string ext , string forcedCoreName , out IEmulator nextEmulator , out GameInfo game )
2020-10-02 04:32:56 +00:00
{
var disc = DiscExtensions . CreateAnyType ( path , str = > DoLoadErrorCallback ( str , "???" , LoadErrorType . DiscError ) ) ;
if ( disc = = null )
{
game = null ;
nextEmulator = null ;
return false ;
}
game = MakeGameFromDisc ( disc , ext , Path . GetFileNameWithoutExtension ( file . Name ) ) ;
2020-06-07 08:46:49 +00:00
2020-07-12 17:21:10 +00:00
var cip = new CoreInventoryParameters ( this )
2020-06-07 08:46:49 +00:00
{
2020-07-12 17:21:10 +00:00
Comm = nextComm ,
Game = game ,
2020-08-22 19:24:17 +00:00
Discs =
2020-06-07 08:46:49 +00:00
{
2020-07-12 17:21:10 +00:00
new DiscAsset
{
DiscData = disc ,
DiscType = new DiscIdentifier ( disc ) . DetectDiscType ( ) ,
DiscName = Path . GetFileNameWithoutExtension ( path )
}
} ,
} ;
2021-01-14 18:33:14 +00:00
nextEmulator = MakeCoreFromCoreInventory ( cip , forcedCoreName ) ;
2020-06-07 13:39:21 +00:00
return true ;
2020-06-07 08:46:49 +00:00
}
2021-01-14 18:33:14 +00:00
private void LoadM3U ( string path , CoreComm nextComm , HawkFile file , string forcedCoreName , out IEmulator nextEmulator , out GameInfo game )
2020-06-07 08:46:49 +00:00
{
2020-10-02 04:32:56 +00:00
M3U_File m3u ;
using ( var sr = new StreamReader ( path ) )
m3u = M3U_File . Read ( sr ) ;
if ( m3u . Entries . Count = = 0 )
throw new InvalidOperationException ( "Can't load an empty M3U" ) ;
m3u . Rebase ( Path . GetDirectoryName ( path ) ) ;
var discs = m3u . Entries
. Select ( e = > e . Path )
. Where ( p = > Disc . IsValidExtension ( Path . GetExtension ( p ) ) )
2024-06-09 18:58:13 +00:00
. Select ( p = > ( p , d : DiscExtensions . CreateAnyType ( p , str = > DoLoadErrorCallback ( str , "???" , LoadErrorType . DiscError ) ) ) )
2020-10-02 04:32:56 +00:00
. Where ( a = > a . d ! = null )
. Select ( a = > ( IDiscAsset ) new DiscAsset
{
DiscData = a . d ,
DiscType = new DiscIdentifier ( a . d ) . DetectDiscType ( ) ,
DiscName = Path . GetFileNameWithoutExtension ( a . p )
} )
. ToList ( ) ;
2024-06-09 18:58:13 +00:00
if ( discs . Count = = 0 )
2020-10-02 04:32:56 +00:00
throw new InvalidOperationException ( "Couldn't load any contents of the M3U as discs" ) ;
game = MakeGameFromDisc ( discs [ 0 ] . DiscData , Path . GetExtension ( m3u . Entries [ 0 ] . Path ) , discs [ 0 ] . DiscName ) ;
var cip = new CoreInventoryParameters ( this )
{
Comm = nextComm ,
Game = game ,
Discs = discs
} ;
2021-01-14 18:33:14 +00:00
nextEmulator = MakeCoreFromCoreInventory ( cip , forcedCoreName ) ;
2020-06-07 08:46:49 +00:00
}
2021-01-13 17:48:45 +00:00
private IEmulator MakeCoreFromCoreInventory ( CoreInventoryParameters cip , string forcedCoreName = null )
2020-07-12 16:52:27 +00:00
{
2021-01-13 17:48:45 +00:00
IReadOnlyCollection < CoreInventory . Core > cores ;
if ( forcedCoreName ! = null )
{
var singleCore = CoreInventory . Instance . GetCores ( cip . Game . System ) . SingleOrDefault ( c = > c . Name = = forcedCoreName ) ;
cores = singleCore ! = null ? new [ ] { singleCore } : Array . Empty < CoreInventory . Core > ( ) ;
}
else
{
2023-05-02 18:28:56 +00:00
_ = _config . PreferredCores . TryGetValue ( cip . Game . System , out var preferredCore ) ;
2021-01-13 17:48:45 +00:00
var dbForcedCoreName = cip . Game . ForcedCore ;
cores = CoreInventory . Instance . GetCores ( cip . Game . System )
. OrderBy ( c = >
2020-07-12 21:10:01 +00:00
{
2021-01-13 17:48:45 +00:00
if ( c . Name = = preferredCore )
{
return ( int ) CorePriority . UserPreference ;
}
2020-07-12 21:10:01 +00:00
2023-06-08 16:42:50 +00:00
if ( string . Equals ( c . Name , dbForcedCoreName , StringComparison . OrdinalIgnoreCase ) )
2021-01-13 17:48:45 +00:00
{
return ( int ) CorePriority . GameDbPreference ;
}
2020-07-12 21:10:01 +00:00
2021-01-13 17:48:45 +00:00
return ( int ) c . Priority ;
} )
. ToList ( ) ;
if ( cores . Count = = 0 ) throw new InvalidOperationException ( "No core was found to try on the game" ) ;
}
2020-07-12 16:52:27 +00:00
var exceptions = new List < Exception > ( ) ;
foreach ( var core in cores )
{
try
{
return core . Create ( cip ) ;
}
catch ( Exception e )
{
2021-05-16 15:57:30 +00:00
if ( _config . DontTryOtherCores | | e is MissingFirmwareException | | e . InnerException is MissingFirmwareException )
2020-08-24 19:52:49 +00:00
throw ;
2020-07-12 16:52:27 +00:00
exceptions . Add ( e ) ;
}
}
2020-07-12 17:21:10 +00:00
throw new AggregateException ( "No core could load the game" , exceptions ) ;
2020-07-12 16:52:27 +00:00
}
2023-04-09 00:05:52 +00:00
private void LoadOther (
CoreComm nextComm ,
HawkFile file ,
string ext ,
string forcedCoreName ,
out IEmulator nextEmulator ,
out RomGame rom ,
out GameInfo game ,
out bool cancel )
2020-06-07 08:46:49 +00:00
{
cancel = false ;
2020-06-07 13:39:21 +00:00
rom = new RomGame ( file ) ;
2020-06-07 08:46:49 +00:00
// hacky for now
2023-04-09 00:05:52 +00:00
rom . GameInfo . System = ext switch
2020-06-07 08:46:49 +00:00
{
2022-10-03 22:49:09 +00:00
".exe" = > VSystemID . Raw . PSX ,
".nsf" = > VSystemID . Raw . NES ,
".gbs" = > VSystemID . Raw . GB ,
_ = > rom . GameInfo . System ,
} ;
2020-06-07 08:46:49 +00:00
2021-07-27 05:12:31 +00:00
Util . DebugWriteLine ( rom . GameInfo . System ) ;
2020-06-07 08:46:49 +00:00
if ( string . IsNullOrEmpty ( rom . GameInfo . System ) )
{
// Has the user picked a preference for this extension?
2021-02-22 19:35:05 +00:00
if ( _config . TryGetChosenSystemForFileExt ( rom . Extension . ToLowerInvariant ( ) , out var systemID ) )
2020-06-07 08:46:49 +00:00
{
2021-02-22 19:35:05 +00:00
rom . GameInfo . System = systemID ;
2020-06-07 08:46:49 +00:00
}
else if ( ChoosePlatform ! = null )
{
var result = ChoosePlatform ( rom ) ;
if ( ! string . IsNullOrEmpty ( result ) )
{
rom . GameInfo . System = result ;
}
else
{
cancel = true ;
}
}
}
2020-06-07 13:39:21 +00:00
game = rom . GameInfo ;
2020-06-07 08:46:49 +00:00
2020-06-07 13:39:21 +00:00
nextEmulator = null ;
2020-07-12 00:31:36 +00:00
if ( game . System = = null )
return ; // The user picked nothing in the Core picker
2020-06-07 16:06:00 +00:00
2020-06-07 08:46:49 +00:00
switch ( game . System )
{
2021-11-10 03:00:03 +00:00
case VSystemID . Raw . GB :
case VSystemID . Raw . GBC :
2023-04-09 00:05:52 +00:00
if ( ext = = ".gbs" )
2022-10-03 22:49:09 +00:00
{
nextEmulator = new Sameboy (
nextComm ,
rom . GameInfo ,
rom . FileData ,
GetCoreSettings < Sameboy , Sameboy . SameboySettings > ( ) ,
GetCoreSyncSettings < Sameboy , Sameboy . SameboySyncSettings > ( )
) ;
return ;
}
2020-06-07 16:06:00 +00:00
if ( _config . GbAsSgb )
2020-06-07 08:46:49 +00:00
{
2021-11-10 03:00:03 +00:00
game . System = VSystemID . Raw . SGB ;
2020-06-07 08:46:49 +00:00
}
break ;
}
2020-07-12 16:52:27 +00:00
var cip = new CoreInventoryParameters ( this )
2020-07-12 15:37:06 +00:00
{
Comm = nextComm ,
Game = game ,
Roms =
{
new RomAsset
{
RomData = rom . RomData ,
FileData = rom . FileData ,
Extension = rom . Extension ,
2022-04-24 01:52:03 +00:00
RomPath = file . FullPathWithoutMember ,
2020-07-12 15:37:06 +00:00
Game = game
}
} ,
2020-07-12 16:52:27 +00:00
} ;
2021-01-13 17:48:45 +00:00
nextEmulator = MakeCoreFromCoreInventory ( cip , forcedCoreName ) ;
2020-06-07 08:46:49 +00:00
}
2020-06-07 13:39:21 +00:00
private void LoadPSF ( string path , CoreComm nextComm , HawkFile file , out IEmulator nextEmulator , out RomGame rom , out GameInfo game )
2020-06-07 08:46:49 +00:00
{
2021-01-23 14:14:51 +00:00
// TODO: Why does the PSF loader need CbDeflater provided? Surely this is a matter internal to it.
2020-06-07 16:06:00 +00:00
static byte [ ] CbDeflater ( Stream instream , int size )
2020-06-07 08:46:49 +00:00
{
2022-08-15 18:35:57 +00:00
return new GZipStream ( instream , CompressionMode . Decompress ) . ReadAllBytes ( ) ;
2020-06-07 09:36:42 +00:00
}
2020-06-07 16:06:00 +00:00
var psf = new PSF ( ) ;
2020-06-07 09:36:42 +00:00
psf . Load ( path , CbDeflater ) ;
2020-06-07 16:06:00 +00:00
nextEmulator = new Octoshock (
nextComm ,
psf ,
2020-07-11 17:34:41 +00:00
GetCoreSettings < Octoshock , Octoshock . Settings > ( ) ,
GetCoreSyncSettings < Octoshock , Octoshock . SyncSettings > ( )
2020-06-07 16:06:00 +00:00
) ;
2020-06-07 08:46:49 +00:00
// total garbage, this
2020-06-07 13:39:21 +00:00
rom = new RomGame ( file ) ;
game = rom . GameInfo ;
2020-06-07 08:46:49 +00:00
}
2024-04-30 05:20:24 +00:00
// HACK due to MAME wanting CHDs as hard drives / handling it on its own (bad design, I know!)
// only matters for XML, as CHDs are never the "main" rom for MAME
// (in general, this is kind of bad as CHD hard drives might be useful for other future cores?)
private static bool IsDiscForXML ( string system , string path )
{
var ext = Path . GetExtension ( path ) ;
if ( system = = VSystemID . Raw . Arcade & & ext . ToLowerInvariant ( ) = = ".chd" )
{
return false ;
}
return Disc . IsValidExtension ( ext ) ;
}
2021-01-14 18:33:14 +00:00
private bool LoadXML ( string path , CoreComm nextComm , HawkFile file , string forcedCoreName , out IEmulator nextEmulator , out RomGame rom , out GameInfo game )
2020-06-07 08:46:49 +00:00
{
2020-06-07 13:39:21 +00:00
nextEmulator = null ;
rom = null ;
game = null ;
2020-06-07 08:46:49 +00:00
try
{
var xmlGame = XmlGame . Create ( file ) ; // if load fails, are we supposed to retry as a bsnes XML????????
2020-06-07 13:39:21 +00:00
game = xmlGame . GI ;
2020-06-07 08:46:49 +00:00
2020-07-12 17:45:33 +00:00
var system = game . System ;
var cip = new CoreInventoryParameters ( this )
2020-07-12 11:08:52 +00:00
{
2020-07-12 17:45:33 +00:00
Comm = nextComm ,
Game = game ,
Roms = xmlGame . Assets
2024-04-30 05:20:24 +00:00
. Where ( kvp = > ! IsDiscForXML ( system , kvp . Key ) )
2020-07-12 17:45:33 +00:00
. Select ( kvp = > ( IRomAsset ) new RomAsset
{
RomData = kvp . Value ,
FileData = kvp . Value , // TODO: Hope no one needed anything special here
Extension = Path . GetExtension ( kvp . Key ) ,
2023-07-28 12:33:56 +00:00
RomPath = Path . GetFullPath ( Path . Combine ( Path . GetDirectoryName ( path . SubstringBefore ( '|' ) ) ! , kvp . Key ! ) ) ,
2020-07-12 17:45:33 +00:00
Game = Database . GetGameInfo ( kvp . Value , Path . GetFileName ( kvp . Key ) )
} )
. ToList ( ) ,
Discs = xmlGame . AssetFullPaths
2024-04-30 05:20:24 +00:00
. Where ( p = > IsDiscForXML ( system , p ) )
2023-07-28 12:33:56 +00:00
. Select ( discPath = > ( p : discPath , d : DiscExtensions . CreateAnyType ( discPath , str = > DoLoadErrorCallback ( str , system , LoadErrorType . DiscError ) ) ) )
2020-07-12 11:08:52 +00:00
. Where ( a = > a . d ! = null )
2020-07-12 11:28:06 +00:00
. Select ( a = > ( IDiscAsset ) new DiscAsset
2020-07-12 11:08:52 +00:00
{
DiscData = a . d ,
2020-07-12 17:45:33 +00:00
DiscType = new DiscIdentifier ( a . d ) . DetectDiscType ( ) ,
2020-07-12 11:08:52 +00:00
DiscName = Path . GetFileNameWithoutExtension ( a . p )
} )
2020-07-12 17:45:33 +00:00
. ToList ( ) ,
} ;
2021-01-14 18:33:14 +00:00
nextEmulator = MakeCoreFromCoreInventory ( cip , forcedCoreName ) ;
2020-07-12 17:45:33 +00:00
return true ;
2020-06-07 08:46:49 +00:00
}
catch ( Exception ex )
{
try
{
// need to get rid of this hack at some point
2020-06-07 13:39:21 +00:00
rom = new RomGame ( file ) ;
game = rom . GameInfo ;
2021-11-10 03:00:03 +00:00
game . System = VSystemID . Raw . SNES ;
2020-06-07 16:06:00 +00:00
nextEmulator = new LibsnesCore (
game ,
null ,
rom . FileData ,
Path . GetDirectoryName ( path . SubstringBefore ( '|' ) ) ,
nextComm ,
2020-07-11 17:34:41 +00:00
GetCoreSettings < LibsnesCore , LibsnesCore . SnesSettings > ( ) ,
GetCoreSyncSettings < LibsnesCore , LibsnesCore . SnesSyncSettings > ( )
2020-06-07 16:06:00 +00:00
) ;
2020-06-07 13:39:21 +00:00
return true ;
2020-06-07 08:46:49 +00:00
}
catch
{
2021-11-21 08:59:56 +00:00
DoLoadErrorCallback ( ex . ToString ( ) , VSystemID . Raw . GBL , LoadErrorType . Xml ) ;
2020-06-07 13:39:21 +00:00
return false ;
2020-06-07 08:46:49 +00:00
}
}
}
2023-04-09 00:05:52 +00:00
private void LoadMAME (
string path ,
CoreComm nextComm ,
HawkFile file ,
string ext ,
out IEmulator nextEmulator ,
out RomGame rom ,
out GameInfo game ,
out bool cancel )
2022-11-22 02:25:18 +00:00
{
try
{
2023-04-09 00:05:52 +00:00
LoadOther ( nextComm , file , ext : ext , forcedCoreName : null , out nextEmulator , out rom , out game , out cancel ) ;
2022-11-22 02:25:18 +00:00
}
catch ( Exception mex ) // ok, MAME threw, let's try another core...
{
try
{
using var f = new HawkFile ( path , allowArchives : true ) ;
if ( ! HandleArchiveBinding ( f ) ) throw ;
2023-04-09 00:05:52 +00:00
LoadOther ( nextComm , f , ext : ext , forcedCoreName : null , out nextEmulator , out rom , out game , out cancel ) ;
2022-11-22 02:25:18 +00:00
}
catch ( Exception oex )
{
if ( mex = = oex ) throw mex ;
throw new AggregateException ( mex , oex ) ;
}
}
}
2021-01-13 17:48:45 +00:00
public bool LoadRom ( string path , CoreComm nextComm , string launchLibretroCore , string forcedCoreName = null , int recursiveCount = 0 )
2020-05-17 08:14:32 +00:00
{
2020-05-21 05:45:08 +00:00
if ( path = = null ) return false ;
2020-05-17 16:05:44 +00:00
if ( recursiveCount > 1 ) // hack to stop recursive calls from endlessly rerunning if we can't load it
2013-12-25 19:09:53 +00:00
{
2020-05-17 16:05:44 +00:00
DoLoadErrorCallback ( "Failed multiple attempts to load ROM." , "" ) ;
2013-12-25 19:09:53 +00:00
return false ;
}
2021-01-20 20:20:55 +00:00
bool allowArchives = true ;
2022-11-10 08:05:25 +00:00
if ( OpenAdvanced is OpenAdvanced_MAME | | MAMEMachineDB . IsMAMEMachine ( path ) ) allowArchives = false ;
2021-01-20 20:20:55 +00:00
using var file = new HawkFile ( path , false , allowArchives ) ;
2022-05-05 08:49:29 +00:00
if ( ! file . Exists & & OpenAdvanced is not OpenAdvanced_LibretroNoGame ) return false ; // if the provided file doesn't even exist, give up! (unless libretro no game is used)
2020-03-14 19:22:23 +00:00
2020-05-17 16:05:44 +00:00
CanonicalFullPath = file . CanonicalFullPath ;
2015-11-09 01:18:08 +00:00
2020-06-07 16:06:00 +00:00
IEmulator nextEmulator ;
2020-05-17 16:05:44 +00:00
RomGame rom = null ;
GameInfo game = null ;
2020-05-17 08:14:32 +00:00
2020-05-17 16:05:44 +00:00
try
2020-05-17 08:14:32 +00:00
{
2020-06-07 09:59:18 +00:00
var cancel = false ;
2020-05-17 08:14:32 +00:00
2022-05-05 08:49:29 +00:00
if ( OpenAdvanced is OpenAdvanced_Libretro or OpenAdvanced_LibretroNoGame )
2020-05-17 08:14:32 +00:00
{
2020-05-17 16:05:44 +00:00
// must be done before LoadNoGame (which triggers retro_init and the paths to be consumed by the core)
// game name == name of core
2021-11-10 03:00:03 +00:00
Game = game = new GameInfo { Name = Path . GetFileNameWithoutExtension ( launchLibretroCore ) , System = VSystemID . Raw . Libretro } ;
2022-12-10 12:45:00 +00:00
var retro = new LibretroHost ( nextComm , game , launchLibretroCore ) ;
2020-05-17 16:05:44 +00:00
nextEmulator = retro ;
if ( retro . Description . SupportsNoGame & & string . IsNullOrEmpty ( path ) )
2020-05-17 08:14:32 +00:00
{
2020-05-17 16:05:44 +00:00
// if we are allowed to run NoGame and we don't have a game, boot up the core that way
2020-06-07 16:06:00 +00:00
if ( ! retro . LoadNoGame ( ) )
2020-05-17 16:05:44 +00:00
{
2021-11-10 03:00:03 +00:00
DoLoadErrorCallback ( "LibretroNoGame failed to load. This is weird" , VSystemID . Raw . Libretro ) ;
2020-05-17 16:05:44 +00:00
retro . Dispose ( ) ;
return false ;
}
2020-02-22 18:29:12 +00:00
}
else
{
2020-05-17 16:05:44 +00:00
bool ret ;
2020-02-22 18:29:12 +00:00
2020-05-17 16:05:44 +00:00
// if the core requires an archive file, then try passing the filename of the archive
// (but do we ever need to actually load the contents of the archive file into ram?)
if ( retro . Description . NeedsArchives )
{
if ( file . IsArchiveMember )
{
throw new InvalidOperationException ( "Should not have bound file member for libretro block_extract core" ) ;
}
2017-04-14 19:59:01 +00:00
2020-05-17 16:05:44 +00:00
ret = retro . LoadPath ( file . FullPathWithoutMember ) ;
}
else
{
// otherwise load the data or pass the filename, as requested. but..
if ( retro . Description . NeedsRomAsPath & & file . IsArchiveMember )
{
throw new InvalidOperationException ( "Cannot pass archive member to libretro needs_fullpath core" ) ;
}
2015-11-06 14:31:50 +00:00
2020-06-07 16:06:00 +00:00
ret = retro . Description . NeedsRomAsPath
? retro . LoadPath ( file . FullPathWithoutMember )
: HandleArchiveBinding ( file ) & & retro . LoadData ( file . ReadAllBytes ( ) , file . Name ) ;
2020-05-17 16:05:44 +00:00
}
2015-11-06 14:31:50 +00:00
2020-05-17 16:05:44 +00:00
if ( ! ret )
{
2021-11-10 03:00:03 +00:00
DoLoadErrorCallback ( "Libretro failed to load the given file. This is probably due to a core/content mismatch. Moreover, the process is now likely to be hosed. We suggest you restart the program." , VSystemID . Raw . Libretro ) ;
2020-05-17 16:05:44 +00:00
retro . Dispose ( ) ;
return false ;
}
2020-02-22 18:29:12 +00:00
}
2020-05-17 16:05:44 +00:00
}
else
{
// do the archive binding we had to skip
if ( ! HandleArchiveBinding ( file ) )
2020-02-22 18:29:12 +00:00
{
2020-05-17 16:05:44 +00:00
return false ;
2015-11-06 14:31:50 +00:00
}
2020-05-17 16:05:44 +00:00
2020-06-07 09:59:18 +00:00
// not libretro: do extension checking
2021-01-20 04:32:32 +00:00
var ext = file . Extension ;
2020-06-07 09:59:18 +00:00
switch ( ext )
{
case ".m3u" :
2021-01-14 18:33:14 +00:00
LoadM3U ( path , nextComm , file , forcedCoreName , out nextEmulator , out game ) ;
2020-06-07 09:59:18 +00:00
break ;
case ".xml" :
2021-01-14 18:33:14 +00:00
if ( ! LoadXML ( path , nextComm , file , forcedCoreName , out nextEmulator , out rom , out game ) )
2020-07-12 00:38:09 +00:00
return false ;
2020-06-07 09:59:18 +00:00
break ;
case ".psf" :
case ".minipsf" :
2020-06-07 13:39:21 +00:00
LoadPSF ( path , nextComm , file , out nextEmulator , out rom , out game ) ;
2020-06-07 09:59:18 +00:00
break ;
2022-11-22 02:25:18 +00:00
case ".zip" when forcedCoreName is null :
case ".7z" when forcedCoreName is null :
2023-04-09 00:05:52 +00:00
LoadMAME ( path : path , nextComm , file , ext : ext , out nextEmulator , out rom , out game , out cancel ) ;
2022-11-22 02:25:18 +00:00
break ;
2020-06-07 09:59:18 +00:00
default :
if ( Disc . IsValidExtension ( ext ) )
{
2020-07-12 00:38:09 +00:00
if ( file . IsArchive )
throw new InvalidOperationException ( "Can't load CD files from archives!" ) ;
2021-01-14 18:33:14 +00:00
if ( ! LoadDisc ( path , nextComm , file , ext , forcedCoreName , out nextEmulator , out game ) )
2020-07-12 00:38:09 +00:00
return false ;
2020-06-07 09:59:18 +00:00
}
else
{
2023-04-09 00:05:52 +00:00
// must be called after LoadXML because of SNES hacks
LoadOther (
nextComm ,
file ,
ext : ext ,
forcedCoreName : forcedCoreName ,
out nextEmulator ,
out rom ,
out game ,
out cancel ) ;
2020-06-07 09:59:18 +00:00
}
break ;
}
2020-05-17 16:05:44 +00:00
}
2014-05-12 00:14:45 +00:00
2020-05-17 16:05:44 +00:00
if ( nextEmulator = = null )
{
if ( ! cancel )
2017-04-14 19:59:01 +00:00
{
2020-05-17 16:05:44 +00:00
DoLoadErrorCallback ( "No core could load the rom." , null ) ;
2017-04-14 19:59:01 +00:00
}
2020-05-17 08:14:32 +00:00
2020-05-17 16:05:44 +00:00
return false ;
}
2022-07-25 21:59:57 +00:00
2023-04-28 15:52:43 +00:00
if ( game is null ) throw new Exception ( "RomLoader returned core but no GameInfo" ) ; // shouldn't be null if `nextEmulator` isn't? just in case
2020-02-22 18:29:12 +00:00
}
catch ( Exception ex )
{
2020-06-07 09:36:42 +00:00
var system = game ? . System ;
2020-05-17 16:05:44 +00:00
2022-01-26 16:46:05 +00:00
DispatchErrorMessage ( ex , system : system , path : path ) ;
2020-08-22 19:24:17 +00:00
return false ;
}
2020-05-17 16:05:44 +00:00
2020-08-22 19:24:17 +00:00
Rom = rom ;
LoadedEmulator = nextEmulator ;
Game = game ;
return true ;
}
2022-01-26 16:46:05 +00:00
private void DispatchErrorMessage ( Exception ex , string system , string path )
2020-08-22 19:24:17 +00:00
{
if ( ex is AggregateException agg )
{
// all cores failed to load a game, so tell the user everything that went wrong and maybe they can fix it
if ( agg . InnerExceptions . Count > 1 )
2020-05-17 16:05:44 +00:00
{
2020-08-22 19:24:17 +00:00
DoLoadErrorCallback ( "Multiple cores failed to load the rom:" , system ) ;
2020-05-17 16:05:44 +00:00
}
2020-08-22 19:24:17 +00:00
foreach ( Exception e in agg . InnerExceptions )
2020-05-17 16:05:44 +00:00
{
2022-01-26 16:46:05 +00:00
DispatchErrorMessage ( e , system : system , path : path ) ;
2020-05-17 16:05:44 +00:00
}
2020-08-22 19:24:17 +00:00
return ;
2013-12-25 19:09:53 +00:00
}
2020-05-17 16:05:44 +00:00
2020-08-22 19:24:17 +00:00
// all of the specific exceptions we're trying to catch here aren't expected to have inner exceptions,
// so drill down in case we got a TargetInvocationException or something like that
while ( ex . InnerException ! = null )
ex = ex . InnerException ;
if ( ex is MissingFirmwareException )
{
2022-01-26 16:46:05 +00:00
DoLoadErrorCallback ( ex . Message , system , path , Deterministic , LoadErrorType . MissingFirmware ) ;
2020-08-22 19:24:17 +00:00
}
else if ( ex is CGBNotSupportedException )
{
// failed to load SGB bios or game does not support SGB mode.
DoLoadErrorCallback ( "Failed to load a GB rom in SGB mode. You might try disabling SGB Mode." , system ) ;
}
else if ( ex is NoAvailableCoreException )
{
// handle exceptions thrown by the new detected systems that BizHawk does not have cores for
DoLoadErrorCallback ( $"{ex.Message}\n\n{ex}" , system ) ;
}
else
{
DoLoadErrorCallback ( $"A core accepted the rom, but threw an exception while loading it:\n\n{ex}" , system ) ;
}
2013-12-25 19:09:53 +00:00
}
2020-06-07 16:06:00 +00:00
2021-08-23 23:56:17 +00:00
/// <remarks>roms ONLY; when an archive is loaded with a single file whose extension is one of these, the user prompt is skipped</remarks>
private static class RomFileExtensions
{
2021-08-24 00:26:48 +00:00
public static readonly IReadOnlyCollection < string > A26 = new [ ] { "a26" } ;
2021-08-23 23:56:17 +00:00
public static readonly IReadOnlyCollection < string > A78 = new [ ] { "a78" } ;
2024-10-24 18:47:22 +00:00
public static readonly IReadOnlyCollection < string > Amiga = new [ ] { "adf" , "adz" , "dms" , "fdi" , /*"hdf", "ipf", "lha"*/ } ;
2024-06-11 13:02:38 +00:00
2021-08-24 00:26:48 +00:00
public static readonly IReadOnlyCollection < string > AppleII = new [ ] { "dsk" , "do" , "po" } ;
2022-11-17 01:32:20 +00:00
public static readonly IReadOnlyCollection < string > Arcade = new [ ] { "zip" , "7z" , "chd" } ;
2022-11-10 08:05:25 +00:00
2021-08-24 00:26:48 +00:00
public static readonly IReadOnlyCollection < string > C64 = new [ ] { "prg" , "d64" , "g64" , "crt" , "tap" } ;
2021-08-23 23:56:17 +00:00
public static readonly IReadOnlyCollection < string > Coleco = new [ ] { "col" } ;
2021-08-24 00:26:48 +00:00
public static readonly IReadOnlyCollection < string > GB = new [ ] { "gb" , "gbc" , "sgb" } ;
2021-08-23 23:56:17 +00:00
public static readonly IReadOnlyCollection < string > GBA = new [ ] { "gba" } ;
public static readonly IReadOnlyCollection < string > GEN = new [ ] { "gen" , "md" , "smd" , "32x" } ;
public static readonly IReadOnlyCollection < string > INTV = new [ ] { "int" , "bin" , "rom" } ;
2022-09-12 04:38:46 +00:00
public static readonly IReadOnlyCollection < string > Jaguar = new [ ] { "j64" , "jag" } ;
2021-08-24 00:26:48 +00:00
public static readonly IReadOnlyCollection < string > Lynx = new [ ] { "lnx" } ;
2022-06-14 21:37:54 +00:00
public static readonly IReadOnlyCollection < string > MSX = new [ ] { "cas" , "dsk" , "mx1" , "rom" } ;
2023-07-28 12:33:56 +00:00
public static readonly IReadOnlyCollection < string > N3DS = new [ ] { "3ds" , "3dsx" , "axf" , "cci" , "cxi" , "app" , "elf" , "cia" } ;
2021-08-23 23:56:17 +00:00
public static readonly IReadOnlyCollection < string > N64 = new [ ] { "z64" , "v64" , "n64" } ;
2022-11-27 13:44:00 +00:00
public static readonly IReadOnlyCollection < string > N64DD = new [ ] { "ndd" } ;
2024-10-19 01:03:04 +00:00
public static readonly IReadOnlyCollection < string > NDS = new [ ] { "nds" , "srl" , "dsi" , "ids" } ;
2021-08-24 00:26:48 +00:00
2021-08-23 23:56:17 +00:00
public static readonly IReadOnlyCollection < string > NES = new [ ] { "nes" , "fds" , "unf" } ;
2021-08-24 00:26:48 +00:00
public static readonly IReadOnlyCollection < string > NGP = new [ ] { "ngp" , "ngc" } ;
2021-08-23 23:56:17 +00:00
public static readonly IReadOnlyCollection < string > O2 = new [ ] { "o2" } ;
public static readonly IReadOnlyCollection < string > PCE = new [ ] { "pce" , "sgx" } ;
public static readonly IReadOnlyCollection < string > SMS = new [ ] { "sms" , "gg" , "sg" } ;
2022-11-21 10:13:14 +00:00
public static readonly IReadOnlyCollection < string > SNES = new [ ] { "smc" , "sfc" , "xml" , "bs" } ;
2021-08-23 23:56:17 +00:00
2022-01-30 02:23:09 +00:00
public static readonly IReadOnlyCollection < string > TI83 = new [ ] { "83g" , "83l" , "83p" } ;
2021-08-23 23:56:17 +00:00
2022-06-16 07:14:48 +00:00
public static readonly IReadOnlyCollection < string > TIC80 = new [ ] { "tic" } ;
2021-08-24 00:26:48 +00:00
public static readonly IReadOnlyCollection < string > UZE = new [ ] { "uze" } ;
public static readonly IReadOnlyCollection < string > VB = new [ ] { "vb" } ;
2021-08-23 23:56:17 +00:00
public static readonly IReadOnlyCollection < string > VEC = new [ ] { "vec" } ;
2022-12-21 08:41:42 +00:00
public static readonly IReadOnlyCollection < string > WSWAN = new [ ] { "ws" , "wsc" , "pc2" } ;
2021-08-23 23:56:17 +00:00
2024-10-15 16:27:05 +00:00
public static readonly IReadOnlyCollection < string > ZXSpectrum = new [ ] { "tzx" , "tap" , "dsk" , "pzx" , "ipf" } ;
2021-08-24 00:26:48 +00:00
2022-03-08 02:16:53 +00:00
public static readonly IReadOnlyCollection < string > AutoloadFromArchive = Array . Empty < string > ( )
2021-08-24 00:26:48 +00:00
. Concat ( A26 )
2021-08-23 23:56:17 +00:00
. Concat ( A78 )
2024-06-11 13:02:38 +00:00
. Concat ( Amiga )
2021-08-24 00:26:48 +00:00
. Concat ( AppleII )
2021-08-23 23:56:17 +00:00
. Concat ( C64 )
. Concat ( Coleco )
. Concat ( GB )
. Concat ( GBA )
. Concat ( GEN )
. Concat ( INTV )
2022-09-12 04:38:46 +00:00
. Concat ( Jaguar )
2021-08-24 00:26:48 +00:00
. Concat ( Lynx )
2022-06-14 21:37:54 +00:00
. Concat ( MSX )
2021-08-23 23:56:17 +00:00
. Concat ( N64 )
2022-11-27 13:44:00 +00:00
. Concat ( N64DD )
2021-08-24 00:26:48 +00:00
. Concat ( NDS )
2021-08-23 23:56:17 +00:00
. Concat ( NES )
2021-08-24 00:26:48 +00:00
. Concat ( NGP )
2021-08-23 23:56:17 +00:00
. Concat ( O2 )
. Concat ( PCE )
. Concat ( SMS )
. Concat ( SNES )
. Concat ( TI83 )
2022-06-16 07:14:48 +00:00
. Concat ( TIC80 )
2021-08-24 00:26:48 +00:00
. Concat ( UZE )
. Concat ( VB )
2021-08-23 23:56:17 +00:00
. Concat ( VEC )
. Concat ( WSWAN )
2021-08-24 00:26:48 +00:00
. Concat ( ZXSpectrum )
2021-08-23 23:56:17 +00:00
. Select ( static s = > $".{s}" ) // this is what's expected at call-site
. ToArray ( ) ;
}
2020-06-16 23:40:31 +00:00
/// <remarks>TODO add and handle <see cref="FilesystemFilter.LuaScripts"/> (you can drag-and-drop scripts and there are already non-rom things in this list, so why not?)</remarks>
2022-10-08 01:23:40 +00:00
public static readonly FilesystemFilterSet RomFilter = new (
2022-10-03 22:49:09 +00:00
new FilesystemFilter ( "Music Files" , Array . Empty < string > ( ) , devBuildExtraExts : new [ ] { "psf" , "minipsf" , "sid" , "nsf" , "gbs" } ) ,
2023-05-18 14:13:25 +00:00
new FilesystemFilter ( "Disc Images" , FilesystemFilter . DiscExtensions ) ,
2021-08-23 23:56:17 +00:00
new FilesystemFilter ( "NES" , RomFileExtensions . NES . Concat ( new [ ] { "nsf" } ) . ToList ( ) , addArchiveExts : true ) ,
new FilesystemFilter ( "Super NES" , RomFileExtensions . SNES , addArchiveExts : true ) ,
2023-05-18 14:13:25 +00:00
new FilesystemFilter ( "PlayStation" , FilesystemFilter . DiscExtensions ) ,
2022-03-08 02:16:53 +00:00
new FilesystemFilter ( "PSX Executables (experimental)" , Array . Empty < string > ( ) , devBuildExtraExts : new [ ] { "exe" } ) ,
2020-06-16 23:40:31 +00:00
new FilesystemFilter ( "PSF Playstation Sound File" , new [ ] { "psf" , "minipsf" } ) ,
2021-08-23 23:56:17 +00:00
new FilesystemFilter ( "Nintendo 64" , RomFileExtensions . N64 ) ,
2022-11-27 13:44:00 +00:00
new FilesystemFilter ( "Nintendo 64 Disk Drive" , RomFileExtensions . N64DD ) ,
2022-10-03 22:49:09 +00:00
new FilesystemFilter ( "Gameboy" , RomFileExtensions . GB . Concat ( new [ ] { "gbs" } ) . ToList ( ) , addArchiveExts : true ) ,
2021-08-23 23:56:17 +00:00
new FilesystemFilter ( "Gameboy Advance" , RomFileExtensions . GBA , addArchiveExts : true ) ,
2023-07-28 12:33:56 +00:00
new FilesystemFilter ( "Nintendo 3DS" , RomFileExtensions . N3DS ) ,
2021-08-24 00:26:48 +00:00
new FilesystemFilter ( "Nintendo DS" , RomFileExtensions . NDS ) ,
2021-08-23 23:56:17 +00:00
new FilesystemFilter ( "Master System" , RomFileExtensions . SMS , addArchiveExts : true ) ,
2023-05-18 14:13:25 +00:00
new FilesystemFilter ( "PC Engine" , RomFileExtensions . PCE . Concat ( FilesystemFilter . DiscExtensions ) . ToList ( ) , addArchiveExts : true ) ,
2021-08-24 00:26:48 +00:00
new FilesystemFilter ( "Atari 2600" , RomFileExtensions . A26 , devBuildExtraExts : new [ ] { "bin" } , addArchiveExts : true ) ,
2021-08-23 23:56:17 +00:00
new FilesystemFilter ( "Atari 7800" , RomFileExtensions . A78 , devBuildExtraExts : new [ ] { "bin" } , addArchiveExts : true ) ,
2022-09-12 04:38:46 +00:00
new FilesystemFilter ( "Atari Jaguar" , RomFileExtensions . Jaguar , addArchiveExts : true ) ,
2021-08-24 00:26:48 +00:00
new FilesystemFilter ( "Atari Lynx" , RomFileExtensions . Lynx , addArchiveExts : true ) ,
2021-08-23 23:56:17 +00:00
new FilesystemFilter ( "ColecoVision" , RomFileExtensions . Coleco , addArchiveExts : true ) ,
new FilesystemFilter ( "IntelliVision" , RomFileExtensions . INTV , addArchiveExts : true ) ,
new FilesystemFilter ( "TI-83" , RomFileExtensions . TI83 , addArchiveExts : true ) ,
2022-06-16 07:14:48 +00:00
new FilesystemFilter ( "TIC-80" , RomFileExtensions . TIC80 , addArchiveExts : true ) ,
2020-06-16 23:40:31 +00:00
FilesystemFilter . Archives ,
2023-05-18 14:13:25 +00:00
new FilesystemFilter ( "Genesis" , RomFileExtensions . GEN . Concat ( FilesystemFilter . DiscExtensions ) . ToList ( ) , addArchiveExts : true ) ,
2022-03-08 02:16:53 +00:00
new FilesystemFilter ( "SID Commodore 64 Music File" , Array . Empty < string > ( ) , devBuildExtraExts : new [ ] { "sid" } , devBuildAddArchiveExts : true ) ,
2021-08-23 23:56:17 +00:00
new FilesystemFilter ( "WonderSwan" , RomFileExtensions . WSWAN , addArchiveExts : true ) ,
2021-08-24 00:26:48 +00:00
new FilesystemFilter ( "Apple II" , RomFileExtensions . AppleII , addArchiveExts : true ) ,
new FilesystemFilter ( "Virtual Boy" , RomFileExtensions . VB , addArchiveExts : true ) ,
new FilesystemFilter ( "Neo Geo Pocket" , RomFileExtensions . NGP , addArchiveExts : true ) ,
new FilesystemFilter ( "Commodore 64" , RomFileExtensions . C64 , addArchiveExts : true ) ,
2022-03-08 02:16:53 +00:00
new FilesystemFilter ( "Amstrad CPC" , Array . Empty < string > ( ) , devBuildExtraExts : new [ ] { "cdt" , "dsk" } , devBuildAddArchiveExts : true ) ,
2021-08-24 00:26:48 +00:00
new FilesystemFilter ( "Sinclair ZX Spectrum" , RomFileExtensions . ZXSpectrum . Concat ( new [ ] { "csw" , "wav" } ) . ToList ( ) , addArchiveExts : true ) ,
2021-08-23 23:56:17 +00:00
new FilesystemFilter ( "Odyssey 2" , RomFileExtensions . O2 ) ,
2021-08-24 00:26:48 +00:00
new FilesystemFilter ( "Uzebox" , RomFileExtensions . UZE ) ,
new FilesystemFilter ( "Vectrex" , RomFileExtensions . VEC ) ,
2022-06-14 21:37:54 +00:00
new FilesystemFilter ( "MSX" , RomFileExtensions . MSX ) ,
2022-11-10 08:05:25 +00:00
new FilesystemFilter ( "Arcade" , RomFileExtensions . Arcade ) ,
2024-06-11 13:02:38 +00:00
new FilesystemFilter ( "Amiga" , RomFileExtensions . Amiga ) ,
2022-10-08 01:23:40 +00:00
FilesystemFilter . EmuHawkSaveStates )
{
CombinedEntryDesc = "Everything" ,
} ;
2020-06-16 23:40:31 +00:00
2022-10-08 01:23:40 +00:00
public static readonly IReadOnlyCollection < string > KnownRomExtensions = RomFilter . Filters
2020-06-16 23:40:31 +00:00
. SelectMany ( f = > f . Extensions )
. Distinct ( )
. Except ( FilesystemFilter . ArchiveExtensions . Concat ( new [ ] { "State" } ) )
. Select ( s = > $".{s.ToUpperInvariant()}" ) // this is what's expected at call-site
. ToList ( ) ;
2013-12-25 19:09:53 +00:00
}
2014-08-01 14:56:23 +00:00
}