2015-06-04 02:04:42 +00:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Text ;
using BizHawk.Common ;
using BizHawk.Emulation.Common ;
using System.Runtime.InteropServices ;
2015-06-06 17:34:19 +00:00
using System.IO ;
2015-06-13 18:01:26 +00:00
using System.ComponentModel ;
2015-06-04 02:04:42 +00:00
namespace BizHawk.Emulation.Cores.Nintendo.GBA
{
2016-09-17 13:51:38 +00:00
[CoreAttributes("mGBA", "endrift", true, true, "0.4.1", "https://mgba.io/", false)]
2015-08-06 00:12:09 +00:00
[ServiceNotApplicable(typeof(IDriveLight), typeof(IRegionable))]
2016-09-17 20:29:25 +00:00
public class MGBAHawk : IEmulator , IVideoProvider , ISyncSoundProvider , IGBAGPUViewable ,
ISaveRam , IStatable , IInputPollable , ISettable < MGBAHawk . Settings , MGBAHawk . SyncSettings >
2015-06-04 02:04:42 +00:00
{
2016-09-17 20:29:25 +00:00
private IntPtr _core ;
private byte [ ] _saveScratch = new byte [ 262144 ] ;
2015-06-04 02:04:42 +00:00
[CoreConstructor("GBA")]
2016-09-17 15:31:06 +00:00
public MGBAHawk ( byte [ ] file , CoreComm comm , SyncSettings syncSettings , Settings settings , bool deterministic , GameInfo game )
2015-06-04 02:04:42 +00:00
{
2015-06-13 18:01:26 +00:00
_syncSettings = syncSettings ? ? new SyncSettings ( ) ;
2016-02-21 13:54:00 +00:00
_settings = settings ? ? new Settings ( ) ;
2015-06-13 18:01:26 +00:00
DeterministicEmulation = deterministic ;
byte [ ] bios = comm . CoreFileProvider . GetFirmware ( "GBA" , "Bios" , false ) ;
DeterministicEmulation & = bios ! = null ;
if ( DeterministicEmulation ! = deterministic )
{
throw new InvalidOperationException ( "A BIOS is required for deterministic recordings!" ) ;
}
if ( ! DeterministicEmulation & & bios ! = null & & ! _syncSettings . RTCUseRealTime & & ! _syncSettings . SkipBios )
2015-06-05 00:12:12 +00:00
{
2015-06-13 18:01:26 +00:00
// in these situations, this core is deterministic even though it wasn't asked to be
DeterministicEmulation = true ;
2015-06-05 00:12:12 +00:00
}
if ( bios ! = null & & bios . Length ! = 16384 )
{
throw new InvalidOperationException ( "BIOS must be exactly 16384 bytes!" ) ;
}
2016-10-17 17:01:52 +00:00
var skipBios = ! DeterministicEmulation & & _syncSettings . SkipBios ;
_core = LibmGBA . BizCreate ( bios , file , file . Length , GetOverrideInfo ( game ) , skipBios ) ;
2016-09-17 20:29:25 +00:00
if ( _core = = IntPtr . Zero )
2015-06-05 00:12:12 +00:00
{
2016-09-17 20:29:25 +00:00
throw new InvalidOperationException ( "BizCreate() returned NULL! Bad BIOS? and/or ROM?" ) ;
2015-06-05 00:12:12 +00:00
}
2015-06-04 02:04:42 +00:00
try
{
2016-03-27 21:35:34 +00:00
CreateMemoryDomains ( file . Length ) ;
2015-06-05 00:43:41 +00:00
var ser = new BasicServiceProvider ( this ) ;
ser . Register < IDisassemblable > ( new ArmV4Disassembler ( ) ) ;
2016-03-27 21:35:34 +00:00
ser . Register < IMemoryDomains > ( MemoryDomains ) ;
2015-06-05 00:43:41 +00:00
ServiceProvider = ser ;
CoreComm = comm ;
2015-06-06 17:34:19 +00:00
2015-06-13 18:01:26 +00:00
CoreComm . VsyncNum = 262144 ;
CoreComm . VsyncDen = 4389 ;
CoreComm . NominalWidth = 240 ;
CoreComm . NominalHeight = 160 ;
2015-06-04 02:04:42 +00:00
}
catch
{
2016-09-17 20:29:25 +00:00
LibmGBA . BizDestroy ( _core ) ;
2015-06-04 02:04:42 +00:00
throw ;
}
}
2016-09-17 15:31:06 +00:00
private static LibmGBA . OverrideInfo GetOverrideInfo ( GameInfo game )
{
if ( ! game . OptionPresent ( "mgbaNeedsOverrides" ) )
{
// the gba game db predates the mgba core in bizhawk, but was never used by the mgba core,
// which had its own handling for overrides
// to avoid possible regressions, we don't want to be overriding things that we already
// know work in mgba, so unless this parameter is set, we do nothing
return null ;
}
var ret = new LibmGBA . OverrideInfo ( ) ;
if ( game . OptionPresent ( "flashSize" ) )
{
switch ( game . GetIntValue ( "flashSize" ) )
{
case 65536 : ret . Savetype = LibmGBA . SaveType . Flash512 ; break ;
case 131072 : ret . Savetype = LibmGBA . SaveType . Flash1m ; break ;
default : throw new InvalidOperationException ( "Unknown flashSize" ) ;
}
}
else if ( game . OptionPresent ( "saveType" ) )
{
switch ( game . GetIntValue ( "saveType" ) )
{
// 3 specifies either flash 512 or 1024, but in vba-over.ini, the latter will have a flashSize as well
case 3 : ret . Savetype = LibmGBA . SaveType . Flash512 ; break ;
case 4 : ret . Savetype = LibmGBA . SaveType . Eeprom ; break ;
default : throw new InvalidOperationException ( "Unknown saveType" ) ;
}
}
if ( game . GetInt ( "rtcEnabled" , 0 ) = = 1 )
{
ret . Hardware | = LibmGBA . Hardware . Rtc ;
}
if ( game . GetInt ( "mirroringEnabled" , 0 ) = = 1 )
{
throw new InvalidOperationException ( "Don't know what to do with mirroringEnabled!" ) ;
}
if ( game . OptionPresent ( "idleLoop" ) )
{
ret . IdleLoop = ( uint ) game . GetHexValue ( "idleLoop" ) ;
}
return ret ;
}
2016-03-27 21:35:34 +00:00
MemoryDomainList MemoryDomains ;
2015-06-04 02:04:42 +00:00
public IEmulatorServiceProvider ServiceProvider { get ; private set ; }
public ControllerDefinition ControllerDefinition { get { return GBA . GBAController ; } }
public IController Controller { get ; set ; }
public void FrameAdvance ( bool render , bool rendersound = true )
{
2015-06-04 22:47:51 +00:00
Frame + + ;
2015-06-04 23:30:24 +00:00
if ( Controller [ "Power" ] )
2016-03-27 21:35:34 +00:00
{
2016-09-17 20:29:25 +00:00
LibmGBA . BizReset ( _core ) ;
2016-03-27 21:35:34 +00:00
//BizReset caused memorydomain pointers to change.
WireMemoryDomainPointers ( ) ;
}
2015-06-04 23:30:24 +00:00
2016-09-17 20:29:25 +00:00
IsLagFrame = LibmGBA . BizAdvance ( _core , VBANext . GetButtons ( Controller ) , videobuff , ref nsamp , soundbuff ,
2015-06-13 18:01:26 +00:00
RTCTime ( ) ,
2015-06-06 22:23:42 +00:00
( short ) Controller . GetFloat ( "Tilt X" ) ,
( short ) Controller . GetFloat ( "Tilt Y" ) ,
( short ) Controller . GetFloat ( "Tilt Z" ) ,
( byte ) ( 255 - Controller . GetFloat ( "Light Sensor" ) ) ) ;
2015-06-13 18:01:26 +00:00
2015-06-10 01:19:09 +00:00
if ( IsLagFrame )
2015-06-13 18:01:26 +00:00
LagCount + + ;
// this should be called in hblank on the appropriate line, but until we implement that, just do it here
if ( _scanlinecb ! = null )
_scanlinecb ( ) ;
2015-06-04 02:04:42 +00:00
}
public int Frame { get ; private set ; }
public string SystemId { get { return "GBA" ; } }
2015-06-13 18:01:26 +00:00
public bool DeterministicEmulation { get ; private set ; }
2015-06-04 02:04:42 +00:00
public string BoardName { get { return null ; } }
public void ResetCounters ( )
{
2015-06-04 22:47:51 +00:00
Frame = 0 ;
2015-06-13 18:01:26 +00:00
LagCount = 0 ;
2015-06-06 17:34:19 +00:00
IsLagFrame = false ;
2015-06-04 02:04:42 +00:00
}
public CoreComm CoreComm { get ; private set ; }
public void Dispose ( )
{
2016-09-17 20:29:25 +00:00
if ( _core ! = IntPtr . Zero )
2015-06-04 02:04:42 +00:00
{
2016-09-17 20:29:25 +00:00
LibmGBA . BizDestroy ( _core ) ;
_core = IntPtr . Zero ;
2015-06-04 02:04:42 +00:00
}
}
2015-06-04 22:47:51 +00:00
#region IVideoProvider
2015-06-04 02:04:42 +00:00
public int VirtualWidth { get { return 240 ; } }
public int VirtualHeight { get { return 160 ; } }
public int BufferWidth { get { return 240 ; } }
public int BufferHeight { get { return 160 ; } }
public int BackgroundColor
{
get { return unchecked ( ( int ) 0xff000000 ) ; }
}
public int [ ] GetVideoBuffer ( )
{
return videobuff ;
}
2015-06-04 22:47:51 +00:00
private readonly int [ ] videobuff = new int [ 240 * 160 ] ;
#endregion
2015-06-04 02:04:42 +00:00
2015-06-04 22:47:51 +00:00
#region ISoundProvider
private readonly short [ ] soundbuff = new short [ 2048 ] ;
private int nsamp ;
public void GetSamples ( out short [ ] samples , out int nsamp )
{
nsamp = this . nsamp ;
samples = soundbuff ;
DiscardSamples ( ) ;
}
public void DiscardSamples ( )
{
nsamp = 0 ;
}
public ISoundProvider SoundProvider { get { throw new InvalidOperationException ( ) ; } }
public ISyncSoundProvider SyncSoundProvider { get { return this ; } }
public bool StartAsyncSound ( ) { return false ; }
public void EndAsyncSound ( ) { }
#endregion
2015-06-05 00:43:41 +00:00
#region IMemoryDomains
2016-11-06 15:32:04 +00:00
unsafe byte PeekWRAM ( IntPtr xwram , long addr ) { return ( ( byte * ) xwram ) [ addr ] ; }
unsafe void PokeWRAM ( IntPtr xwram , long addr , byte value ) { ( ( byte * ) xwram ) [ addr ] = value ; }
2015-09-29 04:54:44 +00:00
2016-03-27 21:35:34 +00:00
void WireMemoryDomainPointers ( )
2015-06-05 00:43:41 +00:00
{
var s = new LibmGBA . MemoryAreas ( ) ;
2016-09-17 20:29:25 +00:00
LibmGBA . BizGetMemoryAreas ( _core , s ) ;
2015-06-05 00:43:41 +00:00
2016-04-13 23:50:06 +00:00
_iwram . Data = s . iwram ;
_ewram . Data = s . wram ;
_bios . Data = s . bios ;
_palram . Data = s . palram ;
_vram . Data = s . vram ;
_oam . Data = s . oam ;
_rom . Data = s . rom ;
2016-04-19 02:46:27 +00:00
_sram . Data = s . sram ;
_sram . SetSize ( s . sram_size ) ;
2015-06-05 00:43:41 +00:00
2015-09-29 04:54:44 +00:00
// special combined ram memory domain
2016-04-13 23:50:06 +00:00
_cwram . Peek =
2016-03-27 21:35:34 +00:00
delegate ( long addr )
{
if ( addr < 0 | | addr > = ( 256 + 32 ) * 1024 )
throw new IndexOutOfRangeException ( ) ;
if ( addr > = 256 * 1024 )
return PeekWRAM ( s . iwram , addr & 32767 ) ;
else
return PeekWRAM ( s . wram , addr ) ;
2016-04-13 23:50:06 +00:00
} ;
_cwram . Poke =
2016-03-27 21:35:34 +00:00
delegate ( long addr , byte val )
{
if ( addr < 0 | | addr > = ( 256 + 32 ) * 1024 )
throw new IndexOutOfRangeException ( ) ;
if ( addr > = 256 * 1024 )
PokeWRAM ( s . iwram , addr & 32767 , val ) ;
else
PokeWRAM ( s . wram , addr , val ) ;
2016-04-13 23:50:06 +00:00
} ;
2015-09-29 04:54:44 +00:00
2015-06-05 00:43:41 +00:00
_gpumem = new GBAGPUMemoryAreas
{
mmio = s . mmio ,
oam = s . oam ,
palram = s . palram ,
vram = s . vram
} ;
2016-03-27 21:35:34 +00:00
}
2015-06-05 00:43:41 +00:00
2016-04-13 23:50:06 +00:00
private MemoryDomainIntPtr _iwram ;
private MemoryDomainIntPtr _ewram ;
private MemoryDomainIntPtr _bios ;
private MemoryDomainIntPtr _palram ;
private MemoryDomainIntPtr _vram ;
private MemoryDomainIntPtr _oam ;
private MemoryDomainIntPtr _rom ;
2016-04-19 02:46:27 +00:00
private MemoryDomainIntPtr _sram ;
2016-04-13 23:50:06 +00:00
private MemoryDomainDelegate _cwram ;
2016-03-27 21:35:34 +00:00
private void CreateMemoryDomains ( int romsize )
{
var LE = MemoryDomain . Endian . Little ;
var mm = new List < MemoryDomain > ( ) ;
2016-04-13 23:50:06 +00:00
mm . Add ( _iwram = new MemoryDomainIntPtr ( "IWRAM" , LE , IntPtr . Zero , 32 * 1024 , true , 4 ) ) ;
mm . Add ( _ewram = new MemoryDomainIntPtr ( "EWRAM" , LE , IntPtr . Zero , 256 * 1024 , true , 4 ) ) ;
mm . Add ( _bios = new MemoryDomainIntPtr ( "BIOS" , LE , IntPtr . Zero , 16 * 1024 , false , 4 ) ) ;
mm . Add ( _palram = new MemoryDomainIntPtr ( "PALRAM" , LE , IntPtr . Zero , 1024 , true , 4 ) ) ;
mm . Add ( _vram = new MemoryDomainIntPtr ( "VRAM" , LE , IntPtr . Zero , 96 * 1024 , true , 4 ) ) ;
mm . Add ( _oam = new MemoryDomainIntPtr ( "OAM" , LE , IntPtr . Zero , 1024 , true , 4 ) ) ;
mm . Add ( _rom = new MemoryDomainIntPtr ( "ROM" , LE , IntPtr . Zero , romsize , false , 4 ) ) ;
2016-04-19 02:46:27 +00:00
mm . Add ( _sram = new MemoryDomainIntPtr ( "SRAM" , LE , IntPtr . Zero , 0 , true , 4 ) ) ; //size will be fixed in wireup
2016-04-13 23:50:06 +00:00
mm . Add ( _cwram = new MemoryDomainDelegate ( "Combined WRAM" , ( 256 + 32 ) * 1024 , LE , null , null , 4 ) ) ;
2016-03-27 21:35:34 +00:00
MemoryDomains = new MemoryDomainList ( mm ) ;
WireMemoryDomainPointers ( ) ;
2015-06-05 00:43:41 +00:00
}
#endregion
2015-06-13 18:01:26 +00:00
private Action _scanlinecb ;
2015-06-05 00:43:41 +00:00
private GBAGPUMemoryAreas _gpumem ;
public GBAGPUMemoryAreas GetMemoryAreas ( )
{
return _gpumem ;
}
[FeatureNotImplemented]
public void SetScanlineCallback ( Action callback , int scanline )
{
2015-06-13 18:01:26 +00:00
_scanlinecb = callback ;
2015-06-05 00:43:41 +00:00
}
2015-06-06 12:49:31 +00:00
#region ISaveRam
public byte [ ] CloneSaveRam ( )
{
2016-10-08 15:58:52 +00:00
int len = LibmGBA . BizGetSaveRam ( _core , _saveScratch , _saveScratch . Length ) ;
if ( len = = _saveScratch . Length )
throw new InvalidOperationException ( "Save buffer not long enough" ) ;
2016-09-17 20:29:25 +00:00
if ( len = = 0 )
2015-06-06 12:49:31 +00:00
return null ;
2016-09-17 20:29:25 +00:00
var ret = new byte [ len ] ;
Array . Copy ( _saveScratch , ret , len ) ;
return ret ;
2015-06-06 12:49:31 +00:00
}
2015-06-06 17:42:47 +00:00
private static byte [ ] LegacyFix ( byte [ ] saveram )
{
// at one point vbanext-hawk had a special saveram format which we want to load.
var br = new BinaryReader ( new MemoryStream ( saveram , false ) ) ;
br . ReadBytes ( 8 ) ; // header;
int flashSize = br . ReadInt32 ( ) ;
int eepromsize = br . ReadInt32 ( ) ;
byte [ ] flash = br . ReadBytes ( flashSize ) ;
byte [ ] eeprom = br . ReadBytes ( eepromsize ) ;
if ( flash . Length = = 0 )
return eeprom ;
else if ( eeprom . Length = = 0 )
return flash ;
else
{
// well, isn't this a sticky situation!
return flash ; // woops
}
}
2015-06-06 12:49:31 +00:00
public void StoreSaveRam ( byte [ ] data )
{
2015-06-06 17:42:47 +00:00
if ( data . Take ( 8 ) . SequenceEqual ( Encoding . ASCII . GetBytes ( "GBABATT\0" ) ) )
{
data = LegacyFix ( data ) ;
}
2016-11-06 15:32:04 +00:00
LibmGBA . BizPutSaveRam ( _core , data , data . Length ) ;
2015-06-06 12:49:31 +00:00
}
public bool SaveRamModified
{
2016-09-17 20:29:25 +00:00
get
{
2016-10-08 15:58:52 +00:00
return LibmGBA . BizGetSaveRam ( _core , _saveScratch , _saveScratch . Length ) > 0 ;
2016-09-17 20:29:25 +00:00
}
2015-06-06 12:49:31 +00:00
}
#endregion
2015-06-06 17:34:19 +00:00
2016-10-23 00:49:42 +00:00
private byte [ ] _savebuff = new byte [ 0 ] ;
private byte [ ] _savebuff2 = new byte [ 13 ] ;
2015-06-06 17:34:19 +00:00
public bool BinarySaveStatesPreferred
{
get { return true ; }
}
public void SaveStateText ( TextWriter writer )
{
var tmp = SaveStateBinary ( ) ;
BizHawk . Common . BufferExtensions . BufferExtensions . SaveAsHexFast ( tmp , writer ) ;
}
public void LoadStateText ( TextReader reader )
{
string hex = reader . ReadLine ( ) ;
byte [ ] state = new byte [ hex . Length / 2 ] ;
BizHawk . Common . BufferExtensions . BufferExtensions . ReadFromHexFast ( state , hex ) ;
LoadStateBinary ( new BinaryReader ( new MemoryStream ( state ) ) ) ;
}
2016-10-25 22:14:20 +00:00
private void StartSaveStateBinaryInternal ( )
2015-06-06 17:34:19 +00:00
{
2016-10-23 00:49:42 +00:00
IntPtr p = IntPtr . Zero ;
int size = 0 ;
if ( ! LibmGBA . BizStartGetState ( _core , ref p , ref size ) )
2016-02-21 21:14:43 +00:00
throw new InvalidOperationException ( "Core failed to save!" ) ;
2016-10-23 00:49:42 +00:00
if ( size ! = _savebuff . Length )
{
_savebuff = new byte [ size ] ;
_savebuff2 = new byte [ size + 13 ] ;
}
LibmGBA . BizFinishGetState ( p , _savebuff , size ) ;
2016-10-25 22:14:20 +00:00
}
private void FinishSaveStateBinaryInternal ( BinaryWriter writer )
{
2016-10-22 22:09:28 +00:00
writer . Write ( _savebuff . Length ) ;
writer . Write ( _savebuff , 0 , _savebuff . Length ) ;
2015-06-06 17:34:19 +00:00
// other variables
writer . Write ( IsLagFrame ) ;
writer . Write ( LagCount ) ;
writer . Write ( Frame ) ;
}
2016-10-25 22:14:20 +00:00
public void SaveStateBinary ( BinaryWriter writer )
{
StartSaveStateBinaryInternal ( ) ;
FinishSaveStateBinaryInternal ( writer ) ;
}
2015-06-06 17:34:19 +00:00
public void LoadStateBinary ( BinaryReader reader )
{
int length = reader . ReadInt32 ( ) ;
2016-10-22 22:09:28 +00:00
if ( length ! = _savebuff . Length )
2016-02-21 21:14:43 +00:00
{
2016-10-23 00:49:42 +00:00
_savebuff = new byte [ length ] ;
_savebuff2 = new byte [ length + 13 ] ;
2016-02-21 21:14:43 +00:00
}
2016-09-17 20:29:25 +00:00
reader . Read ( _savebuff , 0 , length ) ;
if ( ! LibmGBA . BizPutState ( _core , _savebuff , length ) )
2016-02-07 17:51:00 +00:00
throw new InvalidOperationException ( "Core rejected the savestate!" ) ;
2015-06-06 17:34:19 +00:00
// other variables
IsLagFrame = reader . ReadBoolean ( ) ;
2015-06-13 18:01:26 +00:00
LagCount = reader . ReadInt32 ( ) ;
2015-06-06 17:34:19 +00:00
Frame = reader . ReadInt32 ( ) ;
}
public byte [ ] SaveStateBinary ( )
{
2016-10-25 22:14:20 +00:00
StartSaveStateBinaryInternal ( ) ;
2016-09-17 20:29:25 +00:00
var ms = new MemoryStream ( _savebuff2 , true ) ;
2015-06-06 17:34:19 +00:00
var bw = new BinaryWriter ( ms ) ;
2016-10-25 22:14:20 +00:00
FinishSaveStateBinaryInternal ( bw ) ;
2015-06-06 17:34:19 +00:00
bw . Flush ( ) ;
ms . Close ( ) ;
2016-09-17 20:29:25 +00:00
return _savebuff2 ;
2015-06-06 17:34:19 +00:00
}
2015-07-09 17:05:30 +00:00
public int LagCount { get ; set ; }
2016-01-26 10:34:42 +00:00
public bool IsLagFrame { get ; set ; }
2015-06-06 17:34:19 +00:00
[FeatureNotImplemented]
public IInputCallbackSystem InputCallbacks
{
get { throw new NotImplementedException ( ) ; }
}
2015-06-13 18:01:26 +00:00
private long RTCTime ( )
{
if ( ! DeterministicEmulation & & _syncSettings . RTCUseRealTime )
{
return ( long ) DateTime . Now . Subtract ( new DateTime ( 1970 , 1 , 1 ) ) . TotalSeconds ;
}
long basetime = ( long ) _syncSettings . RTCInitialTime . Subtract ( new DateTime ( 1970 , 1 , 1 ) ) . TotalSeconds ;
long increment = Frame * 4389L > > 18 ;
return basetime + increment ;
}
2016-02-21 13:54:00 +00:00
public Settings GetSettings ( )
2015-06-13 18:01:26 +00:00
{
2016-02-21 13:54:00 +00:00
return _settings . Clone ( ) ;
2015-06-13 18:01:26 +00:00
}
2016-02-21 13:54:00 +00:00
public bool PutSettings ( Settings o )
2015-06-13 18:01:26 +00:00
{
2016-02-21 13:54:00 +00:00
LibmGBA . Layers mask = 0 ;
if ( o . DisplayBG0 ) mask | = LibmGBA . Layers . BG0 ;
if ( o . DisplayBG1 ) mask | = LibmGBA . Layers . BG1 ;
if ( o . DisplayBG2 ) mask | = LibmGBA . Layers . BG2 ;
if ( o . DisplayBG3 ) mask | = LibmGBA . Layers . BG3 ;
if ( o . DisplayOBJ ) mask | = LibmGBA . Layers . OBJ ;
2016-09-17 20:29:25 +00:00
LibmGBA . BizSetLayerMask ( _core , mask ) ;
2016-02-21 13:54:00 +00:00
_settings = o ;
return false ;
2015-06-13 18:01:26 +00:00
}
2016-02-21 13:54:00 +00:00
private Settings _settings ;
public class Settings
2015-06-13 18:01:26 +00:00
{
2016-02-21 13:54:00 +00:00
[DefaultValue(true)]
public bool DisplayBG0 { get ; set ; }
[DefaultValue(true)]
public bool DisplayBG1 { get ; set ; }
[DefaultValue(true)]
public bool DisplayBG2 { get ; set ; }
[DefaultValue(true)]
public bool DisplayBG3 { get ; set ; }
[DefaultValue(true)]
public bool DisplayOBJ { get ; set ; }
public Settings Clone ( )
{
return ( Settings ) MemberwiseClone ( ) ;
}
public Settings ( )
{
SettingsUtil . SetDefaultValues ( this ) ;
}
}
public SyncSettings GetSyncSettings ( )
{
return _syncSettings . Clone ( ) ;
2015-06-13 18:01:26 +00:00
}
public bool PutSyncSettings ( SyncSettings o )
{
bool ret = SyncSettings . NeedsReboot ( o , _syncSettings ) ;
_syncSettings = o ;
return ret ;
}
private SyncSettings _syncSettings ;
public class SyncSettings
{
[DisplayName("Skip BIOS")]
[Description("Skips the BIOS intro. Not applicable when a BIOS is not provided.")]
[DefaultValue(true)]
public bool SkipBios { get ; set ; }
[DisplayName("RTC Use Real Time")]
[Description("Causes the internal clock to reflect your system clock. Only relevant when a game has an RTC chip. Forced to false for movie recording.")]
[DefaultValue(true)]
public bool RTCUseRealTime { get ; set ; }
[DisplayName("RTC Initial Time")]
[Description("The initial time of emulation. Only relevant when a game has an RTC chip and \"RTC Use Real Time\" is false.")]
[DefaultValue(typeof(DateTime), "2010-01-01")]
public DateTime RTCInitialTime { get ; set ; }
public SyncSettings ( )
{
SettingsUtil . SetDefaultValues ( this ) ;
}
public static bool NeedsReboot ( SyncSettings x , SyncSettings y )
{
return ! DeepEquality . DeepEquals ( x , y ) ;
}
public SyncSettings Clone ( )
{
return ( SyncSettings ) MemberwiseClone ( ) ;
}
}
2015-06-04 02:04:42 +00:00
}
}