mame core wip (#1705)
* add MAME to OpenAdvanced * make mame launch games limited to arcades that only need rom name. other devices require machine name and rom name, and won't run. nor they are meant to be supported anyway: we have enough emulators that do the job better for particular devices. dunno if direct disk access will be avoidable, there are quite some files it might want to load other than the rom (parent rom, bios, artwork). trapping all of these might be a future task. it is also known that mame can load "romname.zip" file just as well as "romname" folder, which would represent an unarchived zip. I make use of it to send it zip name with extension. it's easy, and we're not obliged to recognize mere folder paths in the mame-advanced-loader logic. * ability to run lua code inside mame
This commit is contained in:
parent
d65092e967
commit
0247a8f1a8
|
@ -117,6 +117,9 @@ namespace BizHawk.Client.ApiHawk
|
|||
case "GB4x":
|
||||
return CoreSystem.GB3x;
|
||||
|
||||
case "MAME":
|
||||
return CoreSystem.MAME;
|
||||
|
||||
case "VB":
|
||||
case "NGP":
|
||||
case "DNGP":
|
||||
|
|
|
@ -34,8 +34,9 @@
|
|||
ZXSpectrum,
|
||||
AmstradCPC,
|
||||
GGL,
|
||||
ChannelF,
|
||||
GB3x,
|
||||
GB4x
|
||||
GB4x,
|
||||
ChannelF,
|
||||
MAME
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ namespace BizHawk.Client.Common
|
|||
public const string OpenRom = "OpenRom";
|
||||
public const string Libretro = "Libretro";
|
||||
public const string LibretroNoGame = "LibretroNoGame";
|
||||
public const string MAME = "MAME";
|
||||
}
|
||||
|
||||
|
||||
|
@ -55,12 +56,33 @@ namespace BizHawk.Client.Common
|
|||
string type = text.Substring(0, idx);
|
||||
string token = text.Substring(idx + 1);
|
||||
IOpenAdvanced ioa;
|
||||
if (type == OpenAdvancedTypes.OpenRom) ioa = new OpenAdvanced_OpenRom();
|
||||
else if (type == OpenAdvancedTypes.Libretro) ioa = new OpenAdvanced_Libretro();
|
||||
else if (type == OpenAdvancedTypes.LibretroNoGame) ioa = new OpenAdvanced_LibretroNoGame();
|
||||
else ioa = null;
|
||||
if (ioa == null)
|
||||
throw new InvalidOperationException($"{nameof(IOpenAdvanced)} deserialization error");
|
||||
|
||||
if (type == OpenAdvancedTypes.OpenRom)
|
||||
{
|
||||
ioa = new OpenAdvanced_OpenRom();
|
||||
}
|
||||
else if (type == OpenAdvancedTypes.Libretro)
|
||||
{
|
||||
ioa = new OpenAdvanced_Libretro();
|
||||
}
|
||||
else if (type == OpenAdvancedTypes.LibretroNoGame)
|
||||
{
|
||||
ioa = new OpenAdvanced_LibretroNoGame();
|
||||
}
|
||||
else if (type == OpenAdvancedTypes.MAME)
|
||||
{
|
||||
ioa = new OpenAdvanced_MAME();
|
||||
}
|
||||
else
|
||||
{
|
||||
ioa = null;
|
||||
}
|
||||
|
||||
if (ioa == null)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(IOpenAdvanced)} deserialization error");
|
||||
}
|
||||
|
||||
ioa.Deserialize(token);
|
||||
return ioa;
|
||||
}
|
||||
|
@ -161,4 +183,26 @@ namespace BizHawk.Client.Common
|
|||
tw.Write(Path);
|
||||
}
|
||||
}
|
||||
|
||||
public class OpenAdvanced_MAME : IOpenAdvanced
|
||||
{
|
||||
public OpenAdvanced_MAME()
|
||||
{ }
|
||||
|
||||
public string Path;
|
||||
|
||||
public string TypeName { get { return "MAME"; } }
|
||||
public string DisplayName { get { return Path; } }
|
||||
public string SimplePath { get { return Path; } }
|
||||
|
||||
public void Deserialize(string str)
|
||||
{
|
||||
Path = str;
|
||||
}
|
||||
|
||||
public void Serialize(TextWriter tw)
|
||||
{
|
||||
tw.Write(Path);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ using BizHawk.Emulation.Cores.Sega.Saturn;
|
|||
using BizHawk.Emulation.Cores.Sony.PSP;
|
||||
using BizHawk.Emulation.Cores.Sony.PSX;
|
||||
using BizHawk.Emulation.Cores.Computers.SinclairSpectrum;
|
||||
using BizHawk.Emulation.Cores.Arcades.MAME;
|
||||
using BizHawk.Emulation.DiscSystem;
|
||||
|
||||
using GPGX64 = BizHawk.Emulation.Cores.Consoles.Sega.gpgx;
|
||||
|
@ -171,7 +172,7 @@ namespace BizHawk.Client.Common
|
|||
return false;
|
||||
}
|
||||
|
||||
public bool AsLibretro { get; set; }
|
||||
public AdvancedRomLoaderType AdvancedLoader { get; set; }
|
||||
|
||||
private bool HandleArchiveBinding(HawkFile file)
|
||||
{
|
||||
|
@ -268,8 +269,13 @@ namespace BizHawk.Client.Common
|
|||
// only try mounting a file if a filename was given
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
// lets not use this unless we need to
|
||||
// file.NonArchiveExtensions = romExtensions;
|
||||
// MAME uses these extensions for arcade ROMs, but also accepts all sorts of variations of archives, folders, and files. if we let archive loader handle this, it won't know where to stop, since it'd require MAME's ROM database (which contains ROM names and blob hashes) to look things up, and even then it might be confused by archive/folder structure
|
||||
// so assume the user provides the proper ROM directly, and handle possible errors later
|
||||
if (AdvancedLoader == AdvancedRomLoaderType.MAMELaunchGame)
|
||||
{
|
||||
file.NonArchiveExtensions = new[] { ".zip", ".7z" };
|
||||
}
|
||||
|
||||
file.Open(path);
|
||||
|
||||
// if the provided file doesnt even exist, give up!
|
||||
|
@ -289,7 +295,7 @@ namespace BizHawk.Client.Common
|
|||
{
|
||||
string ext = null;
|
||||
|
||||
if (AsLibretro)
|
||||
if (AdvancedLoader == AdvancedRomLoaderType.LibretroLaunchGame)
|
||||
{
|
||||
string codePathPart = Path.GetFileNameWithoutExtension(nextComm.LaunchLibretroCore);
|
||||
|
||||
|
@ -1152,6 +1158,9 @@ namespace BizHawk.Client.Common
|
|||
nextEmulator = new Octoshock(nextComm, null, null, rom.FileData, GetCoreSettings<Octoshock>(), GetCoreSyncSettings<Octoshock>());
|
||||
nextEmulator.CoreComm.RomStatusDetails = "PSX etc.";
|
||||
break;
|
||||
case "Arcade":
|
||||
nextEmulator = new MAME(nextComm, file.Directory, file.CanonicalName);
|
||||
break;
|
||||
case "GEN":
|
||||
if (Global.Config.CoreForcingViaGameDB && game.ForcedCore?.ToLower() == "pico")
|
||||
{
|
||||
|
|
|
@ -223,6 +223,11 @@ namespace BizHawk.Client.Common
|
|||
/// </summary>
|
||||
public static SystemInfo ChannelF { get; } = new SystemInfo("Channel F", CoreSystem.ChannelF, 2);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="SystemInfo"/> instance for MAME
|
||||
/// </summary>
|
||||
public static SystemInfo MAME { get; } = new SystemInfo("MAME", CoreSystem.MAME, 4);
|
||||
|
||||
#endregion Get SystemInfo
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -1896,6 +1896,7 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="images\StopButton.png" />
|
||||
<None Include="images\mame.png" />
|
||||
<None Include="Resources\MoveTop.png" />
|
||||
<None Include="Resources\MoveBottom.png" />
|
||||
<None Include="Resources\MoveTop.bmp" />
|
||||
|
@ -2225,6 +2226,7 @@
|
|||
<None Include="images\ESE.png" />
|
||||
<None Include="images\ControllerImages\NGPController.png" />
|
||||
<Content Include="config\ControllerImages\ZXSpectrumKeyboards.png" />
|
||||
<None Include="images\ControllerImages\ArcadeController.jpg" />
|
||||
<Content Include="images\logo.ico" />
|
||||
<None Include="images\Paste.png" />
|
||||
<None Include="images\reboot.png" />
|
||||
|
|
|
@ -11,6 +11,7 @@ using BizHawk.Emulation.Cores.Nintendo.SNES9X;
|
|||
using BizHawk.Emulation.Cores.Sega.Saturn;
|
||||
using BizHawk.Emulation.Cores.Consoles.Sega.gpgx;
|
||||
using BizHawk.Emulation.Cores.Sony.PSP;
|
||||
using BizHawk.Emulation.Cores.Arcades.MAME;
|
||||
|
||||
using BizHawk.Client.Common;
|
||||
|
||||
|
@ -51,6 +52,10 @@ namespace BizHawk.Client.EmuHawk.CoreExtensions
|
|||
{
|
||||
return Properties.Resources.snes9x;
|
||||
}
|
||||
else if (core is MAME)
|
||||
{
|
||||
return Properties.Resources.mame;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
|
|
|
@ -306,6 +306,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
private void OpenRomMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
AdvancedLoader = AdvancedRomLoaderType.None;
|
||||
OpenRom();
|
||||
}
|
||||
|
||||
|
@ -317,7 +318,9 @@ namespace BizHawk.Client.EmuHawk
|
|||
return;
|
||||
}
|
||||
|
||||
if (oac.Result == OpenAdvancedChooser.Command.RetroLaunchNoGame)
|
||||
AdvancedLoader = oac.Result;
|
||||
|
||||
if (AdvancedLoader == AdvancedRomLoaderType.LibretroLaunchNoGame)
|
||||
{
|
||||
var argsNoGame = new LoadRomArgs
|
||||
{
|
||||
|
@ -331,15 +334,20 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
var filter = RomFilter;
|
||||
|
||||
if (oac.Result == OpenAdvancedChooser.Command.RetroLaunchGame)
|
||||
if (AdvancedLoader == AdvancedRomLoaderType.LibretroLaunchGame)
|
||||
{
|
||||
args.OpenAdvanced = new OpenAdvanced_Libretro();
|
||||
filter = oac.SuggestedExtensionFilter;
|
||||
}
|
||||
else if (oac.Result == OpenAdvancedChooser.Command.ClassicLaunchGame)
|
||||
else if (AdvancedLoader == AdvancedRomLoaderType.ClassicLaunchGame)
|
||||
{
|
||||
args.OpenAdvanced = new OpenAdvanced_OpenRom();
|
||||
}
|
||||
else if (AdvancedLoader == AdvancedRomLoaderType.MAMELaunchGame)
|
||||
{
|
||||
args.OpenAdvanced = new OpenAdvanced_MAME();
|
||||
filter = "MAME Arcade ROMs (*.zip)|*.zip";
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Automatic Alpha Sanitizer");
|
||||
|
|
|
@ -588,6 +588,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
// runloop won't exec lua
|
||||
public bool SuppressLua { get; set; }
|
||||
|
||||
public AdvancedRomLoaderType AdvancedLoader { get; set; }
|
||||
|
||||
public long MouseWheelTracker { get; private set; }
|
||||
|
||||
private int? _pauseOnFrame;
|
||||
|
@ -610,9 +612,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
|
||||
public bool IsSeeking => PauseOnFrame.HasValue;
|
||||
|
||||
private bool IsTurboSeeking => PauseOnFrame.HasValue && Global.Config.TurboSeek;
|
||||
|
||||
public bool IsTurboing => Global.ClientControls["Turbo"] || IsTurboSeeking;
|
||||
|
||||
#endregion
|
||||
|
@ -3488,15 +3488,13 @@ namespace BizHawk.Client.EmuHawk
|
|||
return false;
|
||||
}
|
||||
|
||||
bool asLibretro = args.OpenAdvanced is OpenAdvanced_Libretro || args.OpenAdvanced is OpenAdvanced_LibretroNoGame;
|
||||
|
||||
var loader = new RomLoader
|
||||
{
|
||||
ChooseArchive = LoadArchiveChooser,
|
||||
ChoosePlatform = ChoosePlatformForRom,
|
||||
Deterministic = deterministic,
|
||||
MessageCallback = GlobalWin.OSD.AddMessage,
|
||||
AsLibretro = asLibretro
|
||||
AdvancedLoader = AdvancedLoader
|
||||
};
|
||||
Global.FirmwareManager.RecentlyServed.Clear();
|
||||
|
||||
|
|
|
@ -28,147 +28,184 @@
|
|||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.btnLibretroLaunchNoGame = new System.Windows.Forms.Button();
|
||||
this.btnCancel = new System.Windows.Forms.Button();
|
||||
this.groupBox2 = new System.Windows.Forms.GroupBox();
|
||||
this.txtLibretroCore = new System.Windows.Forms.TextBox();
|
||||
this.btnLibretroLaunchGame = new System.Windows.Forms.Button();
|
||||
this.btnSetLibretroCore = new System.Windows.Forms.Button();
|
||||
this.groupBox3 = new System.Windows.Forms.GroupBox();
|
||||
this.btnClassicLaunchGame = new System.Windows.Forms.Button();
|
||||
this.groupBox2.SuspendLayout();
|
||||
this.groupBox3.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.Location = new System.Drawing.Point(6, 25);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(250, 29);
|
||||
this.label3.TabIndex = 5;
|
||||
this.label3.Text = "Load a rom with the classic BizHawk autodetection method. But why not just use Op" +
|
||||
"en Rom?";
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Location = new System.Drawing.Point(6, 26);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(69, 13);
|
||||
this.label2.TabIndex = 3;
|
||||
this.label2.Text = "Current Core:";
|
||||
//
|
||||
// btnLibretroLaunchNoGame
|
||||
//
|
||||
this.btnLibretroLaunchNoGame.Location = new System.Drawing.Point(217, 50);
|
||||
this.btnLibretroLaunchNoGame.Name = "btnLibretroLaunchNoGame";
|
||||
this.btnLibretroLaunchNoGame.Size = new System.Drawing.Size(102, 23);
|
||||
this.btnLibretroLaunchNoGame.TabIndex = 1;
|
||||
this.btnLibretroLaunchNoGame.Text = "Launch No Game";
|
||||
this.btnLibretroLaunchNoGame.UseVisualStyleBackColor = true;
|
||||
this.btnLibretroLaunchNoGame.Click += new System.EventHandler(this.btnLibretroLaunchNoGame_Click);
|
||||
//
|
||||
// btnCancel
|
||||
//
|
||||
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
this.btnCancel.Location = new System.Drawing.Point(370, 176);
|
||||
this.btnCancel.Name = "btnCancel";
|
||||
this.btnCancel.Size = new System.Drawing.Size(75, 23);
|
||||
this.btnCancel.TabIndex = 2;
|
||||
this.btnCancel.Text = "Cancel";
|
||||
this.btnCancel.UseVisualStyleBackColor = true;
|
||||
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
|
||||
//
|
||||
// groupBox2
|
||||
//
|
||||
this.groupBox2.Controls.Add(this.txtLibretroCore);
|
||||
this.groupBox2.Controls.Add(this.btnLibretroLaunchGame);
|
||||
this.groupBox2.Controls.Add(this.btnSetLibretroCore);
|
||||
this.groupBox2.Controls.Add(this.label2);
|
||||
this.groupBox2.Controls.Add(this.btnLibretroLaunchNoGame);
|
||||
this.groupBox2.Location = new System.Drawing.Point(12, 12);
|
||||
this.groupBox2.Name = "groupBox2";
|
||||
this.groupBox2.Size = new System.Drawing.Size(433, 81);
|
||||
this.groupBox2.TabIndex = 3;
|
||||
this.groupBox2.TabStop = false;
|
||||
this.groupBox2.Text = "Libretro";
|
||||
//
|
||||
// txtLibretroCore
|
||||
//
|
||||
this.txtLibretroCore.AllowDrop = true;
|
||||
this.txtLibretroCore.Location = new System.Drawing.Point(81, 23);
|
||||
this.txtLibretroCore.Name = "txtLibretroCore";
|
||||
this.txtLibretroCore.ReadOnly = true;
|
||||
this.txtLibretroCore.Size = new System.Drawing.Size(314, 20);
|
||||
this.txtLibretroCore.TabIndex = 6;
|
||||
this.txtLibretroCore.DragDrop += new System.Windows.Forms.DragEventHandler(this.txtLibretroCore_DragDrop);
|
||||
this.txtLibretroCore.DragEnter += new System.Windows.Forms.DragEventHandler(this.txtLibretroCore_DragEnter);
|
||||
//
|
||||
// btnLibretroLaunchGame
|
||||
//
|
||||
this.btnLibretroLaunchGame.Location = new System.Drawing.Point(325, 50);
|
||||
this.btnLibretroLaunchGame.Name = "btnLibretroLaunchGame";
|
||||
this.btnLibretroLaunchGame.Size = new System.Drawing.Size(102, 23);
|
||||
this.btnLibretroLaunchGame.TabIndex = 5;
|
||||
this.btnLibretroLaunchGame.Text = "Launch Game";
|
||||
this.btnLibretroLaunchGame.UseVisualStyleBackColor = true;
|
||||
this.btnLibretroLaunchGame.Click += new System.EventHandler(this.btnLibretroLaunchGame_Click);
|
||||
//
|
||||
// btnSetLibretroCore
|
||||
//
|
||||
this.btnSetLibretroCore.AutoSize = true;
|
||||
this.btnSetLibretroCore.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.btnSetLibretroCore.Location = new System.Drawing.Point(401, 21);
|
||||
this.btnSetLibretroCore.Name = "btnSetLibretroCore";
|
||||
this.btnSetLibretroCore.Size = new System.Drawing.Size(26, 23);
|
||||
this.btnSetLibretroCore.TabIndex = 4;
|
||||
this.btnSetLibretroCore.Text = "...";
|
||||
this.btnSetLibretroCore.UseVisualStyleBackColor = true;
|
||||
this.btnSetLibretroCore.Click += new System.EventHandler(this.btnSetLibretroCore_Click);
|
||||
//
|
||||
// groupBox3
|
||||
//
|
||||
this.groupBox3.Controls.Add(this.btnClassicLaunchGame);
|
||||
this.groupBox3.Controls.Add(this.label3);
|
||||
this.groupBox3.Location = new System.Drawing.Point(12, 99);
|
||||
this.groupBox3.Name = "groupBox3";
|
||||
this.groupBox3.Size = new System.Drawing.Size(277, 100);
|
||||
this.groupBox3.TabIndex = 6;
|
||||
this.groupBox3.TabStop = false;
|
||||
this.groupBox3.Text = "BizHawk Classic";
|
||||
//
|
||||
// btnClassicLaunchGame
|
||||
//
|
||||
this.btnClassicLaunchGame.Location = new System.Drawing.Point(169, 71);
|
||||
this.btnClassicLaunchGame.Name = "btnClassicLaunchGame";
|
||||
this.btnClassicLaunchGame.Size = new System.Drawing.Size(102, 23);
|
||||
this.btnClassicLaunchGame.TabIndex = 6;
|
||||
this.btnClassicLaunchGame.Text = "Launch Game";
|
||||
this.btnClassicLaunchGame.UseVisualStyleBackColor = true;
|
||||
this.btnClassicLaunchGame.Click += new System.EventHandler(this.btnClassicLaunchGame_Click);
|
||||
//
|
||||
// OpenAdvancedChooser
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.CancelButton = this.btnCancel;
|
||||
this.ClientSize = new System.Drawing.Size(457, 208);
|
||||
this.Controls.Add(this.groupBox3);
|
||||
this.Controls.Add(this.groupBox2);
|
||||
this.Controls.Add(this.btnCancel);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "OpenAdvancedChooser";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "Open Advanced";
|
||||
this.groupBox2.ResumeLayout(false);
|
||||
this.groupBox2.PerformLayout();
|
||||
this.groupBox3.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.btnLibretroLaunchNoGame = new System.Windows.Forms.Button();
|
||||
this.btnCancel = new System.Windows.Forms.Button();
|
||||
this.groupBox2 = new System.Windows.Forms.GroupBox();
|
||||
this.txtLibretroCore = new System.Windows.Forms.TextBox();
|
||||
this.btnLibretroLaunchGame = new System.Windows.Forms.Button();
|
||||
this.btnSetLibretroCore = new System.Windows.Forms.Button();
|
||||
this.groupBox3 = new System.Windows.Forms.GroupBox();
|
||||
this.btnClassicLaunchGame = new System.Windows.Forms.Button();
|
||||
this.groupBox1 = new System.Windows.Forms.GroupBox();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.btnMAMELaunchGame = new System.Windows.Forms.Button();
|
||||
this.groupBox2.SuspendLayout();
|
||||
this.groupBox3.SuspendLayout();
|
||||
this.groupBox1.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.Location = new System.Drawing.Point(6, 25);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(198, 45);
|
||||
this.label3.TabIndex = 5;
|
||||
this.label3.Text = "Load a ROM with the classic BizHawk autodetection method. But why not just use Op" +
|
||||
"en Rom?";
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Location = new System.Drawing.Point(6, 26);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(69, 13);
|
||||
this.label2.TabIndex = 3;
|
||||
this.label2.Text = "Current Core:";
|
||||
//
|
||||
// btnLibretroLaunchNoGame
|
||||
//
|
||||
this.btnLibretroLaunchNoGame.Location = new System.Drawing.Point(217, 50);
|
||||
this.btnLibretroLaunchNoGame.Name = "btnLibretroLaunchNoGame";
|
||||
this.btnLibretroLaunchNoGame.Size = new System.Drawing.Size(102, 23);
|
||||
this.btnLibretroLaunchNoGame.TabIndex = 1;
|
||||
this.btnLibretroLaunchNoGame.Text = "Launch No Game";
|
||||
this.btnLibretroLaunchNoGame.UseVisualStyleBackColor = true;
|
||||
this.btnLibretroLaunchNoGame.Click += new System.EventHandler(this.btnLibretroLaunchNoGame_Click);
|
||||
//
|
||||
// btnCancel
|
||||
//
|
||||
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
this.btnCancel.Location = new System.Drawing.Point(370, 221);
|
||||
this.btnCancel.Name = "btnCancel";
|
||||
this.btnCancel.Size = new System.Drawing.Size(75, 23);
|
||||
this.btnCancel.TabIndex = 2;
|
||||
this.btnCancel.Text = "Cancel";
|
||||
this.btnCancel.UseVisualStyleBackColor = true;
|
||||
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
|
||||
//
|
||||
// groupBox2
|
||||
//
|
||||
this.groupBox2.Controls.Add(this.txtLibretroCore);
|
||||
this.groupBox2.Controls.Add(this.btnLibretroLaunchGame);
|
||||
this.groupBox2.Controls.Add(this.btnSetLibretroCore);
|
||||
this.groupBox2.Controls.Add(this.label2);
|
||||
this.groupBox2.Controls.Add(this.btnLibretroLaunchNoGame);
|
||||
this.groupBox2.Location = new System.Drawing.Point(12, 12);
|
||||
this.groupBox2.Name = "groupBox2";
|
||||
this.groupBox2.Size = new System.Drawing.Size(433, 81);
|
||||
this.groupBox2.TabIndex = 3;
|
||||
this.groupBox2.TabStop = false;
|
||||
this.groupBox2.Text = "Libretro";
|
||||
//
|
||||
// txtLibretroCore
|
||||
//
|
||||
this.txtLibretroCore.AllowDrop = true;
|
||||
this.txtLibretroCore.Location = new System.Drawing.Point(81, 23);
|
||||
this.txtLibretroCore.Name = "txtLibretroCore";
|
||||
this.txtLibretroCore.ReadOnly = true;
|
||||
this.txtLibretroCore.Size = new System.Drawing.Size(314, 20);
|
||||
this.txtLibretroCore.TabIndex = 6;
|
||||
this.txtLibretroCore.DragDrop += new System.Windows.Forms.DragEventHandler(this.txtLibretroCore_DragDrop);
|
||||
this.txtLibretroCore.DragEnter += new System.Windows.Forms.DragEventHandler(this.txtLibretroCore_DragEnter);
|
||||
//
|
||||
// btnLibretroLaunchGame
|
||||
//
|
||||
this.btnLibretroLaunchGame.Location = new System.Drawing.Point(325, 50);
|
||||
this.btnLibretroLaunchGame.Name = "btnLibretroLaunchGame";
|
||||
this.btnLibretroLaunchGame.Size = new System.Drawing.Size(102, 23);
|
||||
this.btnLibretroLaunchGame.TabIndex = 5;
|
||||
this.btnLibretroLaunchGame.Text = "Launch Game";
|
||||
this.btnLibretroLaunchGame.UseVisualStyleBackColor = true;
|
||||
this.btnLibretroLaunchGame.Click += new System.EventHandler(this.btnLibretroLaunchGame_Click);
|
||||
//
|
||||
// btnSetLibretroCore
|
||||
//
|
||||
this.btnSetLibretroCore.AutoSize = true;
|
||||
this.btnSetLibretroCore.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.btnSetLibretroCore.Location = new System.Drawing.Point(401, 21);
|
||||
this.btnSetLibretroCore.Name = "btnSetLibretroCore";
|
||||
this.btnSetLibretroCore.Size = new System.Drawing.Size(26, 23);
|
||||
this.btnSetLibretroCore.TabIndex = 4;
|
||||
this.btnSetLibretroCore.Text = "...";
|
||||
this.btnSetLibretroCore.UseVisualStyleBackColor = true;
|
||||
this.btnSetLibretroCore.Click += new System.EventHandler(this.btnSetLibretroCore_Click);
|
||||
//
|
||||
// groupBox3
|
||||
//
|
||||
this.groupBox3.Controls.Add(this.btnClassicLaunchGame);
|
||||
this.groupBox3.Controls.Add(this.label3);
|
||||
this.groupBox3.Location = new System.Drawing.Point(235, 99);
|
||||
this.groupBox3.Name = "groupBox3";
|
||||
this.groupBox3.Size = new System.Drawing.Size(210, 100);
|
||||
this.groupBox3.TabIndex = 6;
|
||||
this.groupBox3.TabStop = false;
|
||||
this.groupBox3.Text = "BizHawk Classic";
|
||||
//
|
||||
// btnClassicLaunchGame
|
||||
//
|
||||
this.btnClassicLaunchGame.Location = new System.Drawing.Point(102, 71);
|
||||
this.btnClassicLaunchGame.Name = "btnClassicLaunchGame";
|
||||
this.btnClassicLaunchGame.Size = new System.Drawing.Size(102, 23);
|
||||
this.btnClassicLaunchGame.TabIndex = 6;
|
||||
this.btnClassicLaunchGame.Text = "Launch Game";
|
||||
this.btnClassicLaunchGame.UseVisualStyleBackColor = true;
|
||||
this.btnClassicLaunchGame.Click += new System.EventHandler(this.btnClassicLaunchGame_Click);
|
||||
//
|
||||
// groupBox1
|
||||
//
|
||||
this.groupBox1.Controls.Add(this.label1);
|
||||
this.groupBox1.Controls.Add(this.btnMAMELaunchGame);
|
||||
this.groupBox1.Location = new System.Drawing.Point(13, 99);
|
||||
this.groupBox1.Name = "groupBox1";
|
||||
this.groupBox1.Size = new System.Drawing.Size(216, 100);
|
||||
this.groupBox1.TabIndex = 7;
|
||||
this.groupBox1.TabStop = false;
|
||||
this.groupBox1.Text = "MAME Arcade";
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.Location = new System.Drawing.Point(6, 25);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(204, 42);
|
||||
this.label1.TabIndex = 1;
|
||||
this.label1.Text = "Load .zip archive as MAME Arcade ROM (do not unzip)";
|
||||
this.label1.Click += new System.EventHandler(this.btnMAMELaunchGame_Click);
|
||||
//
|
||||
// btnMAMELaunchGame
|
||||
//
|
||||
this.btnMAMELaunchGame.Location = new System.Drawing.Point(108, 71);
|
||||
this.btnMAMELaunchGame.Name = "btnMAMELaunchGame";
|
||||
this.btnMAMELaunchGame.Size = new System.Drawing.Size(102, 23);
|
||||
this.btnMAMELaunchGame.TabIndex = 0;
|
||||
this.btnMAMELaunchGame.Text = "Launch Game";
|
||||
this.btnMAMELaunchGame.UseVisualStyleBackColor = true;
|
||||
this.btnMAMELaunchGame.Click += new System.EventHandler(this.btnMAMELaunchGame_Click);
|
||||
//
|
||||
// OpenAdvancedChooser
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.CancelButton = this.btnCancel;
|
||||
this.ClientSize = new System.Drawing.Size(457, 256);
|
||||
this.Controls.Add(this.groupBox1);
|
||||
this.Controls.Add(this.groupBox3);
|
||||
this.Controls.Add(this.groupBox2);
|
||||
this.Controls.Add(this.btnCancel);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "OpenAdvancedChooser";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "Open Advanced";
|
||||
this.groupBox2.ResumeLayout(false);
|
||||
this.groupBox2.PerformLayout();
|
||||
this.groupBox3.ResumeLayout(false);
|
||||
this.groupBox1.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -182,6 +219,9 @@
|
|||
private System.Windows.Forms.TextBox txtLibretroCore;
|
||||
private System.Windows.Forms.Button btnLibretroLaunchGame;
|
||||
private System.Windows.Forms.GroupBox groupBox3;
|
||||
private System.Windows.Forms.Button btnClassicLaunchGame;
|
||||
private System.Windows.Forms.Button btnClassicLaunchGame;
|
||||
private System.Windows.Forms.GroupBox groupBox1;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.Button btnMAMELaunchGame;
|
||||
}
|
||||
}
|
|
@ -7,7 +7,8 @@ using System.Drawing;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores;
|
||||
using BizHawk.Emulation.Cores.Libretro;
|
||||
using BizHawk.Client.Common;
|
||||
|
@ -20,13 +21,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
MainForm mainForm;
|
||||
|
||||
public enum Command
|
||||
{
|
||||
RetroLaunchNoGame, RetroLaunchGame,
|
||||
ClassicLaunchGame
|
||||
}
|
||||
|
||||
public Command Result;
|
||||
public AdvancedRomLoaderType Result;
|
||||
public string SuggestedExtensionFilter;
|
||||
|
||||
public OpenAdvancedChooser(MainForm mainForm)
|
||||
|
@ -131,21 +126,28 @@ namespace BizHawk.Client.EmuHawk
|
|||
filter = MainForm.FormatFilter(args.ToArray());
|
||||
SuggestedExtensionFilter = filter;
|
||||
|
||||
Result = Command.RetroLaunchGame;
|
||||
Result = AdvancedRomLoaderType.LibretroLaunchGame;
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void btnMAMELaunchGame_Click(object sender, EventArgs e)
|
||||
{
|
||||
Result = AdvancedRomLoaderType.MAMELaunchGame;
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void btnClassicLaunchGame_Click(object sender, EventArgs e)
|
||||
{
|
||||
Result = Command.ClassicLaunchGame;
|
||||
Result = AdvancedRomLoaderType.ClassicLaunchGame;
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void btnLibretroLaunchNoGame_Click(object sender, EventArgs e)
|
||||
{
|
||||
Result = Command.RetroLaunchNoGame;
|
||||
Result = AdvancedRomLoaderType.LibretroLaunchNoGame;
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
|
@ -170,6 +172,6 @@ namespace BizHawk.Client.EmuHawk
|
|||
var filePaths = (string[])e.Data.GetData(DataFormats.FileDrop);
|
||||
Global.Config.LibretroCore = filePaths[0];
|
||||
RefreshLibretroCore(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,6 +120,16 @@ namespace BizHawk.Client.EmuHawk.Properties {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap ArcadeController {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("ArcadeController", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
|
@ -870,6 +880,16 @@ namespace BizHawk.Client.EmuHawk.Properties {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap mame {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("mame", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
|
|
|
@ -1566,4 +1566,10 @@
|
|||
<data name="StopButton" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\images\StopButton.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="ArcadeController" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\images\ControllerImages\ArcadeController.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="mame" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\images\mame.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
</root>
|
|
@ -49,6 +49,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
ControllerImages.Add("Apple IIe Keyboard", Properties.Resources.AppleIIKeyboard);
|
||||
ControllerImages.Add("VirtualBoy Controller", Properties.Resources.VBoyController);
|
||||
ControllerImages.Add("NeoGeo Portable Controller", Properties.Resources.NGPController);
|
||||
ControllerImages.Add("MAME Controller", Properties.Resources.ArcadeController);
|
||||
}
|
||||
|
||||
private ControllerConfig()
|
||||
|
@ -192,7 +193,10 @@ namespace BizHawk.Client.EmuHawk
|
|||
if (Global.Emulator.SystemId == "ZXSpectrum" || Global.Emulator.SystemId == "AmstradCPC" || Global.Emulator.SystemId == "ChannelF")
|
||||
return;
|
||||
|
||||
string tabname = (Global.Emulator.SystemId == "C64") ? "Keyboard" : "Console"; // hack
|
||||
string tabname =
|
||||
(Global.Emulator.SystemId == "C64") ? "Keyboard" :
|
||||
(Global.Emulator.SystemId == "MAME") ? "Misc" :
|
||||
"Console"; // hack
|
||||
tt.TabPages.Add(tabname);
|
||||
tt.TabPages[pageidx].Controls.Add(createpanel(settings, buckets[0], tt.Size));
|
||||
}
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 115 KiB |
Binary file not shown.
After Width: | Height: | Size: 745 B |
|
@ -371,6 +371,7 @@ namespace BizHawk.Emulation.Common
|
|||
case ".UZE":
|
||||
game.System = "UZE";
|
||||
break;
|
||||
|
||||
case ".32X":
|
||||
game.System = "32X";
|
||||
game.AddOption("32X", "true");
|
||||
|
@ -380,6 +381,12 @@ namespace BizHawk.Emulation.Common
|
|||
game.System = "VEC";
|
||||
game.AddOption("VEC", "true");
|
||||
break;
|
||||
|
||||
// refactor to use mame db (output of "mame -listxml" command)
|
||||
// there's no good definition for Arcade anymore, so we might limit to coin-based machines?
|
||||
case ".ZIP":
|
||||
game.System = "Arcade";
|
||||
break;
|
||||
}
|
||||
|
||||
game.Name = Path.GetFileNameWithoutExtension(fileName)?.Replace('_', ' ');
|
||||
|
|
|
@ -35,4 +35,16 @@
|
|||
Overdump,
|
||||
NotInDatabase
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Advanced ROM Loader type in MainForm/RomLoader/OpenAdvancedChooser
|
||||
/// </summary>
|
||||
public enum AdvancedRomLoaderType
|
||||
{
|
||||
None,
|
||||
LibretroLaunchNoGame,
|
||||
LibretroLaunchGame,
|
||||
ClassicLaunchGame,
|
||||
MAMELaunchGame
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Arcades.MAME
|
||||
{
|
||||
public static class LibMAME
|
||||
{
|
||||
const string dll = "libmamearcade64.dll"; // libmamearcade64.dll libpacmansh64d.dll
|
||||
const CallingConvention cc = CallingConvention.Cdecl;
|
||||
|
||||
public enum OutputChannel
|
||||
{
|
||||
ERROR, WARNING, INFO, DEBUG, VERBOSE, LOG, COUNT
|
||||
};
|
||||
|
||||
// main launcher
|
||||
[DllImport(dll, CallingConvention = cc)]
|
||||
public static extern UInt32 mame_launch(int argc, string[] argv);
|
||||
|
||||
#region Lua API
|
||||
|
||||
// execute
|
||||
[DllImport(dll, CallingConvention = cc)]
|
||||
public static extern void mame_lua_execute(string code);
|
||||
|
||||
// get int
|
||||
[DllImport(dll, CallingConvention = cc)]
|
||||
public static extern int mame_lua_get_int(string code);
|
||||
|
||||
// get double
|
||||
[DllImport(dll, CallingConvention = cc)]
|
||||
public static extern double mame_lua_get_double(string code);
|
||||
|
||||
// get bool
|
||||
[DllImport(dll, CallingConvention = cc)]
|
||||
public static extern bool mame_lua_get_bool(string code);
|
||||
|
||||
// get string
|
||||
[DllImport(dll, CallingConvention = cc)]
|
||||
public static extern IntPtr mame_lua_get_string(string code, out int length);
|
||||
|
||||
// free string
|
||||
[DllImport(dll, CallingConvention = cc)]
|
||||
public static extern bool mame_lua_free_string(IntPtr pointer);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Callbacks
|
||||
|
||||
// periodic
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void PeriodicCallbackDelegate();
|
||||
[DllImport(dll, CallingConvention = cc)]
|
||||
public static extern void mame_set_periodic_callback(PeriodicCallbackDelegate cb);
|
||||
|
||||
// sound
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void SoundCallbackDelegate();
|
||||
[DllImport(dll, CallingConvention = cc)]
|
||||
public static extern void mame_set_sound_callback(SoundCallbackDelegate cb);
|
||||
|
||||
// boot
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void BootCallbackDelegate();
|
||||
[DllImport(dll, CallingConvention = cc)]
|
||||
public static extern void mame_set_boot_callback(BootCallbackDelegate cb);
|
||||
|
||||
// log
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void LogCallbackDelegate(OutputChannel channel, int size, string data);
|
||||
[DllImport(dll, CallingConvention = cc)]
|
||||
public static extern void mame_set_log_callback(LogCallbackDelegate cb);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,484 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Diagnostics;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Common.IEmulatorExtensions;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Arcades.MAME
|
||||
{
|
||||
[Core(
|
||||
name: "MAME",
|
||||
author: "MAMEDev",
|
||||
isPorted: true,
|
||||
portedVersion: "0.214",
|
||||
portedUrl: "https://github.com/mamedev/mame.git",
|
||||
singleInstance: false)]
|
||||
public partial class MAME : IEmulator, IVideoProvider, ISoundProvider
|
||||
{
|
||||
public MAME(CoreComm comm, string dir, string file)
|
||||
{
|
||||
ServiceProvider = new BasicServiceProvider(this);
|
||||
|
||||
CoreComm = comm;
|
||||
gameDirectory = dir;
|
||||
gameFilename = file;
|
||||
MAMEThread = new Thread(ExecuteMAMEThread);
|
||||
|
||||
AsyncLaunchMAME();
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
public CoreComm CoreComm { get; private set; }
|
||||
public IEmulatorServiceProvider ServiceProvider { get; private set; }
|
||||
public ControllerDefinition ControllerDefinition => MAMEController;
|
||||
public string SystemId => "MAME";
|
||||
public int[] GetVideoBuffer() => frameBuffer;
|
||||
public bool DeterministicEmulation => true;
|
||||
public bool CanProvideAsync => false;
|
||||
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
|
||||
public int BackgroundColor => 0;
|
||||
public int Frame { get; private set; }
|
||||
public int VirtualWidth { get; private set; } = 320;
|
||||
public int VirtualHeight { get; private set; } = 240;
|
||||
public int BufferWidth { get; private set; } = 320;
|
||||
public int BufferHeight { get; private set; } = 240;
|
||||
public int VsyncNumerator { get; private set; } = 60;
|
||||
public int VsyncDenominator { get; private set; } = 1;
|
||||
private int samplesPerFrame => (int)Math.Round(sampleRate / this.VsyncRate());
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
private Thread MAMEThread;
|
||||
private ManualResetEvent MAMEStartupComplete = new ManualResetEvent(false);
|
||||
private ManualResetEvent MAMEFrameComplete = new ManualResetEvent(false);
|
||||
private SortedDictionary<string, string> fieldsPorts = new SortedDictionary<string, string>();
|
||||
private IController Controller = NullController.Instance;
|
||||
private int[] frameBuffer = new int[0];
|
||||
private short[] audioBuffer = new short[0];
|
||||
private Queue<short> audioSamples = new Queue<short>();
|
||||
private int sampleRate = 44100;
|
||||
private bool paused = true;
|
||||
private bool exiting = false;
|
||||
private bool frameDone = true;
|
||||
private int numSamples = 0;
|
||||
private string gameDirectory;
|
||||
private string gameFilename;
|
||||
private LibMAME.PeriodicCallbackDelegate periodicCallback;
|
||||
private LibMAME.SoundCallbackDelegate soundCallback;
|
||||
private LibMAME.BootCallbackDelegate bootCallback;
|
||||
private LibMAME.LogCallbackDelegate logCallback;
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEmulator
|
||||
|
||||
public bool FrameAdvance(IController controller, bool render, bool rendersound = true)
|
||||
{
|
||||
if (exiting)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Controller = controller;
|
||||
paused = false;
|
||||
frameDone = false;
|
||||
|
||||
for (; frameDone == false;)
|
||||
{
|
||||
MAMEFrameComplete.WaitOne();
|
||||
}
|
||||
|
||||
Frame++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ResetCounters()
|
||||
{
|
||||
Frame = 0;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
exiting = true;
|
||||
MAMEThread.Join();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ISoundProvider
|
||||
|
||||
public void SetSyncMode(SyncSoundMode mode)
|
||||
{
|
||||
if (mode == SyncSoundMode.Async)
|
||||
{
|
||||
throw new NotSupportedException("Async mode is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
public void GetSamplesSync(out short[] samples, out int nsamp)
|
||||
{
|
||||
nsamp = samplesPerFrame;
|
||||
samples = new short[samplesPerFrame * 2];
|
||||
|
||||
for (int i = 0; i < samplesPerFrame * 2; i++)
|
||||
{
|
||||
if (audioSamples.Any())
|
||||
{
|
||||
samples[i] = audioSamples.Dequeue();
|
||||
}
|
||||
else
|
||||
{
|
||||
samples[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void GetSamplesAsync(short[] samples)
|
||||
{
|
||||
throw new InvalidOperationException("Async mode is not supported.");
|
||||
}
|
||||
|
||||
public void DiscardSamples()
|
||||
{
|
||||
audioSamples.Clear();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Launchers
|
||||
|
||||
private void AsyncLaunchMAME()
|
||||
{
|
||||
MAMEThread.Start();
|
||||
MAMEStartupComplete.WaitOne();
|
||||
}
|
||||
|
||||
private void ExecuteMAMEThread()
|
||||
{
|
||||
// dodge GC
|
||||
periodicCallback = MAMEPeriodicCallback;
|
||||
soundCallback = MAMESoundCallback;
|
||||
bootCallback = MAMEBootCallback;
|
||||
logCallback = MAMELogCallback;
|
||||
|
||||
LibMAME.mame_set_periodic_callback(periodicCallback);
|
||||
LibMAME.mame_set_sound_callback(soundCallback);
|
||||
LibMAME.mame_set_boot_callback(bootCallback);
|
||||
LibMAME.mame_set_log_callback(logCallback);
|
||||
|
||||
// https://docs.mamedev.org/commandline/commandline-index.html
|
||||
string[] args = new string[] {
|
||||
"mame" // dummy, internally discarded by index, so has to go first
|
||||
, gameFilename // no dash for rom names
|
||||
, "-noreadconfig" // forbid reading any config files
|
||||
, "-norewind" // forbid rewind savestates (captured upon frame advance)
|
||||
, "-skip_gameinfo" // forbid this blocking screen that requires user input
|
||||
, "-nothrottle" // forbid throttling to "real" speed of the device
|
||||
, "-update_in_pause" // ^ including frame-advancing
|
||||
, "-rompath", gameDirectory // mame doesn't load roms from full paths, only from dirs to scan
|
||||
, "-volume", "-32" // lowest attenuation means mame osd remains silent
|
||||
, "-output", "console" // print everyting to hawk console
|
||||
, "-samplerate", sampleRate.ToString() // match hawk samplerate
|
||||
, "-video", "none" // forbid mame window altogether
|
||||
, "-keyboardprovider", "none"
|
||||
, "-mouseprovider", "none"
|
||||
, "-lightgunprovider", "none"
|
||||
, "-joystickprovider", "none"
|
||||
};
|
||||
|
||||
LibMAME.mame_launch(args.Length, args);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Updaters
|
||||
|
||||
private void UpdateFramerate()
|
||||
{
|
||||
VsyncNumerator = 1000000000;
|
||||
UInt64 refresh = (UInt64)LibMAME.mame_lua_get_double(MAMELuaCommand.GetRefresh);
|
||||
VsyncDenominator = (int)(refresh / 1000000000);
|
||||
}
|
||||
|
||||
private void UpdateAspect()
|
||||
{
|
||||
int x = (int)LibMAME.mame_lua_get_double(MAMELuaCommand.GetBoundX);
|
||||
int y = (int)LibMAME.mame_lua_get_double(MAMELuaCommand.GetBoundY);
|
||||
VirtualHeight = BufferWidth > BufferHeight * x / y
|
||||
? BufferWidth * y / x
|
||||
: BufferHeight;
|
||||
VirtualWidth = VirtualHeight * x / y;
|
||||
}
|
||||
|
||||
private void UpdateVideo()
|
||||
{
|
||||
BufferWidth = LibMAME.mame_lua_get_int(MAMELuaCommand.GetWidth);
|
||||
BufferHeight = LibMAME.mame_lua_get_int(MAMELuaCommand.GetHeight);
|
||||
int expectedSize = BufferWidth * BufferHeight;
|
||||
int bytesPerPixel = 4;
|
||||
int lengthInBytes;
|
||||
IntPtr ptr = LibMAME.mame_lua_get_string(MAMELuaCommand.GetPixels, out lengthInBytes);
|
||||
|
||||
if (ptr == IntPtr.Zero)
|
||||
{
|
||||
Console.WriteLine("LibMAME ERROR: frame buffer pointer is null");
|
||||
return;
|
||||
}
|
||||
|
||||
if (expectedSize * bytesPerPixel != lengthInBytes)
|
||||
{
|
||||
Console.WriteLine(
|
||||
"LibMAME ERROR: frame buffer has wrong size\n" +
|
||||
$"width: { BufferWidth } pixels\n" +
|
||||
$"height: { BufferHeight } pixels\n" +
|
||||
$"expected: { expectedSize * bytesPerPixel } bytes\n" +
|
||||
$"received: { lengthInBytes } bytes\n");
|
||||
return;
|
||||
}
|
||||
|
||||
frameBuffer = new int[expectedSize];
|
||||
Marshal.Copy(ptr, frameBuffer, 0, expectedSize);
|
||||
|
||||
if (!LibMAME.mame_lua_free_string(ptr))
|
||||
{
|
||||
Console.WriteLine("LibMAME ERROR: frame buffer wasn't freed");
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateInput()
|
||||
{
|
||||
foreach (var fieldPort in fieldsPorts)
|
||||
{
|
||||
LibMAME.mame_lua_execute(
|
||||
"manager:machine():ioport()" +
|
||||
$".ports [\"{ fieldPort.Value }\"]" +
|
||||
$".fields [\"{ fieldPort.Key }\"]" +
|
||||
$":set_value({ (Controller.IsPressed(fieldPort.Key) ? 1 : 0) })");
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
UpdateFramerate();
|
||||
UpdateVideo();
|
||||
UpdateAspect();
|
||||
UpdateInput();
|
||||
}
|
||||
|
||||
private void CheckVersions()
|
||||
{
|
||||
int lengthInBytes;
|
||||
IntPtr ptr = LibMAME.mame_lua_get_string(MAMELuaCommand.GetVersion, out lengthInBytes);
|
||||
string MAMEVersion = Marshal.PtrToStringAnsi(ptr, lengthInBytes);
|
||||
|
||||
if (!LibMAME.mame_lua_free_string(ptr))
|
||||
{
|
||||
Console.WriteLine("LibMAME ERROR: string buffer wasn't freed");
|
||||
}
|
||||
|
||||
string version = this.Attributes().PortedVersion;
|
||||
Debug.Assert(version == MAMEVersion,
|
||||
"MAME versions desync!\n\n" +
|
||||
$"MAME is { MAMEVersion }\n" +
|
||||
$"MAMEHawk is { version }");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Callbacks
|
||||
|
||||
/*
|
||||
* FrameAdvance() and MAME
|
||||
*
|
||||
* MAME fires the periodic callback on every video and debugger update,
|
||||
* which happens every VBlank and also repeatedly at certain time
|
||||
* intervals while paused. Since MAME's luaengine runs in a separate
|
||||
* thread, it's only safe to update everything we need per frame during
|
||||
* this callback, when it's explicitly waiting for further lua commands.
|
||||
*
|
||||
* If we disable throttling and pass -update_in_pause, there will be no
|
||||
* delay between video updates. This allows to run at full speed while
|
||||
* frame-stepping.
|
||||
*
|
||||
* MAME only captures new frame data once per VBlank, while unpaused.
|
||||
* But it doesn't have an exclusive VBlank callback we could attach to.
|
||||
* It has a LUA_ON_FRAME_DONE callback, but that fires even more
|
||||
* frequently and updates all sorts of other non-video stuff, and we
|
||||
* need none of that here.
|
||||
*
|
||||
* So we filter out all the calls that happen while paused (non-VBlank
|
||||
* updates). Then, when Hawk asks us to advance a frame, we virtually
|
||||
* unpause and declare the new frame unfinished. This informs MAME that
|
||||
* it should advance one frame internally. Hawk starts waiting for the
|
||||
* MAME thread to complete the request.
|
||||
*
|
||||
* After MAME's done advancing, it fires the periodic callback again.
|
||||
* That's when we update everything and declare the new frame finished,
|
||||
* filtering out any further updates again. Then we allow Hawk to
|
||||
* complete frame-advancing.
|
||||
*/
|
||||
private void MAMEPeriodicCallback()
|
||||
{
|
||||
if (exiting)
|
||||
{
|
||||
LibMAME.mame_lua_execute(MAMELuaCommand.Exit);
|
||||
exiting = false;
|
||||
}
|
||||
|
||||
int MAMEFrame = LibMAME.mame_lua_get_int(MAMELuaCommand.GetFrameNumber);
|
||||
|
||||
if (!paused)
|
||||
{
|
||||
LibMAME.mame_lua_execute(MAMELuaCommand.Step);
|
||||
frameDone = false;
|
||||
paused = true;
|
||||
}
|
||||
else if (!frameDone)
|
||||
{
|
||||
Update();
|
||||
frameDone = true;
|
||||
MAMEFrameComplete.Set();
|
||||
}
|
||||
}
|
||||
|
||||
private void MAMESoundCallback()
|
||||
{
|
||||
int bytesPerSample = 2;
|
||||
int lengthInBytes;
|
||||
IntPtr ptr = LibMAME.mame_lua_get_string(MAMELuaCommand.GetSamples, out lengthInBytes);
|
||||
|
||||
if (ptr == IntPtr.Zero)
|
||||
{
|
||||
Console.WriteLine("LibMAME ERROR: audio buffer pointer is null");
|
||||
return;
|
||||
}
|
||||
|
||||
numSamples = lengthInBytes / bytesPerSample;
|
||||
|
||||
unsafe
|
||||
{
|
||||
short* pSample = (short*)ptr.ToPointer();
|
||||
for (int i = 0; i < numSamples; i++)
|
||||
{
|
||||
audioSamples.Enqueue(*(pSample + i));
|
||||
}
|
||||
}
|
||||
|
||||
if (!LibMAME.mame_lua_free_string(ptr))
|
||||
{
|
||||
Console.WriteLine("LibMAME ERROR: audio buffer wasn't freed");
|
||||
}
|
||||
}
|
||||
|
||||
private void MAMEBootCallback()
|
||||
{
|
||||
LibMAME.mame_lua_execute(MAMELuaCommand.Pause);
|
||||
CheckVersions();
|
||||
GetInputFields();
|
||||
Update();
|
||||
MAMEStartupComplete.Set();
|
||||
}
|
||||
|
||||
private void MAMELogCallback(LibMAME.OutputChannel channel, int size, string data)
|
||||
{
|
||||
// mame sends osd_output_channel casted to int, we implicitly cast is back
|
||||
if (!data.Contains("pause = "))
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[MAME { channel.ToString() }] " +
|
||||
$"{ data.Replace('\n', ' ') }");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Input
|
||||
|
||||
public static ControllerDefinition MAMEController = new ControllerDefinition
|
||||
{
|
||||
Name = "MAME Controller",
|
||||
BoolButtons = new List<string>()
|
||||
};
|
||||
|
||||
private void GetInputFields()
|
||||
{
|
||||
int lengthInBytes;
|
||||
|
||||
IntPtr ptr = LibMAME.mame_lua_get_string(MAMELuaCommand.GetInputFields, out lengthInBytes);
|
||||
|
||||
if (ptr == IntPtr.Zero)
|
||||
{
|
||||
Console.WriteLine("LibMAME ERROR: string buffer pointer is null");
|
||||
return;
|
||||
}
|
||||
|
||||
string inputFields = Marshal.PtrToStringAnsi(ptr, lengthInBytes);
|
||||
string[] portFields = inputFields.Split(';');
|
||||
MAMEController.BoolButtons.Clear();
|
||||
|
||||
foreach (string portField in portFields)
|
||||
{
|
||||
if (portField != string.Empty)
|
||||
{
|
||||
string[] substrings = portField.Split(',');
|
||||
string tag = substrings.First();
|
||||
string field = substrings.Last();
|
||||
|
||||
fieldsPorts.Add(field, tag);
|
||||
MAMEController.BoolButtons.Add(field);
|
||||
}
|
||||
}
|
||||
|
||||
if (!LibMAME.mame_lua_free_string(ptr))
|
||||
{
|
||||
Console.WriteLine("LibMAME ERROR: string buffer wasn't freed");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lua Commands
|
||||
|
||||
private class MAMELuaCommand
|
||||
{
|
||||
public const string Step = "emu.step()";
|
||||
public const string Pause = "emu.pause()";
|
||||
public const string Unpause = "emu.unpause()";
|
||||
public const string Exit = "manager:machine():exit()";
|
||||
public const string GetVersion = "return emu.app_version()";
|
||||
public const string GetPixels = "return manager:machine():video():pixels()";
|
||||
public const string GetSamples = "return manager:machine():sound():samples()";
|
||||
public const string GetFrameNumber = "return select(2, next(manager:machine().screens)):frame_number()";
|
||||
public const string GetRefresh = "return select(2, next(manager:machine().screens)):refresh_attoseconds()";
|
||||
public const string GetWidth = "return (select(1, manager:machine():video():size()))";
|
||||
public const string GetHeight = "return (select(2, manager:machine():video():size()))";
|
||||
public const string GetBoundX =
|
||||
"local x0,x1,y0,y1 = manager:machine():render():ui_target():view_bounds() " +
|
||||
"return x1-x0";
|
||||
public const string GetBoundY =
|
||||
"local x0,x1,y0,y1 = manager:machine():render():ui_target():view_bounds() " +
|
||||
"return y1-y0";
|
||||
public const string GetInputFields =
|
||||
"final = {} " +
|
||||
"for tag, _ in pairs(manager:machine():ioport().ports) do " +
|
||||
"for name, field in pairs(manager:machine():ioport().ports[tag].fields) do " +
|
||||
"if field.type_class ~= \"dipswitch\" then " +
|
||||
"table.insert(final, string.format(\"%s,%s;\", tag, name)) " +
|
||||
"end " +
|
||||
"end " +
|
||||
"end " +
|
||||
"table.sort(final) " +
|
||||
"return table.concat(final)";
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -103,6 +103,8 @@
|
|||
<Compile Include="..\Version\VersionInfo.cs">
|
||||
<Link>VersionInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Arcades\MAME\LibMAME.cs" />
|
||||
<Compile Include="Arcades\MAME\MAME.cs" />
|
||||
<Compile Include="Calculator\TI83.IEmulator.cs">
|
||||
<DependentUpon>TI83.cs</DependentUpon>
|
||||
</Compile>
|
||||
|
|
Loading…
Reference in New Issue