2013-04-29 01:57:41 +00:00
using System ;
2014-04-20 01:44:06 +00:00
using System.Threading ;
2013-04-29 01:57:41 +00:00
using System.Collections.Generic ;
using System.IO ;
2014-07-03 18:54:53 +00:00
using BizHawk.Common.BufferExtensions ;
2013-11-04 01:06:36 +00:00
using BizHawk.Emulation.Common ;
2014-12-05 00:59:00 +00:00
using BizHawk.Emulation.Common.IEmulatorExtensions ;
2014-01-24 17:46:35 +00:00
using BizHawk.Emulation.Cores.Nintendo.N64.NativeApi ;
2013-10-27 22:07:40 +00:00
2013-11-13 03:32:25 +00:00
namespace BizHawk.Emulation.Cores.Nintendo.N64
2013-04-29 01:57:41 +00:00
{
2014-04-25 01:19:57 +00:00
[ CoreAttributes (
"Mupen64Plus" ,
2014-06-01 01:57:22 +00:00
"" ,
2014-04-25 01:19:57 +00:00
isPorted : true ,
2014-06-01 01:57:22 +00:00
isReleased : true ,
portedVersion : "2.0" ,
portedUrl : "https://code.google.com/p/mupen64plus/"
2014-04-25 01:19:57 +00:00
) ]
2014-11-30 20:29:30 +00:00
public partial class N64 : IEmulator , IMemoryDomains , ISaveRam , IDebuggable , IStatable , IInputPollable ,
2014-10-19 01:22:47 +00:00
ISettable < N64Settings , N64SyncSettings >
2013-11-11 03:20:33 +00:00
{
2014-05-13 00:31:32 +00:00
private readonly N64Input _inputProvider ;
private readonly N64VideoProvider _videoProvider ;
private readonly N64Audio _audioProvider ;
private readonly EventWaitHandle _pendingThreadEvent = new EventWaitHandle ( false , EventResetMode . AutoReset ) ;
private readonly EventWaitHandle _completeThreadEvent = new EventWaitHandle ( false , EventResetMode . AutoReset ) ;
private mupen64plusApi api ; // mupen64plus DLL Api
2014-06-01 12:06:22 +00:00
2014-05-13 00:31:32 +00:00
private N64SyncSettings _syncSettings ;
2014-06-01 12:06:22 +00:00
private N64Settings _settings ;
2014-05-13 00:31:32 +00:00
private bool _pendingThreadTerminate ;
private DisplayType _display_type = DisplayType . NTSC ;
private Action _pendingThreadAction ;
2014-09-27 12:56:55 +00:00
private bool _disableExpansionSlot = true ;
2014-05-13 00:06:33 +00:00
/// <summary>
/// Create mupen64plus Emulator
/// </summary>
/// <param name="comm">Core communication object</param>
/// <param name="game">Game information of game to load</param>
/// <param name="rom">Rom that should be loaded</param>
2014-09-07 21:48:05 +00:00
/// <param name="syncSettings">N64SyncSettings object</param>
2014-08-23 19:06:37 +00:00
[CoreConstructor("N64")]
2014-09-12 01:02:09 +00:00
public N64 ( CoreComm comm , GameInfo game , byte [ ] file , object settings , object syncSettings )
2014-05-13 00:06:33 +00:00
{
2014-12-04 03:38:30 +00:00
ServiceProvider = new BasicServiceProvider ( this ) ;
2014-12-05 02:22:41 +00:00
InputCallbacks = new InputCallbackSystem ( ) ;
2014-12-05 01:56:45 +00:00
MemoryCallbacks = new MemoryCallbackSystem ( ) ;
2014-12-04 03:38:30 +00:00
2014-05-13 00:06:33 +00:00
int SaveType = 0 ;
if ( game . OptionValue ( "SaveType" ) = = "EEPROM_16K" )
{
SaveType = 1 ;
}
CoreComm = comm ;
2014-06-01 12:06:22 +00:00
_syncSettings = ( N64SyncSettings ) syncSettings ? ? new N64SyncSettings ( ) ;
_settings = ( N64Settings ) settings ? ? new N64Settings ( ) ;
2014-05-13 00:06:33 +00:00
2014-09-27 12:56:55 +00:00
_disableExpansionSlot = _syncSettings . DisableExpansionSlot ;
2014-09-07 00:23:15 +00:00
2014-09-27 12:56:55 +00:00
// Override the user's expansion slot setting if it is mentioned in the gamedb (it is mentioned but the game MUST have this setting or else not work
2014-09-07 00:23:15 +00:00
if ( game . OptionValue ( "expansionpak" ) ! = null & & game . OptionValue ( "expansionpak" ) = = "1" )
{
2014-09-27 12:56:55 +00:00
_disableExpansionSlot = false ;
IsOverridingUserExpansionSlotSetting = true ;
2014-09-07 00:23:15 +00:00
}
2014-09-12 01:02:09 +00:00
byte country_code = file [ 0x3E ] ;
2014-05-13 00:06:33 +00:00
switch ( country_code )
{
// PAL codes
case 0x44 :
case 0x46 :
case 0x49 :
case 0x50 :
case 0x53 :
case 0x55 :
case 0x58 :
case 0x59 :
_display_type = DisplayType . PAL ;
break ;
// NTSC codes
case 0x37 :
case 0x41 :
case 0x45 :
case 0x4a :
default : // Fallback for unknown codes
_display_type = DisplayType . NTSC ;
break ;
}
switch ( DisplayType )
{
case DisplayType . NTSC :
comm . VsyncNum = 60000 ;
comm . VsyncDen = 1001 ;
break ;
default :
comm . VsyncNum = 50 ;
comm . VsyncDen = 1 ;
break ;
}
StartThreadLoop ( ) ;
2014-06-01 12:06:22 +00:00
var videosettings = _syncSettings . GetVPS ( game , _settings . VideoSizeX , _settings . VideoSizeY ) ;
2014-07-20 16:59:03 +00:00
var coreType = _syncSettings . Core ;
2014-05-13 00:06:33 +00:00
//zero 19-apr-2014 - added this to solve problem with SDL initialization corrupting the main thread (I think) and breaking subsequent emulators (for example, NES)
//not sure why this works... if we put the plugin initializations in here, we get deadlocks in some SDL initialization. doesnt make sense to me...
RunThreadAction ( ( ) = >
{
2014-09-27 12:56:55 +00:00
api = new mupen64plusApi ( this , file , videosettings , SaveType , ( int ) coreType , _disableExpansionSlot ) ;
2014-05-13 00:06:33 +00:00
} ) ;
// Order is important because the register with the mupen core
_videoProvider = new N64VideoProvider ( api , videosettings ) ;
_audioProvider = new N64Audio ( api ) ;
2014-12-05 00:59:00 +00:00
_inputProvider = new N64Input ( this . AsInputPollable ( ) , api , comm , this . _syncSettings . Controllers ) ;
2014-05-14 01:50:36 +00:00
2014-07-20 16:59:03 +00:00
string rsp = _syncSettings . Rsp = = N64SyncSettings . RspType . Rsp_Hle ?
2014-05-14 01:50:36 +00:00
"mupen64plus-rsp-hle.dll" :
"mupen64plus-rsp-z64-hlevideo.dll" ;
api . AttachPlugin ( mupen64plusApi . m64p_plugin_type . M64PLUGIN_RSP , rsp ) ;
2014-05-13 00:06:33 +00:00
InitMemoryDomains ( ) ;
RefreshMemoryCallbacks ( ) ;
api . AsyncExecuteEmulator ( ) ;
2014-06-28 13:00:53 +00:00
SetControllerButtons ( ) ;
2014-05-13 00:06:33 +00:00
}
2014-12-04 03:38:30 +00:00
public IEmulatorServiceProvider ServiceProvider { get ; private set ; }
2014-09-27 12:56:55 +00:00
public bool UsingExpansionSlot
{
get { return ! _disableExpansionSlot ; }
}
public bool IsOverridingUserExpansionSlotSetting { get ; set ; }
2014-05-13 00:31:32 +00:00
public void Dispose ( )
2013-11-11 03:20:33 +00:00
{
2014-05-13 00:31:32 +00:00
RunThreadAction ( ( ) = >
{
_videoProvider . Dispose ( ) ;
_audioProvider . Dispose ( ) ;
api . Dispose ( ) ;
} ) ;
2013-11-18 03:29:47 +00:00
2014-05-13 00:31:32 +00:00
EndThreadLoop ( ) ;
}
private void ThreadLoop ( )
{
for ( ; ; )
2013-11-18 03:29:47 +00:00
{
2014-05-13 00:31:32 +00:00
_pendingThreadEvent . WaitOne ( ) ;
_pendingThreadAction ( ) ;
if ( _pendingThreadTerminate )
{
break ;
}
_completeThreadEvent . Set ( ) ;
2013-11-18 03:29:47 +00:00
}
2014-05-13 00:31:32 +00:00
_pendingThreadTerminate = false ;
_completeThreadEvent . Set ( ) ;
}
2013-11-18 03:29:47 +00:00
2014-05-13 00:31:32 +00:00
private void RunThreadAction ( Action action )
{
_pendingThreadAction = action ;
_pendingThreadEvent . Set ( ) ;
_completeThreadEvent . WaitOne ( ) ;
}
2013-11-18 03:29:47 +00:00
2014-05-13 00:31:32 +00:00
private void StartThreadLoop ( )
{
2014-09-07 21:48:05 +00:00
var thread = new Thread ( ThreadLoop ) { IsBackground = true } ;
thread . Start ( ) ; // will this solve the hanging process problem?
2014-05-13 00:31:32 +00:00
}
2013-11-18 03:29:47 +00:00
2014-05-13 00:31:32 +00:00
private void EndThreadLoop ( )
{
RunThreadAction ( ( ) = > { _pendingThreadTerminate = true ; } ) ;
}
2013-11-18 03:29:47 +00:00
2014-05-13 00:31:32 +00:00
public void FrameAdvance ( bool render , bool rendersound )
{
2014-08-03 00:00:26 +00:00
IsVIFrame = false ;
2014-05-13 00:31:32 +00:00
_audioProvider . RenderSound = rendersound ;
2013-11-18 03:29:47 +00:00
2014-05-13 00:31:32 +00:00
if ( Controller [ "Reset" ] )
2013-11-18 03:29:47 +00:00
{
2014-05-13 00:31:32 +00:00
api . soft_reset ( ) ;
2013-11-18 03:29:47 +00:00
}
2014-05-13 00:31:32 +00:00
if ( Controller [ "Power" ] )
2013-11-18 03:29:47 +00:00
{
2014-05-13 00:31:32 +00:00
api . hard_reset ( ) ;
2013-11-18 03:29:47 +00:00
}
2014-05-13 00:31:32 +00:00
api . frame_advance ( ) ;
if ( IsLagFrame )
{
LagCount + + ;
}
Frame + + ;
2013-11-11 03:20:33 +00:00
}
2013-04-29 01:57:41 +00:00
public string SystemId { get { return "N64" ; } }
2013-08-24 16:54:22 +00:00
public string BoardName { get { return null ; } }
2013-04-29 01:57:41 +00:00
public CoreComm CoreComm { get ; private set ; }
2014-01-15 11:24:47 +00:00
2014-05-13 00:06:33 +00:00
public IVideoProvider VideoProvider { get { return _videoProvider ; } }
2014-05-13 00:31:32 +00:00
2013-11-16 21:29:42 +00:00
public DisplayType DisplayType { get { return _display_type ; } }
2013-05-04 02:46:37 +00:00
2013-05-01 14:38:47 +00:00
public ISoundProvider SoundProvider { get { return null ; } }
2014-05-13 00:31:32 +00:00
2014-05-13 00:06:33 +00:00
public ISyncSoundProvider SyncSoundProvider { get { return _audioProvider . Resampler ; } }
2014-05-13 00:31:32 +00:00
2013-05-01 14:38:47 +00:00
public bool StartAsyncSound ( ) { return false ; }
2014-05-13 00:31:32 +00:00
2013-04-29 01:57:41 +00:00
public void EndAsyncSound ( ) { }
2014-05-13 00:31:32 +00:00
public ControllerDefinition ControllerDefinition
{
get { return _inputProvider . ControllerDefinition ; }
}
2014-01-24 17:46:35 +00:00
public IController Controller
2013-04-29 01:57:41 +00:00
{
2014-05-13 00:06:33 +00:00
get { return _inputProvider . Controller ; }
set { _inputProvider . Controller = value ; }
2014-01-24 17:46:35 +00:00
}
2013-04-29 01:57:41 +00:00
2013-11-03 16:29:51 +00:00
public void ResetCounters ( )
2013-05-05 22:53:22 +00:00
{
Frame = 0 ;
LagCount = 0 ;
IsLagFrame = false ;
}
2014-01-15 11:24:47 +00:00
public bool DeterministicEmulation { get { return false ; } }
2013-04-29 01:57:41 +00:00
}
}