Merge pull request #1568 from TASVideos/unix_prep

Preparation for Unix port
This commit is contained in:
James Groom 2019-05-29 15:01:27 +10:00 committed by GitHub
commit 27a4062ea2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 463 additions and 462 deletions

5
.gitignore vendored
View File

@ -12,6 +12,7 @@ svnrev.cs
**/ipch/** **/ipch/**
*.ilk *.ilk
*.il
*.tlog *.tlog
*.obj *.obj
*.o *.o
@ -74,3 +75,7 @@ libsnes/vs2015/libsnes.VC.db
waterbox/**/*.wbx waterbox/**/*.wbx
waterbox/**/*.wbx.in waterbox/**/*.wbx.in
/BizHawkTool_template.zip /BizHawkTool_template.zip
mono_crash*
.idea

View File

@ -8,11 +8,14 @@ libpath=""
if [ "$(command -v lsb_release)" ]; then if [ "$(command -v lsb_release)" ]; then
case "$(lsb_release -i | cut -c17- | tr -d "\n")" in case "$(lsb_release -i | cut -c17- | tr -d "\n")" in
"Arch"|"ManjaroLinux") libpath="/usr/lib/wine";; "Arch"|"ManjaroLinux") libpath="/usr/lib/wine";;
"Debian"|"Ubuntu"|"LinuxMint") libpath="/usr/lib/x86_64-linux-gnu/wine";; "Debian"|"LinuxMint") libpath="/usr/lib/x86_64-linux-gnu/wine";;
"Ubuntu") libpath="/usr/lib/x86_64-linux-gnu/wine"; export MONO_WINFORMS_XIM_STYLE=disabled;; # see https://bugzilla.xamarin.com/show_bug.cgi?id=28047#c9
esac esac
else
printf "Distro does not provide LSB release info API! (You've met with a terrible fate, haven't you?)\n"
fi fi
if [ -z "$libpath" ]; then if [ -z "$libpath" ]; then
printf "%s\n" "Unknown distro, assuming WINE library location is /usr/lib/wine..." printf "%s\n" "Unknown distro, assuming WINE library location is /usr/lib/wine..."
libpath="/usr/lib/wine" libpath="/usr/lib/wine"
fi fi
LD_LIBRARY_PATH="$libpath" mono ./EmuHawk.exe LD_LIBRARY_PATH="$libpath" mono ./EmuHawk.exe >EmuHawkMono_laststdout.txt

View File

@ -89,7 +89,7 @@ namespace SevenZip
// private static string _LibraryVersion; // private static string _LibraryVersion;
private static bool? _modifyCapabale; private static bool? _modifyCapabale;
private static readonly PlatformLinkedLibSingleton.PlatformLinkedLibManager libLoader = PlatformLinkedLibSingleton.LinkedLibManager; private static readonly OSTailoredCode.ILinkedLibManager libLoader = OSTailoredCode.LinkedLibManager;
private static void InitUserInFormat(object user, InArchiveFormat format) private static void InitUserInFormat(object user, InArchiveFormat format)
{ {

View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using BizHawk.Common;
using BizHawk.Emulation.Common; using BizHawk.Emulation.Common;
// ReSharper disable FieldCanBeMadeReadOnly.Global // ReSharper disable FieldCanBeMadeReadOnly.Global
@ -341,8 +342,13 @@ namespace BizHawk.Client.Common
public int DispPrescale = 1; public int DispPrescale = 1;
// warning: we dont even want to deal with changing this at runtime. but we want it changed here for config purposes. so dont check this variable. check in GlobalWin or something like that. /// <remarks>
public EDispMethod DispMethod = EDispMethod.SlimDX9; /// warning: we dont even want to deal with changing this at runtime. but we want it changed here for config purposes. so dont check this variable. check in GlobalWin or something like that.
/// force DX for Windows and GDI+ for Unix when a new config is generated
/// </remarks>
public EDispMethod DispMethod = OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows
? EDispMethod.SlimDX9
: EDispMethod.GdiPlus;
public int DispChrome_FrameWindowed = 2; public int DispChrome_FrameWindowed = 2;
public bool DispChrome_StatusBarWindowed = true; public bool DispChrome_StatusBarWindowed = true;
@ -370,11 +376,9 @@ namespace BizHawk.Client.Common
public int DispCropBottom = 0; public int DispCropBottom = 0;
// Sound options // Sound options
#if WINDOWS public ESoundOutputMethod SoundOutputMethod = OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows
public ESoundOutputMethod SoundOutputMethod = ESoundOutputMethod.DirectSound; ? ESoundOutputMethod.DirectSound
#else : ESoundOutputMethod.OpenAL; // force OpenAL for Unix when config is generated
public ESoundOutputMethod SoundOutputMethod = ESoundOutputMethod.OpenAL;
#endif
public bool SoundEnabled = true; public bool SoundEnabled = true;
public bool SoundEnabledNormal = true; public bool SoundEnabledNormal = true;
public bool SoundEnabledRWFF = true; public bool SoundEnabledRWFF = true;

View File

@ -1,5 +1,8 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using BizHawk.Common;
using NLua; using NLua;
// TODO - evaluate for re-entrancy problems // TODO - evaluate for re-entrancy problems
@ -18,12 +21,10 @@ namespace BizHawk.Client.Common
private string _currentDirectory; private string _currentDirectory;
#if WINDOWS
[DllImport("kernel32.dll", SetLastError = true)] [DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetCurrentDirectoryW(byte* lpPathName); static extern bool SetCurrentDirectoryW(byte* lpPathName);
[DllImport("kernel32.dll", SetLastError=true)] [DllImport("kernel32.dll", SetLastError=true)]
static extern uint GetCurrentDirectoryW(uint nBufferLength, byte* pBuffer); static extern uint GetCurrentDirectoryW(uint nBufferLength, byte* pBuffer);
#endif
private bool CoolSetCurrentDirectory(string path, string currDirSpeedHack = null) private bool CoolSetCurrentDirectory(string path, string currDirSpeedHack = null)
{ {
@ -42,40 +43,43 @@ namespace BizHawk.Client.Common
return true; return true;
} }
// WARNING: setting the current directory is SLOW!!! security checks for some reason. if (OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows)
// so we're bypassing it with windows hacks {
#if WINDOWS // WARNING: setting the current directory is SLOW!!! security checks for some reason.
// so we're bypassing it with windows hacks
fixed (byte* pstr = &System.Text.Encoding.Unicode.GetBytes($"{target}\0")[0]) fixed (byte* pstr = &System.Text.Encoding.Unicode.GetBytes($"{target}\0")[0])
return SetCurrentDirectoryW(pstr); return SetCurrentDirectoryW(pstr);
#else }
if (System.IO.Directory.Exists(CurrentDirectory)) // race condition for great justice else
{ {
Environment.CurrentDirectory = CurrentDirectory; // thats right, you can't set a directory as current that doesnt exist because .net's got to do SENSELESS SLOW-ASS SECURITY CHECKS on it and it can't do that on a NONEXISTENT DIRECTORY if (System.IO.Directory.Exists(_currentDirectory)) // race condition for great justice
return true; {
} Environment.CurrentDirectory = _currentDirectory; // thats right, you can't set a directory as current that doesnt exist because .net's got to do SENSELESS SLOW-ASS SECURITY CHECKS on it and it can't do that on a NONEXISTENT DIRECTORY
else return true;
{ }
return false; else
} {
#endif return false;
}
}
} }
private string CoolGetCurrentDirectory() private string CoolGetCurrentDirectory()
{ {
// GUESS WHAT! if (OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows)
// .NET DOES A SECURITY CHECK ON THE DIRECTORY WE JUST RETRIEVED {
// AS IF ASKING FOR THE CURRENT DIRECTORY IS EQUIVALENT TO TRYING TO ACCESS IT // GUESS WHAT!
// SCREW YOU // .NET DOES A SECURITY CHECK ON THE DIRECTORY WE JUST RETRIEVED
#if WINDOWS // AS IF ASKING FOR THE CURRENT DIRECTORY IS EQUIVALENT TO TRYING TO ACCESS IT
// SCREW YOU
var buf = new byte[32768]; var buf = new byte[32768];
fixed(byte* pBuf = &buf[0]) fixed (byte* pBuf = &buf[0])
{ return System.Text.Encoding.Unicode.GetString(buf, 0, 2 * (int) GetCurrentDirectoryW(32767, pBuf));
uint ret = GetCurrentDirectoryW(32767, pBuf); }
return System.Text.Encoding.Unicode.GetString(buf, 0, (int)ret*2); else
} {
#else
return Environment.CurrentDirectory; return Environment.CurrentDirectory;
#endif }
} }
private void Sandbox(Action callback, Action exceptionCallback) private void Sandbox(Action callback, Action exceptionCallback)

View File

@ -6,6 +6,7 @@ using System.Diagnostics;
using System.Windows.Forms; using System.Windows.Forms;
using BizHawk.Client.Common; using BizHawk.Client.Common;
using BizHawk.Common;
using BizHawk.Emulation.Common; using BizHawk.Emulation.Common;
namespace BizHawk.Client.EmuHawk namespace BizHawk.Client.EmuHawk
@ -85,12 +86,9 @@ namespace BizHawk.Client.EmuHawk
try try
{ {
_ffmpeg = new Process(); _ffmpeg = new Process();
#if WINDOWS _ffmpeg.StartInfo.FileName = OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows
_ffmpeg.StartInfo.FileName = Path.Combine(PathManager.GetDllDirectory(), "ffmpeg.exe"); ? Path.Combine(PathManager.GetDllDirectory(), "ffmpeg.exe")
#else : "ffmpeg";
ffmpeg.StartInfo.FileName = "ffmpeg"; // expecting native version to be in path
#endif
_ffmpeg.StartInfo.Arguments = $"-y -f nut -i - {_token.Commandline} \"{_baseName}{(_segment == 0 ? string.Empty : $"_{_segment}")}{_ext}\""; _ffmpeg.StartInfo.Arguments = $"-y -f nut -i - {_token.Commandline} \"{_baseName}{(_segment == 0 ? string.Empty : $"_{_segment}")}{_ext}\"";
_ffmpeg.StartInfo.CreateNoWindow = true; _ffmpeg.StartInfo.CreateNoWindow = true;

View File

@ -113,8 +113,8 @@
<Reference Include="WindowsBase" /> <Reference Include="WindowsBase" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\BizHawk.Common\PlatformLinkedLibSingleton.cs"> <Compile Include="..\BizHawk.Common\OSTailoredCode.cs">
<Link>PlatformLinkedLibSingleton.cs</Link> <Link>OSTailoredCode.cs</Link>
</Compile> </Compile>
<Compile Include="..\Version\svnrev.cs"> <Compile Include="..\Version\svnrev.cs">
<Link>svnrev.cs</Link> <Link>svnrev.cs</Link>
@ -2283,4 +2283,4 @@
<PreBuildEvent /> <PreBuildEvent />
</PropertyGroup> </PropertyGroup>
<Import Project="$(SolutionDir)Build\Common.targets" /> <Import Project="$(SolutionDir)Build\Common.targets" />
</Project> </Project>

View File

@ -123,24 +123,24 @@ namespace BizHawk.Client.EmuHawk
public static void Initialize() public static void Initialize()
{ {
if (PlatformLinkedLibSingleton.RunningOnUnix) if (OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows)
{
OTK_Keyboard.Initialize();
// OTK_Gamepad.Initialize();
}
else
{ {
KeyInput.Initialize(); KeyInput.Initialize();
IPCKeyInput.Initialize(); IPCKeyInput.Initialize();
GamePad.Initialize(); GamePad.Initialize();
GamePad360.Initialize(); GamePad360.Initialize();
} }
else
{
OTK_Keyboard.Initialize();
// OTK_Gamepad.Initialize();
}
Instance = new Input(); Instance = new Input();
} }
public static void Cleanup() public static void Cleanup()
{ {
if (!PlatformLinkedLibSingleton.RunningOnUnix) if (OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows)
{ {
KeyInput.Cleanup(); KeyInput.Cleanup();
GamePad.Cleanup(); GamePad.Cleanup();
@ -331,18 +331,18 @@ namespace BizHawk.Client.EmuHawk
{ {
while (true) while (true)
{ {
var keyEvents = PlatformLinkedLibSingleton.RunningOnUnix var keyEvents = OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows
? OTK_Keyboard.Update() ? KeyInput.Update().Concat(IPCKeyInput.Update())
: KeyInput.Update().Concat(IPCKeyInput.Update()); : OTK_Keyboard.Update();
if (PlatformLinkedLibSingleton.RunningOnUnix) if (OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows)
{
//TODO
}
else
{ {
GamePad.UpdateAll(); GamePad.UpdateAll();
GamePad360.UpdateAll(); GamePad360.UpdateAll();
} }
else
{
//TODO
}
//this block is going to massively modify data structures that the binding method uses, so we have to lock it all //this block is going to massively modify data structures that the binding method uses, so we have to lock it all
lock (this) lock (this)

View File

@ -25,6 +25,7 @@ using BizHawk.Client.EmuHawk.WinFormExtensions;
using BizHawk.Client.EmuHawk.ToolExtensions; using BizHawk.Client.EmuHawk.ToolExtensions;
using BizHawk.Emulation.Cores.Computers.AppleII; using BizHawk.Emulation.Cores.Computers.AppleII;
using BizHawk.Client.ApiHawk; using BizHawk.Client.ApiHawk;
using BizHawk.Common;
using BizHawk.Emulation.Cores.Computers.Commodore64; using BizHawk.Emulation.Cores.Computers.Commodore64;
using BizHawk.Emulation.Cores.Nintendo.Gameboy; using BizHawk.Emulation.Cores.Nintendo.Gameboy;
using BizHawk.Emulation.Cores.Computers.SinclairSpectrum; using BizHawk.Emulation.Cores.Computers.SinclairSpectrum;
@ -1469,7 +1470,14 @@ namespace BizHawk.Client.EmuHawk
private void RamSearchMenuItem_Click(object sender, EventArgs e) private void RamSearchMenuItem_Click(object sender, EventArgs e)
{ {
GlobalWin.Tools.Load<RamSearch>(); var ramSearch = GlobalWin.Tools.Load<RamSearch>();
if (OSTailoredCode.CurrentOS != OSTailoredCode.DistinctOS.Windows)
{
// this is apparently needed for weird mono-forms-on-different-thread issues
// dont do .Show() within Load<T>() for RamSearch - instead put an instance of it here on MainForm, then show here
// the mono winforms implementation is.... weird and buggy
ramSearch.Show();
}
} }
private void LuaConsoleMenuItem_Click(object sender, EventArgs e) private void LuaConsoleMenuItem_Click(object sender, EventArgs e)

View File

@ -161,7 +161,9 @@ namespace BizHawk.Client.EmuHawk
Database.LoadDatabase(Path.Combine(PathManager.GetExeDirectoryAbsolute(), "gamedb", "gamedb.txt")); Database.LoadDatabase(Path.Combine(PathManager.GetExeDirectoryAbsolute(), "gamedb", "gamedb.txt"));
// TODO GL - a lot of disorganized wiring-up here // TODO GL - a lot of disorganized wiring-up here
CGC.CGCBinPath = Path.Combine(PathManager.GetDllDirectory(), "cgc.exe"); CGC.CGCBinPath = OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows
? Path.Combine(PathManager.GetDllDirectory(), "cgc.exe")
: "cgc"; // installed separately (via package manager or from https://developer.nvidia.com/cg-toolkit-download), look in $PATH
PresentationPanel = new PresentationPanel(); PresentationPanel = new PresentationPanel();
PresentationPanel.GraphicsControl.MainWindow = true; PresentationPanel.GraphicsControl.MainWindow = true;
GlobalWin.DisplayManager = new DisplayManager(PresentationPanel); GlobalWin.DisplayManager = new DisplayManager(PresentationPanel);
@ -1057,7 +1059,7 @@ namespace BizHawk.Client.EmuHawk
if (!_inFullscreen) if (!_inFullscreen)
{ {
SuspendLayout(); SuspendLayout();
#if WINDOWS
// Work around an AMD driver bug in >= vista: // Work around an AMD driver bug in >= vista:
// It seems windows will activate opengl fullscreen mode when a GL control is occupying the exact space of a screen (0,0 and dimensions=screensize) // It seems windows will activate opengl fullscreen mode when a GL control is occupying the exact space of a screen (0,0 and dimensions=screensize)
// AMD cards manifest a problem under these circumstances, flickering other monitors. // AMD cards manifest a problem under these circumstances, flickering other monitors.
@ -1065,7 +1067,9 @@ namespace BizHawk.Client.EmuHawk
// (this could be determined with more work; other side affects of the fullscreen mode include: corrupted taskbar, no modal boxes on top of GL control, no screenshots) // (this could be determined with more work; other side affects of the fullscreen mode include: corrupted taskbar, no modal boxes on top of GL control, no screenshots)
// At any rate, we can solve this by adding a 1px black border around the GL control // At any rate, we can solve this by adding a 1px black border around the GL control
// Please note: It is important to do this before resizing things, otherwise momentarily a GL control without WS_BORDER will be at the magic dimensions and cause the flakeout // Please note: It is important to do this before resizing things, otherwise momentarily a GL control without WS_BORDER will be at the magic dimensions and cause the flakeout
if (Global.Config.DispFullscreenHacks && Global.Config.DispMethod == Config.EDispMethod.OpenGL) if (OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows
&& Global.Config.DispFullscreenHacks
&& Global.Config.DispMethod == Config.EDispMethod.OpenGL)
{ {
//ATTENTION: this causes the statusbar to not work well, since the backcolor is now set to black instead of SystemColors.Control. //ATTENTION: this causes the statusbar to not work well, since the backcolor is now set to black instead of SystemColors.Control.
//It seems that some statusbar elements composite with the backcolor. //It seems that some statusbar elements composite with the backcolor.
@ -1076,7 +1080,6 @@ namespace BizHawk.Client.EmuHawk
// FUTURE WORK: // FUTURE WORK:
// re-add this padding back into the display manager (so the image will get cut off a little but, but a few more resolutions will fully fit into the screen) // re-add this padding back into the display manager (so the image will get cut off a little but, but a few more resolutions will fully fit into the screen)
} }
#endif
_windowedLocation = Location; _windowedLocation = Location;
@ -1093,13 +1096,15 @@ namespace BizHawk.Client.EmuHawk
WindowState = FormWindowState.Normal; WindowState = FormWindowState.Normal;
#if WINDOWS if (OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows)
// do this even if DispFullscreenHacks arent enabled, to restore it in case it changed underneath us or something {
Padding = new Padding(0); // do this even if DispFullscreenHacks arent enabled, to restore it in case it changed underneath us or something
// it's important that we set the form color back to this, because the statusbar icons blend onto the mainform, not onto the statusbar-- Padding = new Padding(0);
// so we need the statusbar and mainform backdrop color to match
BackColor = SystemColors.Control; // it's important that we set the form color back to this, because the statusbar icons blend onto the mainform, not onto the statusbar--
#endif // so we need the statusbar and mainform backdrop color to match
BackColor = SystemColors.Control;
}
_inFullscreen = false; _inFullscreen = false;
@ -2071,7 +2076,22 @@ namespace BizHawk.Client.EmuHawk
// sends an alt+mnemonic combination // sends an alt+mnemonic combination
private void SendAltKeyChar(char c) private void SendAltKeyChar(char c)
{ {
typeof(ToolStrip).InvokeMember("ProcessMnemonicInternal", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.Instance, null, MainformMenu, new object[] { c }); switch (OSTailoredCode.CurrentOS)
{
case OSTailoredCode.DistinctOS.Linux:
case OSTailoredCode.DistinctOS.macOS:
// no mnemonics for you
break;
case OSTailoredCode.DistinctOS.Windows:
//HACK
var _ = typeof(ToolStrip).InvokeMember(
"ProcessMnemonicInternal",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.Instance,
null,
MainformMenu,
new object[] { c });
break;
}
} }
public static string FormatFilter(params string[] args) public static string FormatFilter(params string[] args)

View File

@ -6,9 +6,6 @@ using sysdrawing2d=System.Drawing.Drawing2D;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Windows.Forms; using System.Windows.Forms;
#if WINDOWS
using SlimDX;
#endif
using BizHawk.Client.Common; using BizHawk.Client.Common;
using BizHawk.Bizware.BizwareGL; using BizHawk.Bizware.BizwareGL;

View File

@ -1,21 +1,19 @@
using System; using System;
using System.Linq;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Windows.Forms; using System.Windows.Forms;
#if WINDOWS
using Microsoft.VisualBasic.ApplicationServices; using Microsoft.VisualBasic.ApplicationServices;
#endif
using BizHawk.Common; using BizHawk.Common;
using BizHawk.Client.Common; using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk namespace BizHawk.Client.EmuHawk
{ {
static class Program internal static class Program
{ {
static Program() static Program()
{ {
@ -23,56 +21,52 @@ namespace BizHawk.Client.EmuHawk
Application.EnableVisualStyles(); Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false); Application.SetCompatibleTextRenderingDefault(false);
var libLoader = EXE_PROJECT.PlatformLinkedLibSingleton.LinkedLibManager; if (EXE_PROJECT.OSTailoredCode.CurrentOS == EXE_PROJECT.OSTailoredCode.DistinctOS.Windows)
//http://www.codeproject.com/Articles/310675/AppDomain-AssemblyResolve-Event-Tips
//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 libExt = EXE_PROJECT.PlatformLinkedLibSingleton.RunningOnUnix ? ".dll.so" : ".dll";
var d3dx9 = libLoader.LoadPlatformSpecific($"d3dx9_43{libExt}");
var vc2015 = libLoader.LoadPlatformSpecific($"vcruntime140{libExt}");
var vc2012 = libLoader.LoadPlatformSpecific($"msvcr120{libExt}"); //TODO - check version?
var vc2010 = libLoader.LoadPlatformSpecific($"msvcr100{libExt}"); //TODO - check version?
var vc2010p = libLoader.LoadPlatformSpecific($"msvcp100{libExt}");
bool fail = false, warn = false;
warn |= d3dx9 == IntPtr.Zero;
fail |= vc2015 == IntPtr.Zero;
fail |= vc2010 == IntPtr.Zero;
fail |= vc2012 == IntPtr.Zero;
fail |= vc2010p == IntPtr.Zero;
if (fail || warn)
{ {
var sw = new System.IO.StringWriter(); var libLoader = EXE_PROJECT.OSTailoredCode.LinkedLibManager;
sw.WriteLine("[ OK ] .Net 4.6.1 (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++ 2012 Runtime", (vc2012 == IntPtr.Zero) ? "FAIL" : " OK ");
sw.WriteLine("[{0}] Visual C++ 2015 Runtime", (vc2015 == IntPtr.Zero) ? "FAIL" : " OK ");
var str = sw.ToString();
var box = new BizHawk.Client.EmuHawk.CustomControls.PrereqsAlert(!fail);
box.textBox1.Text = str;
box.ShowDialog();
if (!fail) { }
else
System.Diagnostics.Process.GetCurrentProcess().Kill();
}
libLoader.FreePlatformSpecific(d3dx9); //http://www.codeproject.com/Articles/310675/AppDomain-AssemblyResolve-Event-Tips
libLoader.FreePlatformSpecific(vc2015);
libLoader.FreePlatformSpecific(vc2012); //try loading libraries we know we'll need
libLoader.FreePlatformSpecific(vc2010); //something in the winforms, etc. code below will cause .net to popup a missing msvcr100.dll in case that one's missing
libLoader.FreePlatformSpecific(vc2010p); //but oddly it lets us proceed and we'll then catch it here
var d3dx9 = libLoader.LoadPlatformSpecific("d3dx9_43.dll");
var vc2015 = libLoader.LoadPlatformSpecific("vcruntime140.dll");
var vc2012 = libLoader.LoadPlatformSpecific("msvcr120.dll"); //TODO - check version?
var vc2010 = libLoader.LoadPlatformSpecific("msvcr100.dll"); //TODO - check version?
var vc2010p = libLoader.LoadPlatformSpecific("msvcp100.dll");
var fail = vc2015 == IntPtr.Zero || vc2010 == IntPtr.Zero || vc2012 == IntPtr.Zero || vc2010p == IntPtr.Zero;
var warn = d3dx9 == IntPtr.Zero;
if (fail || warn)
{
var alertLines = new[]
{
"[ OK ] .NET CLR (You wouldn't even get here without it)",
$"[{(d3dx9 == IntPtr.Zero ? "WARN" : " OK ")}] Direct3d 9",
$"[{(vc2010 == IntPtr.Zero || vc2010p == IntPtr.Zero ? "FAIL" : " OK ")}] Visual C++ 2010 SP1 Runtime",
$"[{(vc2012 == IntPtr.Zero ? "FAIL" : " OK ")}] Visual C++ 2012 Runtime",
$"[{(vc2015 == IntPtr.Zero ? "FAIL" : " OK ")}] Visual C++ 2015 Runtime"
};
var box = new BizHawk.Client.EmuHawk.CustomControls.PrereqsAlert(!fail)
{
textBox1 = { Text = string.Concat("\n", alertLines) }
};
box.ShowDialog();
if (fail) System.Diagnostics.Process.GetCurrentProcess().Kill();
}
libLoader.FreePlatformSpecific(d3dx9);
libLoader.FreePlatformSpecific(vc2015);
libLoader.FreePlatformSpecific(vc2012);
libLoader.FreePlatformSpecific(vc2010);
libLoader.FreePlatformSpecific(vc2010p);
if (!EXE_PROJECT.PlatformLinkedLibSingleton.RunningOnUnix)
{
// this will look in subdirectory "dll" to load pinvoked stuff // this will look in subdirectory "dll" to load pinvoked stuff
string dllDir = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "dll"); var dllDir = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "dll");
SetDllDirectory(dllDir); SetDllDirectory(dllDir);
//in case assembly resolution fails, such as if we moved them into the dll subdiretory, this event handler can reroute to them //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); AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
//but before we even try doing that, whack the MOTW from everything in that directory (thats a dll) //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. //otherwise, some people will have crashes at boot-up due to .net security disliking MOTW.
@ -84,129 +78,20 @@ namespace BizHawk.Client.EmuHawk
} }
[STAThread] [STAThread]
static int Main(string[] args) private static int Main(string[] args)
{ {
return SubMain(args); var exitCode = SubMain(args);
} if (EXE_PROJECT.OSTailoredCode.CurrentOS == EXE_PROJECT.OSTailoredCode.DistinctOS.Linux)
private interface PlatformSpecificMainLoopCrashHandler
{
void TryCatchFinally(string[] args);
}
private class Win32MainLoopCrashHandler : PlatformSpecificMainLoopCrashHandler
{
public void TryCatchFinally(string[] args)
{ {
try Console.WriteLine("BizHawk has completed its shutdown routines, killing process...");
{ Process.GetCurrentProcess().Kill();
if (Global.Config.SingleInstanceMode)
{
try
{
new SingleInstanceController(args).Run(args);
}
catch (ObjectDisposedException)
{
// 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
}
}
else
{
using (var mf = new MainForm(args))
{
var title = mf.Text;
mf.Show();
mf.Text = title;
try
{
GlobalWin.ExitCode = mf.ProgramRunLoop();
}
catch (Exception e) when (!Debugger.IsAttached && !VersionInfo.DeveloperBuild && Global.MovieSession.Movie.IsActive)
{
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)
{
Global.MovieSession.Movie.Save();
}
}
}
}
}
catch (Exception e) when (!Debugger.IsAttached)
{
new ExceptionBox(e).ShowDialog();
}
finally
{
if (GlobalWin.Sound != null)
{
GlobalWin.Sound.Dispose();
GlobalWin.Sound = null;
}
GlobalWin.GL.Dispose();
Input.Cleanup();
}
} }
return exitCode;
} }
private class UnixMonoMainLoopCrashHandler : PlatformSpecificMainLoopCrashHandler
{
// Identical to the implementation in Win32MainLoopCrashHandler sans the single-instance check.
public void TryCatchFinally(string[] args)
{
try
{
using (var mf = new MainForm(args))
{
var title = mf.Text;
mf.Show();
mf.Text = title;
try
{
GlobalWin.ExitCode = mf.ProgramRunLoop();
}
catch (Exception e) when (!Debugger.IsAttached && !VersionInfo.DeveloperBuild && Global.MovieSession.Movie.IsActive)
{
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)
{
Global.MovieSession.Movie.Save();
}
}
}
}
catch (Exception e) when (!Debugger.IsAttached)
{
new ExceptionBox(e).ShowDialog();
}
finally
{
if (GlobalWin.Sound != null)
{
GlobalWin.Sound.Dispose();
GlobalWin.Sound = null;
}
GlobalWin.GL.Dispose();
Input.Cleanup();
}
}
}
private static PlatformSpecificMainLoopCrashHandler mainLoopCrashHandler = EXE_PROJECT.PlatformLinkedLibSingleton.RunningOnUnix
? (PlatformSpecificMainLoopCrashHandler) new UnixMonoMainLoopCrashHandler()
: (PlatformSpecificMainLoopCrashHandler) new Win32MainLoopCrashHandler();
//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 //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)] [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
static int SubMain(string[] args) private static int SubMain(string[] args)
{ {
// this check has to be done VERY early. i stepped through a debug build with wrong .dll versions purposely used, // 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?) // and there was a TypeLoadException before the first line of SubMain was reached (some static ColorType init?)
@ -228,7 +113,7 @@ namespace BizHawk.Client.EmuHawk
HawkFile.ArchiveHandlerFactory = new SevenZipSharpArchiveHandler(); HawkFile.ArchiveHandlerFactory = new SevenZipSharpArchiveHandler();
ArgParser argParser = new ArgParser(); var argParser = new ArgParser();
argParser.ParseArguments(args); argParser.ParseArguments(args);
if (argParser.cmdConfigFile != null) PathManager.SetDefaultIniPath(argParser.cmdConfigFile); if (argParser.cmdConfigFile != null) PathManager.SetDefaultIniPath(argParser.cmdConfigFile);
@ -248,13 +133,9 @@ namespace BizHawk.Client.EmuHawk
BizHawk.Client.Common.StringLogUtil.DefaultToAWE = Global.Config.MoviesInAWE; BizHawk.Client.Common.StringLogUtil.DefaultToAWE = Global.Config.MoviesInAWE;
// super hacky! this needs to be done first. still not worth the trouble to make this system fully proper // super hacky! this needs to be done first. still not worth the trouble to make this system fully proper
for (int i = 0; i < args.Length; i++) if (Array.Exists(args, arg => arg.StartsWith("--gdi", StringComparison.InvariantCultureIgnoreCase)))
{ {
var arg = args[i].ToLower(); Global.Config.DispMethod = Config.EDispMethod.GdiPlus;
if (arg.StartsWith("--gdi"))
{
Global.Config.DispMethod = Config.EDispMethod.GdiPlus;
}
} }
// create IGL context. we do this whether or not the user has selected OpenGL, so that we can run opengl-based emulator cores // create IGL context. we do this whether or not the user has selected OpenGL, so that we can run opengl-based emulator cores
@ -265,8 +146,10 @@ namespace BizHawk.Client.EmuHawk
GlobalWin.GLManager = GLManager.Instance; GlobalWin.GLManager = GLManager.Instance;
//now create the "GL" context for the display method. we can reuse the IGL_TK context if opengl display method is chosen //now create the "GL" context for the display method. we can reuse the IGL_TK context if opengl display method is chosen
if (EXE_PROJECT.PlatformLinkedLibSingleton.RunningOnUnix) Global.Config.DispMethod = Config.EDispMethod.GdiPlus; if (EXE_PROJECT.OSTailoredCode.CurrentOS != EXE_PROJECT.OSTailoredCode.DistinctOS.Windows)
REDO_DISPMETHOD: Global.Config.DispMethod = Config.EDispMethod.GdiPlus;
REDO_DISPMETHOD:
if (Global.Config.DispMethod == Config.EDispMethod.GdiPlus) if (Global.Config.DispMethod == Config.EDispMethod.GdiPlus)
GlobalWin.GL = new Bizware.BizwareGL.Drivers.GdiPlus.IGL_GdiPlus(); GlobalWin.GL = new Bizware.BizwareGL.Drivers.GdiPlus.IGL_GdiPlus();
else if (Global.Config.DispMethod == Config.EDispMethod.SlimDX9) else if (Global.Config.DispMethod == Config.EDispMethod.SlimDX9)
@ -277,8 +160,7 @@ namespace BizHawk.Client.EmuHawk
} }
catch(Exception ex) catch(Exception ex)
{ {
var e2 = new Exception("Initialization of Direct3d 9 Display Method failed; falling back to GDI+", ex); new ExceptionBox(new Exception("Initialization of Direct3d 9 Display Method failed; falling back to GDI+", ex)).ShowDialog();
new ExceptionBox(e2).ShowDialog();
// fallback // fallback
Global.Config.DispMethod = Config.EDispMethod.GdiPlus; Global.Config.DispMethod = Config.EDispMethod.GdiPlus;
@ -290,8 +172,7 @@ namespace BizHawk.Client.EmuHawk
GlobalWin.GL = GlobalWin.IGL_GL; GlobalWin.GL = GlobalWin.IGL_GL;
// check the opengl version and dont even try to boot this crap up if its too old // check the opengl version and dont even try to boot this crap up if its too old
int version = GlobalWin.IGL_GL.Version; if (GlobalWin.IGL_GL.Version < 200)
if (version < 200)
{ {
// fallback // fallback
Global.Config.DispMethod = Config.EDispMethod.GdiPlus; Global.Config.DispMethod = Config.EDispMethod.GdiPlus;
@ -306,26 +187,75 @@ namespace BizHawk.Client.EmuHawk
} }
catch(Exception ex) catch(Exception ex)
{ {
var e2 = new Exception("Initialization of Display Method failed; falling back to GDI+", ex); new ExceptionBox(new Exception("Initialization of Display Method failed; falling back to GDI+", ex)).ShowDialog();
new ExceptionBox(e2).ShowDialog();
//fallback //fallback
Global.Config.DispMethod = Config.EDispMethod.GdiPlus; Global.Config.DispMethod = Config.EDispMethod.GdiPlus;
goto REDO_DISPMETHOD; goto REDO_DISPMETHOD;
} }
if (!EXE_PROJECT.PlatformLinkedLibSingleton.RunningOnUnix) if (EXE_PROJECT.OSTailoredCode.CurrentOS == EXE_PROJECT.OSTailoredCode.DistinctOS.Windows)
{ {
//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. //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". //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. //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 //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) //pasting should be safe (not affecting the jit order of things)
string dllDir = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "dll"); var dllDir = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "dll");
SetDllDirectory(dllDir); SetDllDirectory(dllDir);
} }
// Using a simple conditional to skip the single-instancing step caused crashes on GNU+Linux, even though the single-instancing step wasn't being executed. Something about the way instantiation works in C# means this workaround is possible. try
mainLoopCrashHandler.TryCatchFinally(args); {
if (Global.Config.SingleInstanceMode)
{
try
{
new SingleInstanceController(args).Run();
}
catch (ObjectDisposedException)
{
// 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
}
}
else
{
using (var mf = new MainForm(args))
{
var title = mf.Text;
mf.Show();
mf.Text = title;
try
{
GlobalWin.ExitCode = mf.ProgramRunLoop();
}
catch (Exception e) when (Global.MovieSession.Movie.IsActive && !(Debugger.IsAttached || VersionInfo.DeveloperBuild))
{
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)
{
Global.MovieSession.Movie.Save();
}
}
}
}
}
catch (Exception e) when (!Debugger.IsAttached)
{
new ExceptionBox(e).ShowDialog();
}
finally
{
GlobalWin.Sound?.Dispose();
GlobalWin.Sound = null;
GlobalWin.GL.Dispose();
Input.Cleanup();
}
//cleanup: //cleanup:
//cleanup IGL stuff so we can get better refcounts when exiting process, for debugging //cleanup IGL stuff so we can get better refcounts when exiting process, for debugging
@ -341,19 +271,19 @@ namespace BizHawk.Client.EmuHawk
} //SubMain } //SubMain
//declared here instead of a more usual place to avoid dependencies on the more usual place //declared here instead of a more usual place to avoid dependencies on the more usual place
#if WINDOWS
[DllImport("kernel32.dll", SetLastError = true)] [DllImport("kernel32.dll", SetLastError = true)]
static extern uint SetDllDirectory(string lpPathName); private static extern uint SetDllDirectory(string lpPathName);
[DllImport("kernel32.dll", EntryPoint = "DeleteFileW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)] [DllImport("kernel32.dll", EntryPoint = "DeleteFileW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)]
static extern bool DeleteFileW([MarshalAs(UnmanagedType.LPWStr)]string lpFileName); private static extern bool DeleteFileW([MarshalAs(UnmanagedType.LPWStr)]string lpFileName);
public static void RemoveMOTW(string path) private static void RemoveMOTW(string path)
{ {
DeleteFileW($"{path}:Zone.Identifier"); DeleteFileW($"{path}:Zone.Identifier");
} }
static void WhackAllMOTW(string dllDir) private static void WhackAllMOTW(string dllDir)
{ {
var todo = new Queue<DirectoryInfo>(new[] { new DirectoryInfo(dllDir) }); var todo = new Queue<DirectoryInfo>(new[] { new DirectoryInfo(dllDir) });
while (todo.Count > 0) while (todo.Count > 0)
@ -365,14 +295,11 @@ namespace BizHawk.Client.EmuHawk
foreach (var fi in di.GetFiles("*.exe")) foreach (var fi in di.GetFiles("*.exe"))
RemoveMOTW(fi.FullName); RemoveMOTW(fi.FullName);
} }
} }
#endif
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{ {
string requested = args.Name; var requested = args.Name;
//mutate filename depending on selection of lua core. here's how it works //mutate filename depending on selection of lua core. here's how it works
//1. we build NLua to the output/dll/lua directory. that brings KopiLua with it //1. we build NLua to the output/dll/lua directory. that brings KopiLua with it
@ -380,52 +307,43 @@ namespace BizHawk.Client.EmuHawk
//3. When NLua assembly attempts to load, it can't find it //3. When NLua assembly attempts to load, it can't find it
//I. if LuaInterface is selected by the user, we switch to requesting that. //I. if LuaInterface is selected by the user, we switch to requesting that.
// (those DLLs are built into the output/DLL directory) // (those DLLs are built into the output/DLL directory)
//II. if NLua is selected by the user, we skip over this part; //II. if NLua is selected by the user, we skip over this part;
// later, we look for NLua or KopiLua assembly names and redirect them to files located in the output/DLL/nlua directory // later, we look for NLua or KopiLua assembly names and redirect them to files located in the output/DLL/nlua directory
if (new AssemblyName(requested).Name == "NLua") if (new AssemblyName(requested).Name == "NLua")
{ {
//this method referencing Global.Config makes assemblies get loaded, which isnt smart from the assembly resolver. //this method referencing Global.Config makes assemblies get loaded, which isnt smart from the assembly resolver.
//so.. we're going to resort to something really bad. //so.. we're going to resort to something really bad.
//avert your eyes. //avert your eyes.
bool UseNLua = true; var configPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "config.ini");
string configPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "config.ini"); if (EXE_PROJECT.OSTailoredCode.CurrentOS == EXE_PROJECT.OSTailoredCode.DistinctOS.Windows // LuaInterface is not currently working on Mono
if (File.Exists(configPath)) && File.Exists(configPath)
&& (Array.Find(File.ReadAllLines(configPath), line => line.Contains(" \"UseNLua\": ")) ?? string.Empty)
.Contains("false"))
{ {
var cfg = File.ReadAllLines(configPath); requested = "LuaInterface";
var usenlua_key = cfg.FirstOrDefault(line=>line.Contains(" \"UseNLua\": "));
if (usenlua_key != null)
if (usenlua_key.Contains("false"))
UseNLua = false;
} }
if (UseNLua) { }
else requested = "LuaInterface";
} }
lock (AppDomain.CurrentDomain) lock (AppDomain.CurrentDomain)
{ {
var asms = AppDomain.CurrentDomain.GetAssemblies(); var firstAsm = Array.Find(AppDomain.CurrentDomain.GetAssemblies(), asm => asm.FullName == requested);
foreach (var asm in asms) if (firstAsm != null) return firstAsm;
if (asm.FullName == requested)
return asm;
//load missing assemblies by trying to find them in the dll directory //load missing assemblies by trying to find them in the dll directory
string dllname = $"{new AssemblyName(requested).Name}.dll"; var dllname = $"{new AssemblyName(requested).Name}.dll";
string directory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "dll"); var directory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "dll");
string simpleName = new AssemblyName(requested).Name; var simpleName = new AssemblyName(requested).Name;
if (simpleName == "NLua" || simpleName == "KopiLua") directory = Path.Combine(directory, "nlua"); if (simpleName == "NLua" || simpleName == "KopiLua") directory = Path.Combine(directory, "nlua");
string fname = Path.Combine(directory, dllname); var 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/unmanaged) assemblies can't load
return File.Exists(fname) ? Assembly.LoadFile(fname) : 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);
} }
} }
#if WINDOWS private class SingleInstanceController : WindowsFormsApplicationBase
public class SingleInstanceController : WindowsFormsApplicationBase
{ {
readonly string[] cmdArgs; private readonly string[] cmdArgs;
public SingleInstanceController(string[] args) public SingleInstanceController(string[] args)
{ {
cmdArgs = args; cmdArgs = args;
@ -433,10 +351,12 @@ namespace BizHawk.Client.EmuHawk
StartupNextInstance += this_StartupNextInstance; StartupNextInstance += this_StartupNextInstance;
} }
void this_StartupNextInstance(object sender, StartupNextInstanceEventArgs e) public void Run() => Run(cmdArgs);
private void this_StartupNextInstance(object sender, StartupNextInstanceEventArgs e)
{ {
if (e.CommandLine.Count >= 1) if (e.CommandLine.Count >= 1)
(MainForm as MainForm).LoadRom(e.CommandLine[0], new MainForm.LoadRomArgs() { OpenAdvanced = new OpenAdvanced_OpenRom() }); ((MainForm)MainForm).LoadRom(e.CommandLine[0], new MainForm.LoadRomArgs { OpenAdvanced = new OpenAdvanced_OpenRom() });
} }
protected override void OnCreateMainForm() protected override void OnCreateMainForm()
@ -445,11 +365,8 @@ namespace BizHawk.Client.EmuHawk
var title = MainForm.Text; var title = MainForm.Text;
MainForm.Show(); MainForm.Show();
MainForm.Text = title; MainForm.Text = title;
GlobalWin.ExitCode = (MainForm as MainForm).ProgramRunLoop(); GlobalWin.ExitCode = ((MainForm)MainForm).ProgramRunLoop();
} }
} }
#endif
} }
} }

View File

@ -1,80 +1,66 @@
using System; using System.Runtime.InteropServices;
using System.Runtime.InteropServices;
using BizHawk.Client.Common;
using BizHawk.Common; using BizHawk.Common;
namespace BizHawk.Client.EmuHawk namespace BizHawk.Client.EmuHawk
{ {
// Derived from http://www.codeproject.com/KB/cs/ScreenSaverControl.aspx /// <remarks>Derived from http://www.codeproject.com/KB/cs/ScreenSaverControl.aspx</remarks>
public static class ScreenSaver public static class ScreenSaver
{ {
private interface PlatformSpecificScreenBlankInterface private interface IScreenBlankTimer
{ {
Int32 Get(); /// <summary>
void Set(Int32 v); /// The screen saver timeout setting, in seconds
/// </summary>
int Duration { get; set; }
} }
private class WinScreenBlankInterface : PlatformSpecificScreenBlankInterface
private class Win32ScreenBlankTimer : IScreenBlankTimer
{ {
[DllImport("user32.dll", CharSet = CharSet.Auto)] [DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool SystemParametersInfo(int uAction, int uParam, ref int lpvParam, int flags); private static extern bool SystemParametersInfo(int uAction, int uParam, ref int lpvParam, int flags);
public Int32 Get()
{
Int32 value = 0;
SystemParametersInfo(SPI_GETSCREENSAVERTIMEOUT, 0, ref value, 0);
return value;
}
public void Set(Int32 v)
{
int nullVar = 0;
SystemParametersInfo(SPI_SETSCREENSAVERTIMEOUT, v, ref nullVar, SPIF_SENDWININICHANGE);
}
}
private class MiscUnixScreenBlankInterface : PlatformSpecificScreenBlankInterface
{
public Int32 Get()
{
return 0; //TODO implement
}
public void Set(Int32 v)
{
//TODO implement
}
}
private static PlatformSpecificScreenBlankInterface screenBlankInterface = PlatformLinkedLibSingleton.RunningOnUnix
? (PlatformSpecificScreenBlankInterface) new MiscUnixScreenBlankInterface()
: (PlatformSpecificScreenBlankInterface) new WinScreenBlankInterface();
private const int SPI_GETSCREENSAVERTIMEOUT = 14; private const int SPI_GETSCREENSAVERTIMEOUT = 14;
private const int SPI_SETSCREENSAVERTIMEOUT = 15; private const int SPI_SETSCREENSAVERTIMEOUT = 15;
private const int SPIF_SENDWININICHANGE = 2; private const int SPIF_SENDWININICHANGE = 2;
public int Duration
{
get
{
var value = 0;
SystemParametersInfo(SPI_GETSCREENSAVERTIMEOUT, 0, ref value, 0);
return value;
}
set
{
var nullVar = 0;
SystemParametersInfo(SPI_SETSCREENSAVERTIMEOUT, value, ref nullVar, SPIF_SENDWININICHANGE);
}
}
}
private class UnixScreenBlankTimer : IScreenBlankTimer
{
public int Duration { get; set; } = 0; //TODO implementation
}
private static readonly IScreenBlankTimer _screenBlankTimer = OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows
? (IScreenBlankTimer) new Win32ScreenBlankTimer()
: new UnixScreenBlankTimer();
private static int ctr;
public static void ResetTimerImmediate() public static void ResetTimerImmediate()
{ {
SetScreenSaverTimeout(GetScreenSaverTimeout()); _screenBlankTimer.Duration = _screenBlankTimer.Duration;
} }
private static int ctr;
public static void ResetTimerPeriodically() public static void ResetTimerPeriodically()
{ {
ctr++; if (++ctr < 120) return;
if (ctr == 120) ctr = 0;
{ ResetTimerImmediate();
SetScreenSaverTimeout(GetScreenSaverTimeout());
ctr = 0;
}
}
// Returns the screen saver timeout setting, in seconds
private static Int32 GetScreenSaverTimeout()
{
return screenBlankInterface.Get();
}
// Pass in the number of seconds to set the screen saver timeout value.
private static void SetScreenSaverTimeout(Int32 Value)
{
screenBlankInterface.Set(Value);
} }
} }
} }

View File

@ -1,5 +1,4 @@
#if WINDOWS using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
@ -164,4 +163,3 @@ namespace BizHawk.Client.EmuHawk
} }
} }
} }
#endif

View File

@ -1,5 +1,4 @@
#if WINDOWS using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -185,4 +184,3 @@ namespace BizHawk.Client.EmuHawk
} }
} }
} }
#endif

View File

@ -25,15 +25,17 @@ namespace BizHawk.Client.EmuHawk
public Sound(IntPtr mainWindowHandle) public Sound(IntPtr mainWindowHandle)
{ {
if (Global.Config.SoundOutputMethod == Config.ESoundOutputMethod.OpenAL) if (OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows)
_outputDevice = new OpenALSoundOutput(this);
if (!PlatformLinkedLibSingleton.RunningOnUnix)
{ {
if (Global.Config.SoundOutputMethod == Config.ESoundOutputMethod.OpenAL)
_outputDevice = new OpenALSoundOutput(this);
if (Global.Config.SoundOutputMethod == Config.ESoundOutputMethod.DirectSound) if (Global.Config.SoundOutputMethod == Config.ESoundOutputMethod.DirectSound)
_outputDevice = new DirectSoundSoundOutput(this, mainWindowHandle); _outputDevice = new DirectSoundSoundOutput(this, mainWindowHandle);
if (Global.Config.SoundOutputMethod == Config.ESoundOutputMethod.XAudio2) if (Global.Config.SoundOutputMethod == Config.ESoundOutputMethod.XAudio2)
_outputDevice = new XAudio2SoundOutput(this); _outputDevice = new XAudio2SoundOutput(this);
} }
else _outputDevice = new OpenALSoundOutput(this); // at the moment unix/mono can only support OpenAL (so ignore whatever is set in the config)
if (_outputDevice == null) if (_outputDevice == null)
_outputDevice = new DummySoundOutput(this); _outputDevice = new DummySoundOutput(this);
} }

View File

@ -153,14 +153,16 @@ namespace BizHawk.Client.EmuHawk
} }
private class UnixMonoSysTimer : PlatformSpecificSysTimer private class UnixMonoSysTimer : PlatformSpecificSysTimer
{ {
[DllImport("winmm.dll.so", EntryPoint = "timeBeginPeriod")]
private static extern uint timeBeginPeriod(uint uMilliseconds);
public uint TimeBeginPeriod(uint ms) public uint TimeBeginPeriod(uint ms)
{ {
return timeBeginPeriod(ms); // we are not going to bother trying to set a minimum resolution for periodic timers
// (on linux I don't think you can set this in user code)
return ms;
} }
} }
static PlatformSpecificSysTimer sysTimer = PlatformLinkedLibSingleton.RunningOnUnix ? (PlatformSpecificSysTimer) new UnixMonoSysTimer() : (PlatformSpecificSysTimer) new WinSysTimer(); static PlatformSpecificSysTimer sysTimer = OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows
? (PlatformSpecificSysTimer) new WinSysTimer()
: new UnixMonoSysTimer();
static uint TimeBeginPeriod(uint ms) static uint TimeBeginPeriod(uint ms)
{ {
return sysTimer.TimeBeginPeriod(ms); return sysTimer.TimeBeginPeriod(ms);
@ -362,18 +364,23 @@ namespace BizHawk.Client.EmuHawk
int sleepTime = (int)((timePerFrame - elapsedTime) * 1000 / afsfreq); int sleepTime = (int)((timePerFrame - elapsedTime) * 1000 / afsfreq);
if (sleepTime >= 2 || paused) if (sleepTime >= 2 || paused)
{ {
#if WINDOWS switch (OSTailoredCode.CurrentOS)
// Assuming a timer period of 1 ms (i.e. TimeBeginPeriod(1)): The actual sleep time {
// on Windows XP is generally within a half millisecond either way of the requested case OSTailoredCode.DistinctOS.Linux: //TODO repro
// time. The actual sleep time on Windows 8 is generally between the requested time case OSTailoredCode.DistinctOS.macOS:
// and up to a millisecond over. So we'll subtract 1 ms from the time to avoid // The actual sleep time on OS X with Mono is generally between the request time
// sleeping longer than desired. // and up to 25% over. So we'll scale the sleep time back to account for that.
sleepTime -= 1; sleepTime = sleepTime * 4 / 5;
#else break;
// The actual sleep time on OS X with Mono is generally between the request time case OSTailoredCode.DistinctOS.Windows:
// and up to 25% over. So we'll scale the sleep time back to account for that. // Assuming a timer period of 1 ms (i.e. TimeBeginPeriod(1)): The actual sleep time
sleepTime = sleepTime * 4 / 5; // on Windows XP is generally within a half millisecond either way of the requested
#endif // time. The actual sleep time on Windows 8 is generally between the requested time
// and up to a millisecond over. So we'll subtract 1 ms from the time to avoid
// sleeping longer than desired.
sleepTime -= 1;
break;
}
Thread.Sleep(Math.Max(sleepTime, 1)); Thread.Sleep(Math.Max(sleepTime, 1));
} }

View File

@ -3,6 +3,7 @@ using System.IO;
using System.Windows.Forms; using System.Windows.Forms;
using BizHawk.Emulation.Common; using BizHawk.Emulation.Common;
using BizHawk.Client.Common; using BizHawk.Client.Common;
using BizHawk.Common;
namespace BizHawk.Client.EmuHawk namespace BizHawk.Client.EmuHawk
{ {
@ -91,6 +92,16 @@ namespace BizHawk.Client.EmuHawk
txtCropBottom.Text = Global.Config.DispCropBottom.ToString(); txtCropBottom.Text = Global.Config.DispCropBottom.ToString();
RefreshAspectRatioOptions(); RefreshAspectRatioOptions();
if (OSTailoredCode.CurrentOS != OSTailoredCode.DistinctOS.Windows)
{
// Disable SlimDX on Unix
rbD3D9.Enabled = false;
rbD3D9.AutoCheck = false;
cbAlternateVsync.Enabled = false;
label13.Enabled = false;
label8.Enabled = false;
}
} }
private void btnOk_Click(object sender, EventArgs e) private void btnOk_Click(object sender, EventArgs e)

View File

@ -358,6 +358,8 @@ namespace BizHawk.Client.EmuHawk
private void tbbOpenFolder_Click(object sender, EventArgs e) private void tbbOpenFolder_Click(object sender, EventArgs e)
{ {
var frmWares = PathManager.MakeAbsolutePath(Global.Config.PathEntries.FirmwaresPathFragment, null); var frmWares = PathManager.MakeAbsolutePath(Global.Config.PathEntries.FirmwaresPathFragment, null);
if (OSTailoredCode.CurrentOS != OSTailoredCode.DistinctOS.Windows && !Directory.Exists(frmWares))
Directory.CreateDirectory(frmWares);
System.Diagnostics.Process.Start(frmWares); System.Diagnostics.Process.Start(frmWares);
} }

View File

@ -71,7 +71,7 @@ namespace BizHawk.Client.EmuHawk
protected override void OnMouseClick(MouseEventArgs e) protected override void OnMouseClick(MouseEventArgs e)
{ {
if (!PlatformLinkedLibSingleton.RunningOnUnix) HideCaret(Handle); if (OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows) HideCaret(Handle);
base.OnMouseClick(e); base.OnMouseClick(e);
} }
@ -264,7 +264,7 @@ namespace BizHawk.Client.EmuHawk
protected override void OnGotFocus(EventArgs e) protected override void OnGotFocus(EventArgs e)
{ {
if (!PlatformLinkedLibSingleton.RunningOnUnix) HideCaret(Handle); if (OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows) HideCaret(Handle);
} }
protected override bool ProcessCmdKey(ref Message msg, Keys keyData) protected override bool ProcessCmdKey(ref Message msg, Keys keyData)

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Windows.Forms; using System.Windows.Forms;
using BizHawk.Client.Common; using BizHawk.Client.Common;
using BizHawk.Common;
namespace BizHawk.Client.EmuHawk namespace BizHawk.Client.EmuHawk
{ {
@ -24,10 +25,14 @@ namespace BizHawk.Client.EmuHawk
cbEnableNormal.Checked = Global.Config.SoundEnabledNormal; cbEnableNormal.Checked = Global.Config.SoundEnabledNormal;
cbEnableRWFF.Checked = Global.Config.SoundEnabledRWFF; cbEnableRWFF.Checked = Global.Config.SoundEnabledRWFF;
cbMuteFrameAdvance.Checked = Global.Config.MuteFrameAdvance; cbMuteFrameAdvance.Checked = Global.Config.MuteFrameAdvance;
#if !WINDOWS
rbOutputMethodDirectSound.Enabled = false; if (OSTailoredCode.CurrentOS != OSTailoredCode.DistinctOS.Windows)
rbOutputMethodXAudio2.Enabled = false; {
#endif // Disable DirectSound and XAudio2 on Mono
rbOutputMethodDirectSound.Enabled = false;
rbOutputMethodXAudio2.Enabled = false;
}
rbOutputMethodDirectSound.Checked = Global.Config.SoundOutputMethod == Config.ESoundOutputMethod.DirectSound; rbOutputMethodDirectSound.Checked = Global.Config.SoundOutputMethod == Config.ESoundOutputMethod.DirectSound;
rbOutputMethodXAudio2.Checked = Global.Config.SoundOutputMethod == Config.ESoundOutputMethod.XAudio2; rbOutputMethodXAudio2.Checked = Global.Config.SoundOutputMethod == Config.ESoundOutputMethod.XAudio2;
rbOutputMethodOpenAL.Checked = Global.Config.SoundOutputMethod == Config.ESoundOutputMethod.OpenAL; rbOutputMethodOpenAL.Checked = Global.Config.SoundOutputMethod == Config.ESoundOutputMethod.OpenAL;
@ -83,11 +88,13 @@ namespace BizHawk.Client.EmuHawk
private void PopulateDeviceList() private void PopulateDeviceList()
{ {
IEnumerable<string> deviceNames = Enumerable.Empty<string>(); IEnumerable<string> deviceNames = Enumerable.Empty<string>();
#if WINDOWS if (OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows)
if (rbOutputMethodDirectSound.Checked) deviceNames = DirectSoundSoundOutput.GetDeviceNames(); {
if (rbOutputMethodXAudio2.Checked) deviceNames = XAudio2SoundOutput.GetDeviceNames(); if (rbOutputMethodDirectSound.Checked) deviceNames = DirectSoundSoundOutput.GetDeviceNames();
#endif if (rbOutputMethodXAudio2.Checked) deviceNames = XAudio2SoundOutput.GetDeviceNames();
}
if (rbOutputMethodOpenAL.Checked) deviceNames = OpenALSoundOutput.GetDeviceNames(); if (rbOutputMethodOpenAL.Checked) deviceNames = OpenALSoundOutput.GetDeviceNames();
listBoxSoundDevices.Items.Clear(); listBoxSoundDevices.Items.Clear();
listBoxSoundDevices.Items.Add("<default>"); listBoxSoundDevices.Items.Add("<default>");
listBoxSoundDevices.SelectedIndex = 0; listBoxSoundDevices.SelectedIndex = 0;

View File

@ -7,6 +7,7 @@ using BizHawk.Emulation.Common;
using BizHawk.Client.Common; using BizHawk.Client.Common;
using BizHawk.Client.EmuHawk.ToolExtensions; using BizHawk.Client.EmuHawk.ToolExtensions;
using BizHawk.Common;
//TODO - select which memorydomains go out to the CDL file. will this cause a problem when re-importing it? //TODO - select which memorydomains go out to the CDL file. will this cause a problem when re-importing it?
//perhaps missing domains shouldnt fail a check //perhaps missing domains shouldnt fail a check
@ -98,7 +99,16 @@ namespace BizHawk.Client.EmuHawk
if (_cdl == null) if (_cdl == null)
{ {
lvCDL.BeginUpdate(); lvCDL.BeginUpdate();
lvCDL.Items.Clear(); if (OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows)
{
lvCDL.Items.Clear();
}
else
{
// this is a winforms implementation problem for mono
// see https://github.com/mono/mono/issues/11070
// until this is resolved in mono we should just skip this call
}
lvCDL.EndUpdate(); lvCDL.EndUpdate();
return; return;
} }

View File

@ -186,9 +186,9 @@ namespace BizHawk.Client.EmuHawk
} }
var currentScripts = LuaImp?.ScriptList; // Temp fix for now var currentScripts = LuaImp?.ScriptList; // Temp fix for now
LuaImp = PlatformLinkedLibSingleton.RunningOnUnix LuaImp = OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows
? (PlatformEmuLuaLibrary) new NotReallyLuaLibrary() ? (PlatformEmuLuaLibrary) new EmuLuaLibrary(Emulator.ServiceProvider)
: (PlatformEmuLuaLibrary) new EmuLuaLibrary(Emulator.ServiceProvider); : (PlatformEmuLuaLibrary) new NotReallyLuaLibrary();
if (currentScripts != null) if (currentScripts != null)
{ {
LuaImp.ScriptList.AddRange(currentScripts); LuaImp.ScriptList.AddRange(currentScripts);

View File

@ -7,6 +7,7 @@ using System.Windows.Forms;
using BizHawk.Emulation.Common; using BizHawk.Emulation.Common;
using BizHawk.Client.ApiHawk; using BizHawk.Client.ApiHawk;
using BizHawk.Common;
namespace BizHawk.Client.EmuHawk namespace BizHawk.Client.EmuHawk
{ {
@ -68,6 +69,8 @@ namespace BizHawk.Client.EmuHawk
continue; continue;
// if (!ApiInjector.IsAvailable(, t)) // if (!ApiInjector.IsAvailable(, t))
// continue; // continue;
if (t == typeof(HexView) && OSTailoredCode.CurrentOS != OSTailoredCode.DistinctOS.Windows)
continue; // Skip this tool on Unix. It isn't finished and only causes exceptions
var instance = Activator.CreateInstance(t); var instance = Activator.CreateInstance(t);

View File

@ -159,7 +159,15 @@ namespace BizHawk.Client.EmuHawk
} }
newTool.Restart(); newTool.Restart();
newTool.Show(); if (OSTailoredCode.CurrentOS != OSTailoredCode.DistinctOS.Windows
&& newTool is RamSearch)
{
// the mono winforms implementation is buggy, skip to the return statement and call Show in MainForm instead
}
else
{
newTool.Show();
}
return (T)newTool; return (T)newTool;
} }
@ -740,7 +748,7 @@ namespace BizHawk.Client.EmuHawk
return false; return false;
} }
if (t == typeof(LuaConsole) && PlatformLinkedLibSingleton.RunningOnUnix) return false; if (t == typeof(LuaConsole) && OSTailoredCode.CurrentOS != OSTailoredCode.DistinctOS.Windows) return false;
var tool = Assembly var tool = Assembly
.GetExecutingAssembly() .GetExecutingAssembly()

View File

@ -85,7 +85,7 @@
<Compile Include="MruStack.cs" /> <Compile Include="MruStack.cs" />
<Compile Include="MutableIntRange.cs" /> <Compile Include="MutableIntRange.cs" />
<Compile Include="NDBDatabase.cs" /> <Compile Include="NDBDatabase.cs" />
<Compile Include="PlatformLinkedLibSingleton.cs" /> <Compile Include="OSTailoredCode.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="QuickCollections.cs" /> <Compile Include="QuickCollections.cs" />
<Compile Include="Serializer.cs" /> <Compile Include="Serializer.cs" />

View File

@ -8,12 +8,12 @@ namespace BizHawk.Common.BizInvoke
public class DynamicLibraryImportResolver : IImportResolver, IDisposable public class DynamicLibraryImportResolver : IImportResolver, IDisposable
{ {
private IntPtr _p; private IntPtr _p;
private readonly PlatformLinkedLibSingleton.PlatformLinkedLibManager libLoader = PlatformLinkedLibSingleton.LinkedLibManager; private readonly OSTailoredCode.ILinkedLibManager libLoader = OSTailoredCode.LinkedLibManager;
public DynamicLibraryImportResolver(string dllName) public DynamicLibraryImportResolver(string dllName)
{ {
_p = libLoader.LoadPlatformSpecific(dllName); _p = libLoader.LoadPlatformSpecific(dllName);
if (_p == IntPtr.Zero) throw new InvalidOperationException($"null pointer returned by {nameof(PlatformLinkedLibSingleton.PlatformLinkedLibManager.LoadPlatformSpecific)}"); if (_p == IntPtr.Zero) throw new InvalidOperationException($"null pointer returned by {nameof(libLoader.LoadPlatformSpecific)}");
} }
public IntPtr Resolve(string entryPoint) public IntPtr Resolve(string entryPoint)

View File

@ -79,6 +79,9 @@ namespace BizHawk.Common.BizInvoke
/// <param name="size"></param> /// <param name="size"></param>
public MemoryBlock(ulong start, ulong size) public MemoryBlock(ulong start, ulong size)
{ {
if (OSTailoredCode.CurrentOS != OSTailoredCode.DistinctOS.Windows)
throw new InvalidOperationException("MemoryBlock ctor called on Unix");
if (!WaterboxUtils.Aligned(start)) if (!WaterboxUtils.Aligned(start))
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
if (size == 0) if (size == 0)

View File

@ -1,44 +1,73 @@
using System; using System;
using System.Runtime.InteropServices; using System.Diagnostics;
using System.Runtime.InteropServices;
//put in a different namespace for EXE so we can have an instance of this type (by linking to this file rather than copying it) built-in to the exe
//so the exe doesnt implicitly depend on the dll //put in a different namespace for EXE so we can have an instance of this type (by linking to this file rather than copying it) built-in to the exe
//so the exe doesnt implicitly depend on the dll
#if EXE_PROJECT #if EXE_PROJECT
namespace EXE_PROJECT namespace EXE_PROJECT
#else #else
namespace BizHawk.Common namespace BizHawk.Common
#endif #endif
{ {
public sealed class OSTailoredCode
public sealed class PlatformLinkedLibSingleton
{ {
public static readonly bool RunningOnUnix = Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX; /// <remarks>macOS doesn't use PlatformID.MacOSX</remarks>
public static readonly DistinctOS CurrentOS = Environment.OSVersion.Platform == PlatformID.Unix
? currentIsMacOS() ? DistinctOS.macOS : DistinctOS.Linux
: DistinctOS.Windows;
private static readonly Lazy<PlatformLinkedLibManager> lazy = new Lazy<PlatformLinkedLibManager>(() => RunningOnUnix private static readonly Lazy<ILinkedLibManager> lazy = new Lazy<ILinkedLibManager>(() =>
? (PlatformLinkedLibManager) new UnixMonoLinkedLibManager() {
: (PlatformLinkedLibManager) new Win32LinkedLibManager()); switch (CurrentOS)
{
case DistinctOS.Linux:
case DistinctOS.macOS:
return new UnixMonoLLManager();
case DistinctOS.Windows:
return new WindowsLLManager();
default:
throw new ArgumentOutOfRangeException();
}
});
public static PlatformLinkedLibManager LinkedLibManager { get { return lazy.Value; } } public static ILinkedLibManager LinkedLibManager => lazy.Value;
private PlatformLinkedLibSingleton() {} private static bool currentIsMacOS()
{
var proc = new Process {
StartInfo = new ProcessStartInfo {
Arguments = "-s",
CreateNoWindow = true,
FileName = "uname",
RedirectStandardOutput = true,
UseShellExecute = false
}
};
proc.Start();
if (proc.StandardOutput.EndOfStream) throw new Exception("Can't determine OS (uname wrote nothing to stdout)!");
return proc.StandardOutput.ReadLine() == "Darwin";
}
public interface PlatformLinkedLibManager private OSTailoredCode() {}
public interface ILinkedLibManager
{ {
IntPtr LoadPlatformSpecific(string dllToLoad); IntPtr LoadPlatformSpecific(string dllToLoad);
IntPtr GetProcAddr(IntPtr hModule, string procName); IntPtr GetProcAddr(IntPtr hModule, string procName);
int FreePlatformSpecific(IntPtr hModule); int FreePlatformSpecific(IntPtr hModule);
} }
public class UnixMonoLinkedLibManager : PlatformLinkedLibManager /// <remarks>This class is copied from a tutorial, so don't git blame and then email me expecting insight.</remarks>
private class UnixMonoLLManager : ILinkedLibManager
{ {
// This class is copied from a tutorial, so don't git blame and then email me expecting insight. private const int RTLD_NOW = 2;
const int RTLD_NOW = 2;
[DllImport("libdl.so.2")] [DllImport("libdl.so.2")]
private static extern IntPtr dlopen(String fileName, int flags); private static extern IntPtr dlopen(string fileName, int flags);
[DllImport("libdl.so.2")] [DllImport("libdl.so.2")]
private static extern IntPtr dlerror(); private static extern IntPtr dlerror();
[DllImport("libdl.so.2")] [DllImport("libdl.so.2")]
private static extern IntPtr dlsym(IntPtr handle, String symbol); private static extern IntPtr dlsym(IntPtr handle, string symbol);
[DllImport("libdl.so.2")] [DllImport("libdl.so.2")]
private static extern int dlclose(IntPtr handle); private static extern int dlclose(IntPtr handle);
public IntPtr LoadPlatformSpecific(string dllToLoad) public IntPtr LoadPlatformSpecific(string dllToLoad)
@ -59,10 +88,10 @@ public sealed class PlatformLinkedLibSingleton
} }
} }
public class Win32LinkedLibManager : PlatformLinkedLibManager private class WindowsLLManager : ILinkedLibManager
{ {
[DllImport("kernel32.dll")] [DllImport("kernel32.dll")]
private static extern UInt32 GetLastError(); private static extern uint GetLastError();
// was annotated `[DllImport("kernel32.dll", BestFitMapping = false, ThrowOnUnmappableChar = true)]` in SevenZip.NativeMethods // was annotated `[DllImport("kernel32.dll", BestFitMapping = false, ThrowOnUnmappableChar = true)]` in SevenZip.NativeMethods
// param dllToLoad was annotated `[MarshalAs(UnmanagedType.LPStr)]` in SevenZip.NativeMethods // param dllToLoad was annotated `[MarshalAs(UnmanagedType.LPStr)]` in SevenZip.NativeMethods
[DllImport("kernel32.dll")] [DllImport("kernel32.dll")]
@ -89,5 +118,12 @@ public sealed class PlatformLinkedLibSingleton
return FreeLibrary(hModule) ? 1 : 0; return FreeLibrary(hModule) ? 1 : 0;
} }
} }
public enum DistinctOS : byte
{
Linux,
macOS,
Windows
}
} }
} }

View File

@ -59,10 +59,8 @@ namespace BizHawk.Common
} }
} }
#if WINDOWS
[DllImport("kernel32.dll", EntryPoint = "DeleteFileW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)] [DllImport("kernel32.dll", EntryPoint = "DeleteFileW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)]
static extern bool DeleteFileW([MarshalAs(UnmanagedType.LPWStr)]string lpFileName); static extern bool DeleteFileW([MarshalAs(UnmanagedType.LPWStr)]string lpFileName);
#endif
static void ThreadProc() static void ThreadProc()
{ {
@ -94,12 +92,10 @@ namespace BizHawk.Common
{ {
try try
{ {
// SHUT. UP. THE. EXCEPTIONS. if (OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows)
#if WINDOWS DeleteFileW(fi.FullName); // SHUT. UP. THE. EXCEPTIONS.
DeleteFileW(fi.FullName); else
#else fi.Delete();
fi.Delete();
#endif
} }
catch catch
{ {

View File

@ -27,7 +27,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64.NativeApi
bool event_frameend = false; bool event_frameend = false;
bool event_breakpoint = false; bool event_breakpoint = false;
private static readonly PlatformLinkedLibSingleton.PlatformLinkedLibManager libLoader = PlatformLinkedLibSingleton.LinkedLibManager; private static readonly OSTailoredCode.ILinkedLibManager libLoader = OSTailoredCode.LinkedLibManager;
public enum m64p_error public enum m64p_error
{ {

View File

@ -59,8 +59,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BizHawk.Client.MultiHawk",
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BizHawk.Client.ApiHawk", "BizHawk.Client.ApiHawk\BizHawk.Client.ApiHawk.csproj", "{8E2F11F2-3955-4382-8C3A-CEBA1276CAEA}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BizHawk.Client.ApiHawk", "BizHawk.Client.ApiHawk\BizHawk.Client.ApiHawk.csproj", "{8E2F11F2-3955-4382-8C3A-CEBA1276CAEA}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Virtu", "ExternalCoreProjects\Virtu\Virtu.csproj", "{8E522778-7A2C-4364-BDCE-0BA5623828E1}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -127,18 +125,6 @@ Global
{8E2F11F2-3955-4382-8C3A-CEBA1276CAEA}.Debug|Any CPU.Build.0 = Debug|Any CPU {8E2F11F2-3955-4382-8C3A-CEBA1276CAEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8E2F11F2-3955-4382-8C3A-CEBA1276CAEA}.Release|Any CPU.ActiveCfg = Release|Any CPU {8E2F11F2-3955-4382-8C3A-CEBA1276CAEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8E2F11F2-3955-4382-8C3A-CEBA1276CAEA}.Release|Any CPU.Build.0 = Release|Any CPU {8E2F11F2-3955-4382-8C3A-CEBA1276CAEA}.Release|Any CPU.Build.0 = Release|Any CPU
{8E522778-7A2C-4364-BDCE-0BA5623828E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8E522778-7A2C-4364-BDCE-0BA5623828E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8E522778-7A2C-4364-BDCE-0BA5623828E1}.Debug|x64.ActiveCfg = Debug|Any CPU
{8E522778-7A2C-4364-BDCE-0BA5623828E1}.Debug|x64.Build.0 = Debug|Any CPU
{8E522778-7A2C-4364-BDCE-0BA5623828E1}.Debug|x86.ActiveCfg = Debug|Any CPU
{8E522778-7A2C-4364-BDCE-0BA5623828E1}.Debug|x86.Build.0 = Debug|Any CPU
{8E522778-7A2C-4364-BDCE-0BA5623828E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8E522778-7A2C-4364-BDCE-0BA5623828E1}.Release|Any CPU.Build.0 = Release|Any CPU
{8E522778-7A2C-4364-BDCE-0BA5623828E1}.Release|x64.ActiveCfg = Release|Any CPU
{8E522778-7A2C-4364-BDCE-0BA5623828E1}.Release|x64.Build.0 = Release|Any CPU
{8E522778-7A2C-4364-BDCE-0BA5623828E1}.Release|x86.ActiveCfg = Release|Any CPU
{8E522778-7A2C-4364-BDCE-0BA5623828E1}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

2
Dist/BuildDebug.sh Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
cd "$(dirname "$0")/.." && msbuild /p:Configuration=Debug BizHawk.sln

2
Dist/BuildRelease.sh Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
cd "$(dirname "$0")/.." && msbuild /p:Configuration=Release BizHawk.sln

View File

@ -1,12 +0,0 @@
#!/bin/sh
cd "`dirname "$0"`"
if test -d .git ; then
REV="`git svn info | grep Revision: | cut -d' ' -f2`"
else
REV="`svn info | grep Revision: | cut -d' ' -f2`"
fi
sed -e 's/\$WCREV\$/'$REV'/g' "$1/Properties/svnrev_template" > "$1/Properties/svnrev.cs.tmp"
cmp -s "$1/Properties/svnrev.cs.tmp" "$1/Properties/svnrev.cs" || cp "$1/Properties/svnrev.cs.tmp" "$1/Properties/svnrev.cs"