2011-01-11 02:55:51 +00:00
using System ;
2017-04-10 00:59:23 +00:00
using System.Diagnostics ;
2012-10-29 08:37:22 +00:00
using System.IO ;
2013-07-27 00:30:08 +00:00
using System.Collections.Generic ;
2012-10-29 08:37:22 +00:00
using System.Reflection ;
using System.Runtime.InteropServices ;
2011-01-11 02:55:51 +00:00
using System.Windows.Forms ;
2012-03-12 04:44:34 +00:00
#if WINDOWS
2011-05-23 00:33:05 +00:00
using Microsoft.VisualBasic.ApplicationServices ;
2012-03-12 04:44:34 +00:00
#endif
2011-01-11 02:55:51 +00:00
2013-10-27 22:07:40 +00:00
using BizHawk.Common ;
2013-10-25 00:57:23 +00:00
using BizHawk.Client.Common ;
2013-11-03 03:54:37 +00:00
namespace BizHawk.Client.EmuHawk
2011-01-11 02:55:51 +00:00
{
2011-06-19 23:39:25 +00:00
static class Program
{
2013-01-02 20:11:27 +00:00
static Program ( )
2011-06-19 23:39:25 +00:00
{
2016-05-05 14:01:01 +00:00
//this needs to be done before the warnings/errors show up
Application . EnableVisualStyles ( ) ;
Application . SetCompatibleTextRenderingDefault ( false ) ;
2013-02-24 20:17:12 +00:00
//http://www.codeproject.com/Articles/310675/AppDomain-AssemblyResolve-Event-Tips
2012-10-29 08:37:22 +00:00
#if WINDOWS
2016-01-14 07:50:41 +00:00
//try loading libraries we know we'll need
//something in the winforms, etc. code below will cause .net to popup a missing msvcr100.dll in case that one's missing
//but oddly it lets us proceed and we'll then catch it here
var d3dx9 = Win32 . LoadLibrary ( "d3dx9_43.dll" ) ;
var vc2015 = Win32 . LoadLibrary ( "vcruntime140.dll" ) ;
var vc2010 = Win32 . LoadLibrary ( "msvcr100.dll" ) ; //TODO - check version?
var vc2010p = Win32 . LoadLibrary ( "msvcp100.dll" ) ;
2016-05-05 14:01:01 +00:00
bool fail = false , warn = false ;
warn | = d3dx9 = = IntPtr . Zero ;
2016-01-14 07:50:41 +00:00
fail | = vc2015 = = IntPtr . Zero ;
fail | = vc2010 = = IntPtr . Zero ;
fail | = vc2010p = = IntPtr . Zero ;
2016-05-05 14:01:01 +00:00
if ( fail | | warn )
2016-01-14 07:50:41 +00:00
{
var sw = new System . IO . StringWriter ( ) ;
sw . WriteLine ( "[ OK ] .Net 4.0 (You couldn't even get here without it)" ) ;
sw . WriteLine ( "[{0}] Direct3d 9" , d3dx9 = = IntPtr . Zero ? "FAIL" : " OK " ) ;
sw . WriteLine ( "[{0}] Visual C++ 2010 SP1 Runtime" , ( vc2010 = = IntPtr . Zero | | vc2010p = = IntPtr . Zero ) ? "FAIL" : " OK " ) ;
sw . WriteLine ( "[{0}] Visual C++ 2015 Runtime" , ( vc2015 = = IntPtr . Zero ) ? "FAIL" : " OK " ) ;
var str = sw . ToString ( ) ;
2016-05-05 14:01:01 +00:00
var box = new BizHawk . Client . EmuHawk . CustomControls . PrereqsAlert ( ! fail ) ;
2016-01-14 07:50:41 +00:00
box . textBox1 . Text = str ;
box . ShowDialog ( ) ;
2016-05-05 14:01:01 +00:00
if ( ! fail ) { }
else
System . Diagnostics . Process . GetCurrentProcess ( ) . Kill ( ) ;
2016-01-14 07:50:41 +00:00
}
Win32 . FreeLibrary ( d3dx9 ) ;
Win32 . FreeLibrary ( vc2015 ) ;
Win32 . FreeLibrary ( vc2010 ) ;
Win32 . FreeLibrary ( vc2010p ) ;
2012-10-29 08:37:22 +00:00
// this will look in subdirectory "dll" to load pinvoked stuff
2014-06-29 02:28:48 +00:00
string dllDir = Path . Combine ( Path . GetDirectoryName ( Assembly . GetExecutingAssembly ( ) . Location ) , "dll" ) ;
2013-07-27 00:30:08 +00:00
SetDllDirectory ( dllDir ) ;
2017-02-23 11:27:27 +00:00
//in case assembly resolution fails, such as if we moved them into the dll subdiretory, this event handler can reroute to them
AppDomain . CurrentDomain . AssemblyResolve + = new ResolveEventHandler ( CurrentDomain_AssemblyResolve ) ;
2013-07-27 00:30:08 +00:00
//but before we even try doing that, whack the MOTW from everything in that directory (thats a dll)
//otherwise, some people will have crashes at boot-up due to .net security disliking MOTW.
//some people are getting MOTW through a combination of browser used to download bizhawk, and program used to dearchive it
WhackAllMOTW ( dllDir ) ;
2012-09-04 00:20:36 +00:00
2017-02-22 00:23:02 +00:00
//We need to do it here too... otherwise people get exceptions when externaltools we distribute try to startup
2012-10-29 08:37:22 +00:00
#endif
2013-01-02 20:11:27 +00:00
}
[STAThread]
2015-12-15 08:22:44 +00:00
static int Main ( string [ ] args )
2013-01-02 20:11:27 +00:00
{
2015-12-15 08:22:44 +00:00
return SubMain ( args ) ;
2013-01-02 20:11:27 +00:00
}
2016-01-14 07:50:41 +00:00
private static class Win32
{
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary ( string dllToLoad ) ;
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress ( IntPtr hModule , string procedureName ) ;
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary ( IntPtr hModule ) ;
}
2013-01-02 20:11:27 +00:00
//NoInlining should keep this code from getting jammed into Main() which would create dependencies on types which havent been setup by the resolver yet... or something like that
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
2015-12-15 08:22:44 +00:00
static int SubMain ( string [ ] args )
2013-01-02 20:11:27 +00:00
{
2013-01-01 23:10:47 +00:00
// this check has to be done VERY early. i stepped through a debug build with wrong .dll versions purposely used,
// and there was a TypeLoadException before the first line of SubMain was reached (some static ColorType init?)
2012-12-25 20:36:04 +00:00
// zero 25-dec-2012 - only do for public builds. its annoying during development
2014-06-04 17:02:54 +00:00
if ( ! VersionInfo . DeveloperBuild )
2012-12-17 15:38:56 +00:00
{
var thisversion = typeof ( Program ) . Assembly . GetName ( ) . Version ;
2015-01-01 20:54:01 +00:00
var utilversion = Assembly . Load ( new AssemblyName ( "Bizhawk.Client.Common" ) ) . GetName ( ) . Version ;
var emulversion = Assembly . Load ( new AssemblyName ( "Bizhawk.Emulation.Cores" ) ) . GetName ( ) . Version ;
2012-12-17 15:38:56 +00:00
if ( thisversion ! = utilversion | | thisversion ! = emulversion )
{
MessageBox . Show ( "Conflicting revisions found! Don't mix .dll versions!" ) ;
2015-12-15 08:22:44 +00:00
return - 1 ;
2012-12-17 15:38:56 +00:00
}
}
2015-11-17 23:26:03 +00:00
BizHawk . Common . TempFileCleaner . Start ( ) ;
2016-05-05 14:01:01 +00:00
2015-11-15 08:27:48 +00:00
HawkFile . ArchiveHandlerFactory = new SevenZipSharpArchiveHandler ( ) ;
2016-04-02 19:33:34 +00:00
2014-06-29 02:28:48 +00:00
string iniPath = Path . Combine ( Path . GetDirectoryName ( Assembly . GetExecutingAssembly ( ) . Location ) , "config.ini" ) ;
2013-12-22 02:47:35 +00:00
Global . Config = ConfigService . Load < Config > ( iniPath ) ;
2013-08-11 19:55:13 +00:00
Global . Config . ResolveDefaults ( ) ;
2015-11-15 08:27:48 +00:00
BizHawk . Client . Common . StringLogUtil . DefaultToDisk = Global . Config . MoviesOnDisk ;
2016-02-10 07:09:43 +00:00
BizHawk . Client . Common . StringLogUtil . DefaultToAWE = Global . Config . MoviesInAWE ;
2013-10-27 22:07:40 +00:00
2017-05-22 16:16:15 +00:00
// super hacky! this needs to be done first. still not worth the trouble to make this system fully proper
2014-12-16 19:35:52 +00:00
for ( int i = 0 ; i < args . Length ; i + + )
{
var arg = args [ i ] . ToLower ( ) ;
if ( arg . StartsWith ( "--gdi" ) )
{
Global . Config . DispMethod = Config . EDispMethod . GdiPlus ;
}
}
2017-05-22 16:16:15 +00:00
// create IGL context. we do this whether or not the user has selected OpenGL, so that we can run opengl-based emulator cores
2016-04-02 19:33:34 +00:00
GlobalWin . IGL_GL = new Bizware . BizwareGL . Drivers . OpenTK . IGL_TK ( 2 , 0 , false ) ;
2014-12-08 02:15:42 +00:00
2017-05-22 16:16:15 +00:00
// setup the GL context manager, needed for coping with multiple opengl cores vs opengl display method
2016-02-22 06:23:20 +00:00
GLManager . CreateInstance ( GlobalWin . IGL_GL ) ;
2015-03-06 03:05:46 +00:00
GlobalWin . GLManager = GLManager . Instance ;
2014-01-27 05:37:04 +00:00
2014-12-08 02:15:42 +00:00
//now create the "GL" context for the display method. we can reuse the IGL_TK context if opengl display method is chosen
2015-03-01 19:29:33 +00:00
REDO_DISPMETHOD :
2014-12-08 02:15:42 +00:00
if ( Global . Config . DispMethod = = Config . EDispMethod . GdiPlus )
GlobalWin . GL = new Bizware . BizwareGL . Drivers . GdiPlus . IGL_GdiPlus ( ) ;
else if ( Global . Config . DispMethod = = Config . EDispMethod . SlimDX9 )
2016-05-05 14:01:01 +00:00
{
try
{
GlobalWin . GL = new Bizware . BizwareGL . Drivers . SlimDX . IGL_SlimDX9 ( ) ;
}
catch ( Exception ex )
{
var e2 = new Exception ( "Initialization of Direct3d 9 Display Method failed; falling back to GDI+" , ex ) ;
new ExceptionBox ( e2 ) . ShowDialog ( ) ;
2017-05-22 16:16:15 +00:00
// fallback
2016-05-05 14:01:01 +00:00
Global . Config . DispMethod = Config . EDispMethod . GdiPlus ;
goto REDO_DISPMETHOD ;
}
}
2014-12-08 02:15:42 +00:00
else
2014-12-16 20:24:14 +00:00
{
2014-12-08 02:15:42 +00:00
GlobalWin . GL = GlobalWin . IGL_GL ;
2017-05-22 16:16:15 +00:00
// check the opengl version and dont even try to boot this crap up if its too old
2014-12-16 20:24:14 +00:00
int version = GlobalWin . IGL_GL . Version ;
if ( version < 200 )
{
2017-05-22 16:16:15 +00:00
// fallback
2014-12-16 20:24:14 +00:00
Global . Config . DispMethod = Config . EDispMethod . GdiPlus ;
goto REDO_DISPMETHOD ;
}
}
2014-12-08 02:15:42 +00:00
2017-05-22 16:16:15 +00:00
// try creating a GUI Renderer. If that doesn't succeed. we fallback
2016-04-02 19:33:34 +00:00
try
{
2017-04-29 21:49:29 +00:00
using ( GlobalWin . GL . CreateRenderer ( ) ) { }
2016-04-02 19:33:34 +00:00
}
catch ( Exception ex )
{
var e2 = new Exception ( "Initialization of Display Method failed; falling back to GDI+" , ex ) ;
new ExceptionBox ( e2 ) . ShowDialog ( ) ;
//fallback
Global . Config . DispMethod = Config . EDispMethod . GdiPlus ;
goto REDO_DISPMETHOD ;
}
2014-08-01 02:41:27 +00:00
//WHY do we have to do this? some intel graphics drivers (ig7icd64.dll 10.18.10.3304 on an unknown chip on win8.1) are calling SetDllDirectory() for the process, which ruins stuff.
//The relevant initialization happened just before in "create IGL context".
//It isn't clear whether we need the earlier SetDllDirectory(), but I think we do.
//note: this is pasted instead of being put in a static method due to this initialization code being sensitive to things like that, and not wanting to cause it to break
//pasting should be safe (not affecting the jit order of things)
string dllDir = Path . Combine ( Path . GetDirectoryName ( Assembly . GetExecutingAssembly ( ) . Location ) , "dll" ) ;
SetDllDirectory ( dllDir ) ;
2017-04-10 00:59:23 +00:00
try
{
2012-03-12 04:44:34 +00:00
#if WINDOWS
2011-06-19 23:39:25 +00:00
if ( Global . Config . SingleInstanceMode )
{
2013-08-25 22:43:34 +00:00
try
{
new SingleInstanceController ( args ) . Run ( args ) ;
}
2013-09-14 19:34:14 +00:00
catch ( ObjectDisposedException )
2013-08-25 22:43:34 +00:00
{
/*Eat it, MainForm disposed itself and Run attempts to dispose of itself. Eventually we would want to figure out a way to prevent that, but in the meantime it is harmless, so just eat the error*/
}
2011-06-19 23:39:25 +00:00
}
else
2012-03-12 04:44:34 +00:00
#endif
2015-03-01 19:29:33 +00:00
{
2012-04-16 08:18:41 +00:00
using ( var mf = new MainForm ( args ) )
{
var title = mf . Text ;
mf . Show ( ) ;
mf . Text = title ;
2015-03-01 19:29:33 +00:00
2013-11-23 02:07:02 +00:00
try
{
2017-04-10 00:59:23 +00:00
GlobalWin . ExitCode = mf . ProgramRunLoop ( ) ;
2013-11-23 02:07:02 +00:00
}
2017-04-10 00:59:23 +00:00
catch ( Exception e ) when ( ! Debugger . IsAttached & & ! VersionInfo . DeveloperBuild & & Global . MovieSession . Movie . IsActive )
2015-03-01 19:29:33 +00:00
{
2017-04-10 00:59:23 +00:00
var result = MessageBox . Show (
"EmuHawk has thrown a fatal exception and is about to close.\nA movie has been detected. Would you like to try to save?\n(Note: Depending on what caused this error, this may or may not succeed)" ,
"Fatal error: " + e . GetType ( ) . Name ,
MessageBoxButtons . YesNo ,
MessageBoxIcon . Exclamation
) ;
if ( result = = DialogResult . Yes )
2013-11-23 02:07:02 +00:00
{
2017-04-10 00:59:23 +00:00
Global . MovieSession . Movie . Save ( ) ;
2015-03-01 19:29:33 +00:00
}
2013-11-23 02:07:02 +00:00
}
2012-04-16 08:18:41 +00:00
}
2011-06-19 23:39:25 +00:00
}
2017-04-10 00:59:23 +00:00
}
catch ( Exception e ) when ( ! Debugger . IsAttached )
{
new ExceptionBox ( e ) . ShowDialog ( ) ;
}
finally
{
if ( GlobalWin . Sound ! = null )
2015-01-31 04:49:53 +00:00
{
2017-04-10 00:59:23 +00:00
GlobalWin . Sound . Dispose ( ) ;
GlobalWin . Sound = null ;
2017-04-08 02:57:01 +00:00
}
2017-04-10 00:59:23 +00:00
GlobalWin . GL . Dispose ( ) ;
Input . Cleanup ( ) ;
2015-03-01 19:29:33 +00:00
}
2015-10-12 02:03:09 +00:00
//cleanup:
//cleanup IGL stuff so we can get better refcounts when exiting process, for debugging
//DOESNT WORK FOR SOME REASON
//GlobalWin.IGL_GL = new Bizware.BizwareGL.Drivers.OpenTK.IGL_TK();
//GLManager.Instance.Dispose();
//if (GlobalWin.IGL_GL != GlobalWin.GL)
// GlobalWin.GL.Dispose();
//((IDisposable)GlobalWin.IGL_GL).Dispose();
2015-12-15 08:22:44 +00:00
//return 0 assuming things have gone well, non-zero values could be used as error codes or for scripting purposes
return GlobalWin . ExitCode ;
2015-10-12 02:03:09 +00:00
} //SubMain
2011-05-23 00:33:05 +00:00
2012-10-29 08:37:22 +00:00
//declared here instead of a more usual place to avoid dependencies on the more usual place
#if WINDOWS
[DllImport("kernel32.dll", SetLastError = true)]
2014-08-01 02:41:27 +00:00
static extern uint SetDllDirectory ( string lpPathName ) ;
2013-07-27 00:30:08 +00:00
2017-03-21 16:09:28 +00:00
[DllImport("kernel32.dll", EntryPoint = "DeleteFileW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)]
static extern bool DeleteFileW ( [ MarshalAs ( UnmanagedType . LPWStr ) ] string lpFileName ) ;
public static void RemoveMOTW ( string path )
{
DeleteFileW ( path + ":Zone.Identifier" ) ;
}
2017-02-23 10:54:29 +00:00
static void WhackAllMOTW ( string dllDir )
2013-07-27 00:30:08 +00:00
{
var todo = new Queue < DirectoryInfo > ( new [ ] { new DirectoryInfo ( dllDir ) } ) ;
while ( todo . Count > 0 )
{
var di = todo . Dequeue ( ) ;
foreach ( var disub in di . GetDirectories ( ) ) todo . Enqueue ( disub ) ;
foreach ( var fi in di . GetFiles ( "*.dll" ) )
2017-03-21 16:09:28 +00:00
RemoveMOTW ( fi . FullName ) ;
2014-11-06 19:04:51 +00:00
foreach ( var fi in di . GetFiles ( "*.exe" ) )
2017-03-21 16:09:28 +00:00
RemoveMOTW ( fi . FullName ) ;
2013-07-27 00:30:08 +00:00
}
}
2012-10-29 08:37:22 +00:00
#endif
static Assembly CurrentDomain_AssemblyResolve ( object sender , ResolveEventArgs args )
{
2013-01-02 20:11:27 +00:00
lock ( AppDomain . CurrentDomain )
{
var asms = AppDomain . CurrentDomain . GetAssemblies ( ) ;
foreach ( var asm in asms )
if ( asm . FullName = = args . Name )
return asm ;
//load missing assemblies by trying to find them in the dll directory
string dllname = new AssemblyName ( args . Name ) . Name + ".dll" ;
2014-06-29 02:28:48 +00:00
string directory = Path . Combine ( Path . GetDirectoryName ( Assembly . GetExecutingAssembly ( ) . Location ) , "dll" ) ;
2013-01-02 20:11:27 +00:00
string fname = Path . Combine ( directory , dllname ) ;
if ( ! File . Exists ( fname ) ) return null ;
//it is important that we use LoadFile here and not load from a byte array; otherwise mixed (managed/unamanged) assemblies can't load
return Assembly . LoadFile ( fname ) ;
}
2012-10-29 08:37:22 +00:00
}
2012-03-12 04:44:34 +00:00
#if WINDOWS
2011-06-19 23:39:25 +00:00
public class SingleInstanceController : WindowsFormsApplicationBase
{
2013-04-16 00:19:31 +00:00
readonly string [ ] cmdArgs ;
2011-06-19 23:39:25 +00:00
public SingleInstanceController ( string [ ] args )
{
cmdArgs = args ;
IsSingleInstance = true ;
StartupNextInstance + = this_StartupNextInstance ;
}
void this_StartupNextInstance ( object sender , StartupNextInstanceEventArgs e )
{
2014-12-29 04:20:47 +00:00
if ( e . CommandLine . Count > = 1 )
2015-11-12 23:47:39 +00:00
( MainForm as MainForm ) . LoadRom ( e . CommandLine [ 0 ] , new MainForm . LoadRomArgs ( ) { OpenAdvanced = new OpenAdvanced_OpenRom ( ) } ) ;
2011-06-19 23:39:25 +00:00
}
2011-05-23 00:33:05 +00:00
2011-06-19 23:39:25 +00:00
protected override void OnCreateMainForm ( )
{
2013-08-25 22:43:34 +00:00
MainForm = new MainForm ( cmdArgs ) ;
var title = MainForm . Text ;
MainForm . Show ( ) ;
MainForm . Text = title ;
2015-12-15 08:22:44 +00:00
GlobalWin . ExitCode = ( MainForm as MainForm ) . ProgramRunLoop ( ) ;
2015-03-01 19:29:33 +00:00
}
2011-06-19 23:39:25 +00:00
}
2011-08-21 01:07:58 +00:00
2014-01-27 05:37:04 +00:00
2012-03-12 04:44:34 +00:00
#endif
2011-06-19 23:39:25 +00:00
}
2011-01-11 02:55:51 +00:00
}