From 7651f418fe0eb6e717520d275f909beef65b70f7 Mon Sep 17 00:00:00 2001 From: zeromus Date: Fri, 6 Nov 2015 08:31:50 -0600 Subject: [PATCH] usably functional libretro player --- BizHawk.Client.Common/CoreFileProvider.cs | 5 + BizHawk.Client.Common/PathManager.cs | 18 +- BizHawk.Client.Common/RomLoader.cs | 144 ++++-- BizHawk.Client.Common/config/Config.cs | 1 + BizHawk.Client.Common/config/PathEntry.cs | 8 + .../BizHawk.Client.EmuHawk.csproj | 10 + .../Extensions/ToolExtensions.cs | 170 ++++--- BizHawk.Client.EmuHawk/JumpLists.cs | 12 +- BizHawk.Client.EmuHawk/MainForm.Designer.cs | 74 +-- BizHawk.Client.EmuHawk/MainForm.Events.cs | 85 +++- BizHawk.Client.EmuHawk/MainForm.Movie.cs | 2 +- BizHawk.Client.EmuHawk/MainForm.cs | 428 +++++++++++------- BizHawk.Client.EmuHawk/MainForm.resx | 2 +- BizHawk.Client.EmuHawk/OpenAdvanced.cs | 164 +++++++ .../OpenAdvancedChooser.Designer.cs | 183 ++++++++ BizHawk.Client.EmuHawk/OpenAdvancedChooser.cs | 103 +++++ .../OpenAdvancedChooser.resx | 120 +++++ .../ControllerConfigPanel.Designer.cs | 4 +- BizHawk.Common/BizHawk.Common.csproj | 1 + BizHawk.Common/HawkFile.cs | 18 + BizHawk.Common/InstanceDll.cs | 39 +- BizHawk.Common/UnmanagedResourceHeap.cs | 25 + BizHawk.Emulation.Common/CoreComms.cs | 3 + .../Interfaces/ICoreFileProvider.cs | 6 + BizHawk.Emulation.Cores/LibRetro.cs | 75 +-- BizHawk.Emulation.Cores/LibRetroEmulator.cs | 303 ++++++++++--- output/defctrl.json | 26 ++ 27 files changed, 1587 insertions(+), 442 deletions(-) create mode 100644 BizHawk.Client.EmuHawk/OpenAdvanced.cs create mode 100644 BizHawk.Client.EmuHawk/OpenAdvancedChooser.Designer.cs create mode 100644 BizHawk.Client.EmuHawk/OpenAdvancedChooser.cs create mode 100644 BizHawk.Client.EmuHawk/OpenAdvancedChooser.resx create mode 100644 BizHawk.Common/UnmanagedResourceHeap.cs diff --git a/BizHawk.Client.Common/CoreFileProvider.cs b/BizHawk.Client.Common/CoreFileProvider.cs index 5a3c23f987..80911fe4be 100644 --- a/BizHawk.Client.Common/CoreFileProvider.cs +++ b/BizHawk.Client.Common/CoreFileProvider.cs @@ -27,6 +27,11 @@ namespace BizHawk.Client.Common return Path.Combine(PathManager.GetExeDirectoryAbsolute(), "dll"); } + public string GetSaveRAMPath() + { + return PathManager.SaveRamPath(Global.Game); + } + #region EmuLoadHelper api private void FirmwareWarn(string sysID, string firmwareID, bool required, string msg = null) diff --git a/BizHawk.Client.Common/PathManager.cs b/BizHawk.Client.Common/PathManager.cs index 59e96f5337..11d1df49d9 100644 --- a/BizHawk.Client.Common/PathManager.cs +++ b/BizHawk.Client.Common/PathManager.cs @@ -257,6 +257,14 @@ namespace BizHawk.Client.Common public static string FilesystemSafeName(GameInfo game) { var filesystemSafeName = game.Name.Replace("|", "+"); + + // zero 06-nov-2015 - regarding the below, i changed my mind. for libretro i want subdirectories here. + //var parts = filesystemSafeName.Split(System.IO.Path.PathSeparator); + //var dirParts = new string[parts.Length - 1]; + //Array.Copy(parts, dirParts, dirParts.Length); + var filesystemDir = Path.GetDirectoryName(filesystemSafeName); + filesystemSafeName = Path.GetFileName(filesystemSafeName); + filesystemSafeName = RemoveInvalidFileSystemChars(filesystemSafeName); // zero 22-jul-2012 - i dont think this is used the same way it used to. game.Name shouldnt be a path, so this stuff is illogical. @@ -267,10 +275,10 @@ namespace BizHawk.Client.Common // This hack is to prevent annoying things like Super Mario Bros..bk2 if (filesystemSafeName.EndsWith(".")) { - return filesystemSafeName.Remove(filesystemSafeName.Length - 1, 1); + filesystemSafeName = filesystemSafeName.Remove(filesystemSafeName.Length - 1, 1); } - return filesystemSafeName; + return Path.Combine(filesystemDir, filesystemSafeName); } public static string SaveRamPath(GameInfo game) @@ -335,6 +343,12 @@ namespace BizHawk.Client.Common return MakeAbsolutePath(pathEntry.Path, game.System); } + public static string GetPathType(string system, string type) + { + var path = PathManager.GetPathEntryWithFallback(type, system).Path; + return MakeAbsolutePath(path, system); + } + public static string ScreenshotPrefix(GameInfo game) { var name = FilesystemSafeName(game); diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index dfe9b9aa62..115897c67b 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -179,6 +179,8 @@ namespace BizHawk.Client.Common return false; } + public bool AsLibretro; + public bool LoadRom(string path, CoreComm nextComm, bool forceAccurateCore = false, int recursiveCount = 0) // forceAccurateCore is currently just for Quicknes vs Neshawk but could be used for other situations { @@ -199,38 +201,42 @@ namespace BizHawk.Client.Common { var romExtensions = new[] { "SMS", "SMC", "SFC", "PCE", "SGX", "GG", "SG", "BIN", "GEN", "MD", "SMD", "GB", "NES", "FDS", "ROM", "INT", "GBC", "UNF", "A78", "CRT", "COL", "XML", "Z64", "V64", "N64", "WS", "WSC", "GBA" }; - // lets not use this unless we need to - // file.NonArchiveExtensions = romExtensions; - file.Open(path); - - // if the provided file doesnt even exist, give up! - if (!file.Exists) + //only try mounting a file if a filename was given + if (!string.IsNullOrEmpty(path)) { - return false; - } + // lets not use this unless we need to + // file.NonArchiveExtensions = romExtensions; + file.Open(path); - // try binding normal rom extensions first - if (!file.IsBound) - { - file.BindSoleItemOf(romExtensions); - } - - // if we have an archive and need to bind something, then pop the dialog - if (file.IsArchive && !file.IsBound) - { - int? result = HandleArchive(file); - if (result.HasValue) - { - file.BindArchiveMember(result.Value); - } - else + // if the provided file doesnt even exist, give up! + if (!file.Exists) { return false; } - } - // set this here so we can see what file we tried to load even if an error occurs - CanonicalFullPath = file.CanonicalFullPath; + // try binding normal rom extensions first + if (!file.IsBound) + { + file.BindSoleItemOf(romExtensions); + } + + // if we have an archive and need to bind something, then pop the dialog + if (file.IsArchive && !file.IsBound) + { + int? result = HandleArchive(file); + if (result.HasValue) + { + file.BindArchiveMember(result.Value); + } + else + { + return false; + } + } + + // set this here so we can see what file we tried to load even if an error occurs + CanonicalFullPath = file.CanonicalFullPath; + } IEmulator nextEmulator = null; RomGame rom = null; @@ -238,8 +244,79 @@ namespace BizHawk.Client.Common try { - var ext = file.Extension.ToLowerInvariant(); - if (ext == ".m3u") + string ext = null; + + if (AsLibretro) + { + //we'll need to generate a game name for purposes of state/saveram pathing etc. + string gameName; + + string codePathPart = Path.GetFileNameWithoutExtension(nextComm.LaunchLibretroCore); + + var retro = new LibRetroEmulator(nextComm, nextComm.LaunchLibretroCore); + nextEmulator = retro; + + if (retro.EnvironmentInfo.SupportNoGame && string.IsNullOrEmpty(path)) + { + //if we are allowed to run NoGame and we dont have a game, boot up the core that way + bool ret = retro.LoadNoGame(); + if (!ret) + { + DoLoadErrorCallback("LibretroNoGame failed to load. This is weird", "Libretro"); + retro.Dispose(); + return false; + } + + //game name == name of core + gameName = codePathPart; + } + else + { + bool ret; + + //if the core requires an archive file, then try passing the filename of the archive + //(but do we ever need to actually load the contents of the archive file into ram?) + if (retro.system_info.block_extract) + { + if (file.IsArchiveMember) + throw new InvalidOperationException("Should not have bound file member for libretro block_extract core"); + retro.LoadPath(file.FullPathWithoutMember); + } + else + { + //otherwise load the data or pass the filename, as requested. but.. + if (retro.system_info.need_fullpath && file.IsArchiveMember) + throw new InvalidOperationException("Cannot pass archive member to libretro needs_fullpath core"); + + if (retro.system_info.need_fullpath) + ret = retro.LoadPath(file.FullPathWithoutMember); + else + ret = retro.LoadData(file.ReadAllBytes()); + + if (!ret) + { + DoLoadErrorCallback("Libretro failed to load the given file. This is probably due to a core/content mismatch. Moreover, the process is now likely to be hosed. We suggest you restart the program.", "Libretro"); + retro.Dispose(); + return false; + } + + } + + //game name == name of core + extensionless_game_filename + gameName = Path.Combine(codePathPart, Path.GetFileNameWithoutExtension(file.CanonicalName)); + } + + game = new GameInfo { Name = gameName, System = "Libretro" }; + + } + else + { + //if not libretro, do extension checknig + ext = file.Extension.ToLowerInvariant(); + } + + if (string.IsNullOrEmpty(ext)) { } + else if (ext == ".m3u") { //HACK ZONE - currently only psx supports m3u M3U_File m3u; @@ -545,13 +622,13 @@ namespace BizHawk.Client.Common PSF psf = new PSF(); psf.Load(path, cbDeflater); nextEmulator = new Octoshock(nextComm, psf, GetCoreSettings(), GetCoreSyncSettings()); - nextEmulator.CoreComm.RomStatusDetails = "It's a PSF, what do you want."; + nextEmulator.CoreComm.RomStatusDetails = "It's a PSF, what do you want. Oh, tags maybe?"; //total garbage, this rom = new RomGame(file); game = rom.GameInfo; } - else // most extensions + else if(ext != null) // most extensions { rom = new RomGame(file); @@ -689,13 +766,6 @@ namespace BizHawk.Client.Common case "PSX": nextEmulator = new Octoshock(nextComm, null, null, rom.FileData, GetCoreSettings(), GetCoreSyncSettings()); nextEmulator.CoreComm.RomStatusDetails = "PSX etc."; - break; - case "DEBUG": - if (VersionInfo.DeveloperBuild) - { - nextEmulator = LibRetroEmulator.CreateDebug(nextComm, rom.RomData); - } - break; } diff --git a/BizHawk.Client.Common/config/Config.cs b/BizHawk.Client.Common/config/Config.cs index 4f6347c9ab..2e487fe448 100644 --- a/BizHawk.Client.Common/config/Config.cs +++ b/BizHawk.Client.Common/config/Config.cs @@ -470,6 +470,7 @@ namespace BizHawk.Client.Common public bool NES_InQuickNES = true; public bool SNES_InSnes9x = false; public bool GBA_UsemGBA = false; + public string LibretroCore; } // These are used in the defctrl.json or wherever diff --git a/BizHawk.Client.Common/config/PathEntry.cs b/BizHawk.Client.Common/config/PathEntry.cs index 223147d8e6..2f814d4b32 100644 --- a/BizHawk.Client.Common/config/PathEntry.cs +++ b/BizHawk.Client.Common/config/PathEntry.cs @@ -311,6 +311,14 @@ namespace BizHawk.Client.Common new PathEntry { System = "AppleII", SystemDisplayName = "Apple II", Type = "Savestates", Path= Path.Combine(".", "State"), Ordinal = 2 }, new PathEntry { System = "AppleII", SystemDisplayName = "Apple II", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 }, new PathEntry { System = "AppleII", SystemDisplayName = "Apple II", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 }, + + new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Base", Path = Path.Combine(".", "Libretro"), Ordinal = 0 }, + new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Cores", Path = Path.Combine(".", "Cores"), Ordinal = 1 }, + new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 }, + new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 }, + new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 }, + new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 }, + }; } } diff --git a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj index 7214ec93ac..d52da1b6bf 100644 --- a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj +++ b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj @@ -620,6 +620,13 @@ NameStateForm.cs + + + Form + + + OpenAdvancedChooser.cs + Form @@ -1326,6 +1333,9 @@ NameStateForm.cs Designer + + OpenAdvancedChooser.cs + PlatformChooser.cs diff --git a/BizHawk.Client.EmuHawk/Extensions/ToolExtensions.cs b/BizHawk.Client.EmuHawk/Extensions/ToolExtensions.cs index 9d73c88eeb..ab65a75bd4 100644 --- a/BizHawk.Client.EmuHawk/Extensions/ToolExtensions.cs +++ b/BizHawk.Client.EmuHawk/Extensions/ToolExtensions.cs @@ -5,15 +5,16 @@ using System.Windows.Forms; using System.Linq; using BizHawk.Common; - using BizHawk.Emulation.Common; using BizHawk.Client.Common; +//todo - add some more options for libretro types + namespace BizHawk.Client.EmuHawk.ToolExtensions { public static class ToolExtensions { - public static ToolStripItem[] RecentMenu(this RecentFiles recent, Action loadFileCallback, bool autoload = false) + public static ToolStripItem[] RecentMenu(this RecentFiles recent, Action loadFileCallback, bool autoload = false, bool romloading = false) { var items = new List(); @@ -26,97 +27,120 @@ namespace BizHawk.Client.EmuHawk.ToolExtensions { foreach (var filename in recent) { + string caption = filename; + string path = filename; + string physicalPath = filename; + bool crazyStuff = true; + + //sentinel for newer format OpenAdvanced type code + if (romloading) + { + if (filename.StartsWith("*")) + { + var oa = OpenAdvancedSerializer.ParseWithLegacy(filename); + caption = oa.DisplayName; + + crazyStuff = false; + if (oa is OpenAdvanced_OpenRom) + { + crazyStuff = true; + physicalPath = ((oa as OpenAdvanced_OpenRom).Path); + } + } + } + //TODO - do TSMI and TSDD need disposing? yuck - var temp = filename; - var item = new ToolStripMenuItem { Text = temp }; + var item = new ToolStripMenuItem { Text = caption }; items.Add(item); item.Click += (o, ev) => { - loadFileCallback(temp); + loadFileCallback(path); }; - - //TODO - use standard methods to split filename (hawkfile acquire?) - var hf = new HawkFile(); - hf.Parse(temp); - bool canExplore = true; - if (!File.Exists(hf.FullPathWithoutMember)) - canExplore = false; var tsdd = new ToolStripDropDownMenu(); - if (canExplore) + if (crazyStuff) { - //make a menuitem to show the last modified timestamp - var timestamp = File.GetLastWriteTime(hf.FullPathWithoutMember); - var tsmiTimestamp = new ToolStripLabel { Text = timestamp.ToString() }; + //TODO - use standard methods to split filename (hawkfile acquire?) + var hf = new HawkFile(); + hf.Parse(physicalPath); + bool canExplore = true; + if (!File.Exists(hf.FullPathWithoutMember)) + canExplore = false; - tsdd.Items.Add(tsmiTimestamp); - tsdd.Items.Add(new ToolStripSeparator()); - - - - if (hf.IsArchive) + if (canExplore) { - //make a menuitem to let you copy the path - var tsmiCopyCanonicalPath = new ToolStripMenuItem { Text = "&Copy Canonical Path" }; - tsmiCopyCanonicalPath.Click += (o, ev) => { System.Windows.Forms.Clipboard.SetText(temp); }; - tsdd.Items.Add(tsmiCopyCanonicalPath); + //make a menuitem to show the last modified timestamp + var timestamp = File.GetLastWriteTime(hf.FullPathWithoutMember); + var tsmiTimestamp = new ToolStripLabel { Text = timestamp.ToString() }; - var tsmiCopyArchivePath = new ToolStripMenuItem { Text = "Copy Archive Path" }; - tsmiCopyArchivePath.Click += (o, ev) => { System.Windows.Forms.Clipboard.SetText(hf.FullPathWithoutMember); }; - tsdd.Items.Add(tsmiCopyArchivePath); + tsdd.Items.Add(tsmiTimestamp); + tsdd.Items.Add(new ToolStripSeparator()); - var tsmiOpenArchive = new ToolStripMenuItem { Text = "Open &Archive" }; - tsmiOpenArchive.Click += (o, ev) => { System.Diagnostics.Process.Start(hf.FullPathWithoutMember); }; - tsdd.Items.Add(tsmiOpenArchive); + if (hf.IsArchive) + { + //make a menuitem to let you copy the path + var tsmiCopyCanonicalPath = new ToolStripMenuItem { Text = "&Copy Canonical Path" }; + tsmiCopyCanonicalPath.Click += (o, ev) => { System.Windows.Forms.Clipboard.SetText(physicalPath); }; + tsdd.Items.Add(tsmiCopyCanonicalPath); + + var tsmiCopyArchivePath = new ToolStripMenuItem { Text = "Copy Archive Path" }; + tsmiCopyArchivePath.Click += (o, ev) => { System.Windows.Forms.Clipboard.SetText(hf.FullPathWithoutMember); }; + tsdd.Items.Add(tsmiCopyArchivePath); + + var tsmiOpenArchive = new ToolStripMenuItem { Text = "Open &Archive" }; + tsmiOpenArchive.Click += (o, ev) => { System.Diagnostics.Process.Start(hf.FullPathWithoutMember); }; + tsdd.Items.Add(tsmiOpenArchive); + } + else + { + //make a menuitem to let you copy the path + var tsmiCopyPath = new ToolStripMenuItem { Text = "&Copy Path" }; + tsmiCopyPath.Click += (o, ev) => { System.Windows.Forms.Clipboard.SetText(physicalPath); }; + tsdd.Items.Add(tsmiCopyPath); + } + + tsdd.Items.Add(new ToolStripSeparator()); + + //make a menuitem to let you explore to it + var tsmiExplore = new ToolStripMenuItem { Text = "&Explore" }; + string explorePath = "\"" + hf.FullPathWithoutMember + "\""; + tsmiExplore.Click += (o, ev) => { System.Diagnostics.Process.Start("explorer.exe", "/select, " + explorePath); }; + tsdd.Items.Add(tsmiExplore); + + var tsmiCopyFile = new ToolStripMenuItem { Text = "Copy &File" }; + var lame = new System.Collections.Specialized.StringCollection(); + lame.Add(hf.FullPathWithoutMember); + tsmiCopyFile.Click += (o, ev) => { System.Windows.Forms.Clipboard.SetFileDropList(lame); }; + tsdd.Items.Add(tsmiCopyFile); + + var tsmiTest = new ToolStripMenuItem { Text = "&Shell Context Menu" }; + tsmiTest.Click += (o, ev) => + { + var si = new GongSolutions.Shell.ShellItem(hf.FullPathWithoutMember); + var scm = new GongSolutions.Shell.ShellContextMenu(si); + var tsddi = o as ToolStripDropDownItem; + tsddi.Owner.Update(); + scm.ShowContextMenu(tsddi.Owner, new System.Drawing.Point(0, 0)); + }; + tsdd.Items.Add(tsmiTest); + + tsdd.Items.Add(new ToolStripSeparator()); } else { - //make a menuitem to let you copy the path - var tsmiCopyPath = new ToolStripMenuItem { Text = "&Copy Path" }; - tsmiCopyPath.Click += (o, ev) => { System.Windows.Forms.Clipboard.SetText(temp); }; - tsdd.Items.Add(tsmiCopyPath); + //make a menuitem to show the last modified timestamp + var tsmiMissingFile = new ToolStripLabel { Text = "-Missing-" }; + tsdd.Items.Add(tsmiMissingFile); + tsdd.Items.Add(new ToolStripSeparator()); } - tsdd.Items.Add(new ToolStripSeparator()); + } //crazystuff - //make a menuitem to let you explore to it - var tsmiExplore = new ToolStripMenuItem { Text = "&Explore" }; - string explorePath = "\"" + hf.FullPathWithoutMember + "\""; - tsmiExplore.Click += (o, ev) => { System.Diagnostics.Process.Start("explorer.exe", "/select, " + explorePath); }; - tsdd.Items.Add(tsmiExplore); - - var tsmiCopyFile = new ToolStripMenuItem { Text = "Copy &File" }; - var lame = new System.Collections.Specialized.StringCollection(); - lame.Add(hf.FullPathWithoutMember); - tsmiCopyFile.Click += (o, ev) => { System.Windows.Forms.Clipboard.SetFileDropList(lame); }; - tsdd.Items.Add(tsmiCopyFile); - - var tsmiTest = new ToolStripMenuItem { Text = "&Shell Context Menu" }; - tsmiTest.Click += (o, ev) => { - var si = new GongSolutions.Shell.ShellItem(hf.FullPathWithoutMember); - var scm = new GongSolutions.Shell.ShellContextMenu(si); - var tsddi = o as ToolStripDropDownItem; - tsddi.Owner.Update(); - scm.ShowContextMenu(tsddi.Owner, new System.Drawing.Point(0, 0)); - }; - tsdd.Items.Add(tsmiTest); - - tsdd.Items.Add(new ToolStripSeparator()); - } - else - { - //make a menuitem to show the last modified timestamp - var tsmiMissingFile = new ToolStripLabel { Text = "-Missing-" }; - tsdd.Items.Add(tsmiMissingFile); - tsdd.Items.Add(new ToolStripSeparator()); - } - - //in either case, make a menuitem to let you remove the path + //in any case, make a menuitem to let you remove the item var tsmiRemovePath = new ToolStripMenuItem { Text = "&Remove" }; - tsmiRemovePath.Click += (o, ev) => { recent.Remove(temp); }; - + tsmiRemovePath.Click += (o, ev) => { recent.Remove(path); }; tsdd.Items.Add(tsmiRemovePath); ////experiment of popping open a submenu. doesnt work well. @@ -134,7 +158,7 @@ namespace BizHawk.Client.EmuHawk.ToolExtensions // tsdd.Show(pos); //}; - //just add it to the submenu for now + //just add it to the submenu for now. seems to work well enough, even though its a bit odd item.MouseDown += (o, mev) => { if (mev.Button != MouseButtons.Right) return; diff --git a/BizHawk.Client.EmuHawk/JumpLists.cs b/BizHawk.Client.EmuHawk/JumpLists.cs index a51e65d238..41efac51ca 100644 --- a/BizHawk.Client.EmuHawk/JumpLists.cs +++ b/BizHawk.Client.EmuHawk/JumpLists.cs @@ -24,13 +24,13 @@ namespace BizHawk.Client.EmuHawk /// add an item to the W7+ jumplist /// /// fully qualified path, can include '|' character for archives - public static void AddRecentItem(string fullpath) + public static void AddRecentItem(string fullpath, string title) { - string title; - if (fullpath.Contains('|')) - title = fullpath.Split('|')[1]; - else - title = Path.GetFileName(fullpath); + //string title; + //if (fullpath.Contains('|')) + // title = fullpath.Split('|')[1]; + //else + // title = Path.GetFileName(fullpath); string exepath = Assembly.GetEntryAssembly().Location; diff --git a/BizHawk.Client.EmuHawk/MainForm.Designer.cs b/BizHawk.Client.EmuHawk/MainForm.Designer.cs index 9b105d4e18..1eec544e38 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Designer.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Designer.cs @@ -186,6 +186,7 @@ this.gBAWithMGBAToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator8 = new System.Windows.Forms.ToolStripSeparator(); this.N64VideoPluginSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.setLibretroCoreToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator10 = new System.Windows.Forms.ToolStripSeparator(); this.SaveConfigMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.LoadConfigMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -405,6 +406,7 @@ this.ShowMenuContextMenuSeparator = new System.Windows.Forms.ToolStripSeparator(); this.ShowMenuContextMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.timerMouseIdle = new System.Windows.Forms.Timer(this.components); + this.OpenAdvancedMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.MainformMenu.SuspendLayout(); this.MainStatusBar.SuspendLayout(); this.MainFormContextMenu.SuspendLayout(); @@ -452,6 +454,7 @@ this.FileSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.OpenRomMenuItem, this.RecentRomSubMenu, + this.OpenAdvancedMenuItem, this.CloseRomMenuItem, this.toolStripMenuItem1, this.SaveStateSubMenu, @@ -473,8 +476,8 @@ // this.OpenRomMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.OpenFile; this.OpenRomMenuItem.Name = "OpenRomMenuItem"; - this.OpenRomMenuItem.Size = new System.Drawing.Size(134, 22); - this.OpenRomMenuItem.Text = "Open ROM"; + this.OpenRomMenuItem.Size = new System.Drawing.Size(152, 22); + this.OpenRomMenuItem.Text = "&Open ROM"; this.OpenRomMenuItem.Click += new System.EventHandler(this.OpenRomMenuItem_Click); // // RecentRomSubMenu @@ -483,27 +486,27 @@ this.toolStripSeparator3}); this.RecentRomSubMenu.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.Recent; this.RecentRomSubMenu.Name = "RecentRomSubMenu"; - this.RecentRomSubMenu.Size = new System.Drawing.Size(134, 22); - this.RecentRomSubMenu.Text = "Recent ROM"; + this.RecentRomSubMenu.Size = new System.Drawing.Size(152, 22); + this.RecentRomSubMenu.Text = "&Recent ROM"; this.RecentRomSubMenu.DropDownOpened += new System.EventHandler(this.RecentRomMenuItem_DropDownOpened); // // toolStripSeparator3 // this.toolStripSeparator3.Name = "toolStripSeparator3"; - this.toolStripSeparator3.Size = new System.Drawing.Size(57, 6); + this.toolStripSeparator3.Size = new System.Drawing.Size(149, 6); // // CloseRomMenuItem // this.CloseRomMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.Close; this.CloseRomMenuItem.Name = "CloseRomMenuItem"; - this.CloseRomMenuItem.Size = new System.Drawing.Size(134, 22); + this.CloseRomMenuItem.Size = new System.Drawing.Size(152, 22); this.CloseRomMenuItem.Text = "&Close ROM"; this.CloseRomMenuItem.Click += new System.EventHandler(this.CloseRomMenuItem_Click); // // toolStripMenuItem1 // this.toolStripMenuItem1.Name = "toolStripMenuItem1"; - this.toolStripMenuItem1.Size = new System.Drawing.Size(131, 6); + this.toolStripMenuItem1.Size = new System.Drawing.Size(149, 6); // // SaveStateSubMenu // @@ -521,8 +524,8 @@ this.toolStripSeparator6, this.SaveNamedStateMenuItem}); this.SaveStateSubMenu.Name = "SaveStateSubMenu"; - this.SaveStateSubMenu.Size = new System.Drawing.Size(134, 22); - this.SaveStateSubMenu.Text = "Save State"; + this.SaveStateSubMenu.Size = new System.Drawing.Size(152, 22); + this.SaveStateSubMenu.Text = "&Save State"; this.SaveStateSubMenu.DropDownOpened += new System.EventHandler(this.SaveStateSubMenu_DropDownOpened); // // SaveState1MenuItem @@ -625,8 +628,8 @@ this.toolStripSeparator21, this.AutoloadLastSlotMenuItem}); this.LoadStateSubMenu.Name = "LoadStateSubMenu"; - this.LoadStateSubMenu.Size = new System.Drawing.Size(134, 22); - this.LoadStateSubMenu.Text = "Load State"; + this.LoadStateSubMenu.Size = new System.Drawing.Size(152, 22); + this.LoadStateSubMenu.Text = "&Load State"; this.LoadStateSubMenu.DropDownOpened += new System.EventHandler(this.LoadStateSubMenu_DropDownOpened); // // LoadState1MenuItem @@ -742,8 +745,8 @@ this.SaveToCurrentSlotMenuItem, this.LoadCurrentSlotMenuItem}); this.SaveSlotSubMenu.Name = "SaveSlotSubMenu"; - this.SaveSlotSubMenu.Size = new System.Drawing.Size(134, 22); - this.SaveSlotSubMenu.Text = "Save Slot"; + this.SaveSlotSubMenu.Size = new System.Drawing.Size(152, 22); + this.SaveSlotSubMenu.Text = "Save S&lot"; this.SaveSlotSubMenu.DropDownOpened += new System.EventHandler(this.SaveSlotSubMenu_DropDownOpened); // // SelectSlot0MenuItem @@ -857,21 +860,21 @@ this.FlushSaveRAMMenuItem}); this.SaveRAMSubMenu.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Bold); this.SaveRAMSubMenu.Name = "SaveRAMSubMenu"; - this.SaveRAMSubMenu.Size = new System.Drawing.Size(134, 22); - this.SaveRAMSubMenu.Text = "Save RAM"; + this.SaveRAMSubMenu.Size = new System.Drawing.Size(152, 22); + this.SaveRAMSubMenu.Text = "Save &RAM"; // // FlushSaveRAMMenuItem // this.FlushSaveRAMMenuItem.Font = new System.Drawing.Font("Tahoma", 8.25F); this.FlushSaveRAMMenuItem.Name = "FlushSaveRAMMenuItem"; - this.FlushSaveRAMMenuItem.Size = new System.Drawing.Size(150, 22); + this.FlushSaveRAMMenuItem.Size = new System.Drawing.Size(152, 22); this.FlushSaveRAMMenuItem.Text = "&Flush Save Ram"; this.FlushSaveRAMMenuItem.Click += new System.EventHandler(this.FlushSaveRAMMenuItem_Click); // // toolStripMenuItem2 // this.toolStripMenuItem2.Name = "toolStripMenuItem2"; - this.toolStripMenuItem2.Size = new System.Drawing.Size(131, 6); + this.toolStripMenuItem2.Size = new System.Drawing.Size(149, 6); // // MovieSubMenu // @@ -891,8 +894,8 @@ this.FullMovieLoadstatesMenuItem, this.MovieEndSubMenu}); this.MovieSubMenu.Name = "MovieSubMenu"; - this.MovieSubMenu.Size = new System.Drawing.Size(134, 22); - this.MovieSubMenu.Text = "Movie"; + this.MovieSubMenu.Size = new System.Drawing.Size(152, 22); + this.MovieSubMenu.Text = "&Movie"; this.MovieSubMenu.DropDownOpened += new System.EventHandler(this.MovieSubMenu_DropDownOpened); // // ReadonlyMenuItem @@ -1047,8 +1050,8 @@ this.CaptureOSDMenuItem, this.SynclessRecordingMenuItem}); this.AVSubMenu.Name = "AVSubMenu"; - this.AVSubMenu.Size = new System.Drawing.Size(134, 22); - this.AVSubMenu.Text = "AVI/WAV"; + this.AVSubMenu.Size = new System.Drawing.Size(152, 22); + this.AVSubMenu.Text = "&AVI/WAV"; this.AVSubMenu.DropDownOpened += new System.EventHandler(this.AVSubMenu_DropDownOpened); // // RecordAVMenuItem @@ -1096,8 +1099,8 @@ this.toolStripSeparator20, this.ScreenshotCaptureOSDMenuItem1}); this.ScreenshotSubMenu.Name = "ScreenshotSubMenu"; - this.ScreenshotSubMenu.Size = new System.Drawing.Size(134, 22); - this.ScreenshotSubMenu.Text = "Screenshot"; + this.ScreenshotSubMenu.Size = new System.Drawing.Size(152, 22); + this.ScreenshotSubMenu.Text = "Scree&nshot"; this.ScreenshotSubMenu.DropDownOpening += new System.EventHandler(this.ScreenshotSubMenu_DropDownOpening); // // ScreenshotMenuItem @@ -1147,14 +1150,14 @@ // toolStripSeparator4 // this.toolStripSeparator4.Name = "toolStripSeparator4"; - this.toolStripSeparator4.Size = new System.Drawing.Size(131, 6); + this.toolStripSeparator4.Size = new System.Drawing.Size(149, 6); // // ExitMenuItem // this.ExitMenuItem.Name = "ExitMenuItem"; this.ExitMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Alt | System.Windows.Forms.Keys.F4))); - this.ExitMenuItem.Size = new System.Drawing.Size(134, 22); - this.ExitMenuItem.Text = "Exit"; + this.ExitMenuItem.Size = new System.Drawing.Size(152, 22); + this.ExitMenuItem.Text = "E&xit"; this.ExitMenuItem.Click += new System.EventHandler(this.ExitMenuItem_Click); // // EmulationSubMenu @@ -1739,7 +1742,8 @@ this.SnesWithSnes9xMenuItem, this.gBAWithMGBAToolStripMenuItem, this.toolStripSeparator8, - this.N64VideoPluginSettingsMenuItem}); + this.N64VideoPluginSettingsMenuItem, + this.setLibretroCoreToolStripMenuItem}); this.CoresSubMenu.Name = "CoresSubMenu"; this.CoresSubMenu.Size = new System.Drawing.Size(165, 22); this.CoresSubMenu.Text = "Cores"; @@ -1786,6 +1790,13 @@ this.N64VideoPluginSettingsMenuItem.Text = "N64 Video Plugin Settings"; this.N64VideoPluginSettingsMenuItem.Click += new System.EventHandler(this.N64VideoPluginSettingsMenuItem_Click); // + // setLibretroCoreToolStripMenuItem + // + this.setLibretroCoreToolStripMenuItem.Name = "setLibretroCoreToolStripMenuItem"; + this.setLibretroCoreToolStripMenuItem.Size = new System.Drawing.Size(195, 22); + this.setLibretroCoreToolStripMenuItem.Text = "Set Libretro Core"; + this.setLibretroCoreToolStripMenuItem.Click += new System.EventHandler(this.setLibretroCoreToolStripMenuItem_Click); + // // toolStripSeparator10 // this.toolStripSeparator10.Name = "toolStripSeparator10"; @@ -3578,6 +3589,13 @@ this.timerMouseIdle.Interval = 2000; this.timerMouseIdle.Tick += new System.EventHandler(this.timerMouseIdle_Tick); // + // OpenAdvancedMenuItem + // + this.OpenAdvancedMenuItem.Name = "OpenAdvancedMenuItem"; + this.OpenAdvancedMenuItem.Size = new System.Drawing.Size(152, 22); + this.OpenAdvancedMenuItem.Text = "Open Ad&vanced"; + this.OpenAdvancedMenuItem.Click += new System.EventHandler(this.OpenAdvancedMenuItem_Click); + // // MainForm // this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; @@ -3994,6 +4012,8 @@ private System.Windows.Forms.ToolStripMenuItem C64SubMenu; private System.Windows.Forms.ToolStripMenuItem C64SettingsMenuItem; private System.Windows.Forms.ToolStripMenuItem CodeDataLoggerMenuItem; + private System.Windows.Forms.ToolStripMenuItem setLibretroCoreToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem OpenAdvancedMenuItem; } } diff --git a/BizHawk.Client.EmuHawk/MainForm.Events.cs b/BizHawk.Client.EmuHawk/MainForm.Events.cs index 6e8cf1e6d3..8f8281991c 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Events.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Events.cs @@ -58,7 +58,7 @@ namespace BizHawk.Client.EmuHawk { RecentRomSubMenu.DropDownItems.Clear(); RecentRomSubMenu.DropDownItems.AddRange( - Global.Config.RecentRoms.RecentMenu(LoadRomFromRecent, true)); + Global.Config.RecentRoms.RecentMenu(LoadRomFromRecent, true, true)); } private void SaveStateSubMenu_DropDownOpened(object sender, EventArgs e) @@ -300,6 +300,56 @@ namespace BizHawk.Client.EmuHawk OpenRom(); } + private void OpenAdvancedMenuItem_Click(object sender, EventArgs e) + { + var oac = new OpenAdvancedChooser(this); + if (oac.ShowHawkDialog() == System.Windows.Forms.DialogResult.Cancel) + return; + + if (oac.Result == OpenAdvancedChooser.Command.RetroLaunchNoGame) + { + var argsNoGame = new LoadRomArgs(); + argsNoGame.OpenAdvanced = new OpenAdvanced_LibretroNoGame(Global.Config.LibretroCore); + LoadRom("", argsNoGame); + return; + } + + var args = new LoadRomArgs(); + + if (oac.Result == OpenAdvancedChooser.Command.RetroLaunchGame) + args.OpenAdvanced = new OpenAdvanced_Libretro(); + else if (oac.Result == OpenAdvancedChooser.Command.ClassicLaunchGame) + args.OpenAdvanced = new OpenAdvanced_OpenRom(); + else throw new InvalidOperationException("Automatic Alpha Sanitizer"); + + + //----------------- + //CLONE OF CODE FROM OpenRom (mostly) + var ofd = new OpenFileDialog + { + InitialDirectory = PathManager.GetRomsPath(Global.Emulator.SystemId), + Filter = RomFilter, + RestoreDirectory = false, + FilterIndex = _lastOpenRomFilter, + Title = "Open Advanced" + }; + + var result = ofd.ShowHawkDialog(); + if (result != DialogResult.OK) + { + return; + } + + var file = new FileInfo(ofd.FileName); + Global.Config.LastRomPath = file.DirectoryName; + _lastOpenRomFilter = ofd.FilterIndex; + //----------------- + + + + LoadRom(file.FullName, args); + } + private void CloseRomMenuItem_Click(object sender, EventArgs e) { CloseRom(); @@ -1130,6 +1180,34 @@ namespace BizHawk.Client.EmuHawk ThrottleMessage(); } + public void RunLibretroCoreChooser() + { + var ofd = new OpenFileDialog(); + + if (Global.Config.LibretroCore != null) + { + ofd.FileName = Path.GetFileName(Global.Config.LibretroCore); + ofd.InitialDirectory = Path.GetDirectoryName(Global.Config.LibretroCore); + } + else + { + ofd.InitialDirectory = PathManager.GetPathType("Libretro", "Cores"); + } + + ofd.RestoreDirectory = true; + ofd.Filter = "Libretro Cores (*.dll)|*.dll"; + + if (ofd.ShowDialog() == DialogResult.Cancel) + return; + + Global.Config.LibretroCore = ofd.FileName; + } + + private void setLibretroCoreToolStripMenuItem_Click(object sender, EventArgs e) + { + RunLibretroCoreChooser(); + } + #endregion #region Tools @@ -1202,6 +1280,11 @@ namespace BizHawk.Client.EmuHawk private void TAStudioMenuItem_Click(object sender, EventArgs e) { + if (!Global.Emulator.CanPollInput()) + { + MessageBox.Show("Current core does not support input polling. TAStudio can't be used."); + return; + } GlobalWin.Tools.Load(); } diff --git a/BizHawk.Client.EmuHawk/MainForm.Movie.cs b/BizHawk.Client.EmuHawk/MainForm.Movie.cs index 5843066a6a..390a607876 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Movie.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Movie.cs @@ -41,7 +41,7 @@ namespace BizHawk.Client.EmuHawk return false; } - LoadRom(CurrentlyOpenRom); + RebootCore(); if (Global.MovieSession.PreviousNES_InQuickNES.HasValue) { diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index ce72caba0a..f9ee25dd1e 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -408,11 +408,11 @@ namespace BizHawk.Client.EmuHawk ToggleFullscreen(); } - if(!Global.Game.IsNullInstance) + if (!Global.Game.IsNullInstance) { - if(cmdLoadState != null) + if (cmdLoadState != null) { - LoadState(cmdLoadState,Path.GetFileName(cmdLoadState)); + LoadState(cmdLoadState, Path.GetFileName(cmdLoadState)); } else if (cmdLoadSlot != null) { @@ -540,7 +540,7 @@ namespace BizHawk.Client.EmuHawk protected override void Dispose(bool disposing) { //NOTE: this gets called twice sometimes. once by using() in Program.cs and once from winforms internals when the form is closed... - + if (GlobalWin.DisplayManager != null) { GlobalWin.DisplayManager.Dispose(); @@ -594,7 +594,8 @@ namespace BizHawk.Client.EmuHawk #region Properties - public string CurrentlyOpenRom; + public string CurrentlyOpenRom; //todo - delete me and use only args instead + LoadRomArgs CurrentlyOpenRomArgs; public bool PauseAVI = false; public bool PressFrameAdvance = false; public bool PressRewind = false; @@ -820,21 +821,25 @@ namespace BizHawk.Client.EmuHawk float x = P.X / (float)video.BufferWidth; return new Tuple("WMouse X", x * 20000 - 10000); } - + if (o.Item1 == "WMouse Y") { var P = GlobalWin.DisplayManager.UntransformPoint(new Point(0, (int)o.Item2)); float y = P.Y / (float)video.BufferHeight; return new Tuple("WMouse Y", y * 20000 - 10000); } - + return o; })); } public void RebootCore() { - LoadRom(CurrentlyOpenRom); + var ioa = OpenAdvancedSerializer.ParseWithLegacy(CurrentlyOpenRom); + if (ioa is IOpenAdvancedLibretro) + LoadRom("", CurrentlyOpenRomArgs); + else + LoadRom(ioa.SimplePath, CurrentlyOpenRomArgs); } public void PauseEmulator() @@ -877,7 +882,7 @@ namespace BizHawk.Client.EmuHawk { using (var bb = Global.Config.Screenshot_CaptureOSD ? CaptureOSD() : MakeScreenshotImage()) { - using(var img = bb.ToSysdrawingBitmap()) + using (var img = bb.ToSysdrawingBitmap()) Clipboard.SetImage(img); } @@ -905,8 +910,8 @@ namespace BizHawk.Client.EmuHawk { var sequence = string.Format(" ({0})", seq++); fname = string.Format(fmt, prefix, ts, sequence); - } - + } + TakeScreenshot(fname); } @@ -1005,7 +1010,7 @@ namespace BizHawk.Client.EmuHawk } } - public void ToggleFullscreen(bool allowSuppress=false) + public void ToggleFullscreen(bool allowSuppress = false) { AutohideCursor(false); @@ -1019,23 +1024,23 @@ namespace BizHawk.Client.EmuHawk if (_inFullscreen == false) { SuspendLayout(); - #if WINDOWS - //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) - //AMD cards manifest a problem under these circumstances, flickering other monitors. - //It isnt clear whether nvidia cards are failing to employ this optimization, or just not flickering. - //(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 - //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) - { - //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. - //Maybe we could add another control under the statusbar. with a different backcolor - Padding = new Padding(1); - BackColor = Color.Black; - } - #endif +#if WINDOWS + //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) + //AMD cards manifest a problem under these circumstances, flickering other monitors. + //It isnt clear whether nvidia cards are failing to employ this optimization, or just not flickering. + //(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 + //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) + { + //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. + //Maybe we could add another control under the statusbar. with a different backcolor + Padding = new Padding(1); + BackColor = Color.Black; + } +#endif _windowedLocation = Location; @@ -1052,16 +1057,16 @@ namespace BizHawk.Client.EmuHawk WindowState = FormWindowState.Normal; - #if WINDOWS - //do this even if DispFullscreenHacks arent enabled, to restore it in case it changed underneath us or something - Padding = new Padding(0); - //it's important that we set the form color back to this, because the statusbar icons blend onto the mainform, not onto the statusbar-- - //so we need the statusbar and mainform backdrop color to match - BackColor = SystemColors.Control; - #endif +#if WINDOWS + //do this even if DispFullscreenHacks arent enabled, to restore it in case it changed underneath us or something + Padding = new Padding(0); + //it's important that we set the form color back to this, because the statusbar icons blend onto the mainform, not onto the statusbar-- + //so we need the statusbar and mainform backdrop color to match + BackColor = SystemColors.Control; +#endif _inFullscreen = false; - + SynchChrome(); Location = _windowedLocation; ResumeLayout(); @@ -1110,7 +1115,7 @@ namespace BizHawk.Client.EmuHawk { string ttype = ":(none)"; if (Global.Config.SoundThrottle) { ttype = ":Sound"; } - if (Global.Config.VSyncThrottle) { ttype = string.Format(":Vsync{0}", Global.Config.VSync?"[ena]":"[dis]"); } + if (Global.Config.VSyncThrottle) { ttype = string.Format(":Vsync{0}", Global.Config.VSync ? "[ena]" : "[dis]"); } if (Global.Config.ClockThrottle) { ttype = ":Clock"; } string xtype = _unthrottled ? "Unthrottled" : "Throttled"; string msg = string.Format("{0}{1} ", xtype, ttype); @@ -1734,7 +1739,18 @@ namespace BizHawk.Client.EmuHawk private void LoadRomFromRecent(string rom) { - if (!LoadRom(rom)) + + var ioa = OpenAdvancedSerializer.ParseWithLegacy(rom); + + LoadRomArgs args = new LoadRomArgs() + { + OpenAdvanced = ioa + }; + + //if(ioa is this or that) - for more complex behaviour + rom = ioa.SimplePath; + + if (!LoadRom(rom, args)) { Global.Config.RecentRoms.HandleLoadError(rom); } @@ -1931,8 +1947,8 @@ namespace BizHawk.Client.EmuHawk //private Size _lastVideoSize = new Size(-1, -1), _lastVirtualSize = new Size(-1, -1); var video = Global.Emulator.VideoProvider(); //bool change = false; - Size currVideoSize = new Size(video.BufferWidth,video.BufferHeight); - Size currVirtualSize = new Size(video.VirtualWidth,video.VirtualHeight); + Size currVideoSize = new Size(video.BufferWidth, video.BufferHeight); + Size currVirtualSize = new Size(video.VirtualWidth, video.VirtualHeight); if (currVideoSize != _lastVideoSize || currVirtualSize != _lastVirtualSize) { _lastVideoSize = currVideoSize; @@ -2060,7 +2076,9 @@ namespace BizHawk.Client.EmuHawk var file = new FileInfo(ofd.FileName); Global.Config.LastRomPath = file.DirectoryName; _lastOpenRomFilter = ofd.FilterIndex; - LoadRom(file.FullName); + + var lra = new LoadRomArgs { OpenAdvanced = new OpenAdvanced_OpenRom { Path = file.FullName } }; + LoadRom(file.FullName, lra); } private void CoreSyncSettings(object sender, RomLoader.SettingsLoadArgs e) @@ -3387,7 +3405,7 @@ namespace BizHawk.Client.EmuHawk // Retry loading the ROM here. This leads to recursion, as the original call to LoadRom has not exited yet, // but unless the user tries and fails to set his firmware a lot of times, nothing should happen. // Refer to how RomLoader implemented its LoadRom method for a potential fix on this. - LoadRom(e.RomPath, e.Deterministic); + LoadRom(e.RomPath, CurrentLoadRomArgs); } } } @@ -3419,170 +3437,233 @@ namespace BizHawk.Client.EmuHawk return platformChooser.PlatformChoice; } - // Still needs a good bit of refactoring - public bool LoadRom(string path, bool? deterministicemulation = null) + public class LoadRomArgs { - // If deterministic emulation is passed in, respect that value regardless, else determine a good value (currently that simply means movies require deterministic emulaton) - bool deterministic = deterministicemulation.HasValue ? - deterministicemulation.Value : - Global.MovieSession.QueuedMovie != null; - //Global.MovieSession.Movie.IsActive; - - if (!GlobalWin.Tools.AskSave()) + public bool? Deterministic; + public IOpenAdvanced OpenAdvanced; + } + + LoadRomArgs CurrentLoadRomArgs; + + // Still needs a good bit of refactoring + public bool LoadRom(string path, LoadRomArgs args = null) + { + //default args + if (args == null) args = new LoadRomArgs(); + + //if this is the first call to LoadRom (they will come in recursively) then stash the args + bool firstCall = false; + if (CurrentLoadRomArgs == null) { - return false; + firstCall = true; + CurrentLoadRomArgs = args; + } + else + { + args = CurrentLoadRomArgs; } - var loader = new RomLoader + try + { + // If deterministic emulation is passed in, respect that value regardless, else determine a good value (currently that simply means movies require deterministic emulaton) + bool deterministic = args.Deterministic.HasValue ? + args.Deterministic.Value : + Global.MovieSession.QueuedMovie != null; + //Global.MovieSession.Movie.IsActive; + + if (!GlobalWin.Tools.AskSave()) + { + return false; + } + + bool asLibretro = (args.OpenAdvanced is OpenAdvanced_Libretro || args.OpenAdvanced is OpenAdvanced_LibretroNoGame); + + var loader = new RomLoader { ChooseArchive = LoadArhiveChooser, ChoosePlatform = ChoosePlatformForRom, Deterministic = deterministic, - MessageCallback = GlobalWin.OSD.AddMessage + MessageCallback = GlobalWin.OSD.AddMessage, + AsLibretro = asLibretro }; - Global.FirmwareManager.RecentlyServed.Clear(); + Global.FirmwareManager.RecentlyServed.Clear(); - loader.OnLoadError += ShowLoadError; - loader.OnLoadSettings += CoreSettings; - loader.OnLoadSyncSettings += CoreSyncSettings; + loader.OnLoadError += ShowLoadError; + loader.OnLoadSettings += CoreSettings; + loader.OnLoadSyncSettings += CoreSyncSettings; - // this also happens in CloseGame(). but it needs to happen here since if we're restarting with the same core, - // any settings changes that we made need to make it back to config before we try to instantiate that core with - // the new settings objects - CommitCoreSettingsToConfig(); // adelikat: I Think by reordering things, this isn't necessary anymore - CloseGame(); - - var nextComm = CreateCoreComm(); - CoreFileProvider.SyncCoreCommInputSignals(nextComm); - var result = loader.LoadRom(path, nextComm); + // this also happens in CloseGame(). but it needs to happen here since if we're restarting with the same core, + // any settings changes that we made need to make it back to config before we try to instantiate that core with + // the new settings objects + CommitCoreSettingsToConfig(); // adelikat: I Think by reordering things, this isn't necessary anymore + CloseGame(); + + var nextComm = CreateCoreComm(); + + //we need to inform LoadRom which Libretro core to use... + IOpenAdvanced ioa = args.OpenAdvanced; + if (ioa is IOpenAdvancedLibretro) + { + var ioaretro = ioa as IOpenAdvancedLibretro; + + //prepare a core specification + //if it wasnt already specified, use the current default + if (ioaretro.CorePath == null) ioaretro.CorePath = Global.Config.LibretroCore; + nextComm.LaunchLibretroCore = ioaretro.CorePath; + if (nextComm.LaunchLibretroCore == null) + throw new InvalidOperationException("Can't load a file via Libretro until a core is specified"); + } - if (result) - { - Global.Emulator = loader.LoadedEmulator; - Global.Game = loader.Game; CoreFileProvider.SyncCoreCommInputSignals(nextComm); - InputManager.SyncControls(); + var result = loader.LoadRom(path, nextComm); - if (Global.Emulator is TI83 && Global.Config.TI83autoloadKeyPad) + //we need to replace the path in the OpenAdvanced with the canonical one the user chose. + //It can't be done until loder.LoadRom happens (for CanonicalFullPath) + //i'm not sure this needs to be more abstractly engineered yet until we have more OpenAdvanced examples + if (ioa is OpenAdvanced_Libretro) { - GlobalWin.Tools.Load(); + var oaretro = ioa as OpenAdvanced_Libretro; + oaretro.token.Path = loader.CanonicalFullPath; } + if (ioa is OpenAdvanced_OpenRom) ((OpenAdvanced_OpenRom)ioa).Path = loader.CanonicalFullPath; + string loaderName = "*" + OpenAdvancedSerializer.Serialize(ioa); - if (loader.LoadedEmulator is NES) + if (result) { - var nes = loader.LoadedEmulator as NES; - if (!string.IsNullOrWhiteSpace(nes.GameName)) + Global.Emulator = loader.LoadedEmulator; + Global.Game = loader.Game; + CoreFileProvider.SyncCoreCommInputSignals(nextComm); + InputManager.SyncControls(); + + if (Global.Emulator is TI83 && Global.Config.TI83autoloadKeyPad) { - Global.Game.Name = nes.GameName; + GlobalWin.Tools.Load(); } - Global.Game.Status = nes.RomStatus; - } - else if (loader.LoadedEmulator is QuickNES) - { - var qns = loader.LoadedEmulator as QuickNES; - if (!string.IsNullOrWhiteSpace(qns.BootGodName)) + if (loader.LoadedEmulator is NES) { - Global.Game.Name = qns.BootGodName; + var nes = loader.LoadedEmulator as NES; + if (!string.IsNullOrWhiteSpace(nes.GameName)) + { + Global.Game.Name = nes.GameName; + } + + Global.Game.Status = nes.RomStatus; } - if (qns.BootGodStatus.HasValue) + else if (loader.LoadedEmulator is QuickNES) { - Global.Game.Status = qns.BootGodStatus.Value; + var qns = loader.LoadedEmulator as QuickNES; + if (!string.IsNullOrWhiteSpace(qns.BootGodName)) + { + Global.Game.Name = qns.BootGodName; + } + if (qns.BootGodStatus.HasValue) + { + Global.Game.Status = qns.BootGodStatus.Value; + } } - } - Global.Rewinder.ResetRewindBuffer(); + Global.Rewinder.ResetRewindBuffer(); - if (Global.Emulator.CoreComm.RomStatusDetails == null && loader.Rom != null) - { - Global.Emulator.CoreComm.RomStatusDetails = string.Format( - "{0}\r\nSHA1:{1}\r\nMD5:{2}\r\n", - loader.Game.Name, - loader.Rom.RomData.HashSHA1(), - loader.Rom.RomData.HashMD5()); - } - - if (Global.Emulator.BoardName != null) - { - Console.WriteLine("Core reported BoardID: \"{0}\"", Global.Emulator.BoardName); - } - - // restarts the lua console if a different rom is loaded. - // im not really a fan of how this is done.. - if (Global.Config.RecentRoms.Empty || Global.Config.RecentRoms.MostRecent != loader.CanonicalFullPath) - { - GlobalWin.Tools.Restart(); - } - - Global.Config.RecentRoms.Add(loader.CanonicalFullPath); - JumpLists.AddRecentItem(loader.CanonicalFullPath); - - // Don't load Save Ram if a movie is being loaded - if (!Global.MovieSession.MovieIsQueued && File.Exists(PathManager.SaveRamPath(loader.Game))) - { - LoadSaveRam(); - } - - GlobalWin.Tools.Restart(); - - if (Global.Config.LoadCheatFileByGame) - { - if (Global.CheatList.AttemptToLoadCheatFile()) + if (Global.Emulator.CoreComm.RomStatusDetails == null && loader.Rom != null) { - GlobalWin.OSD.AddMessage("Cheats file loaded"); + Global.Emulator.CoreComm.RomStatusDetails = string.Format( + "{0}\r\nSHA1:{1}\r\nMD5:{2}\r\n", + loader.Game.Name, + loader.Rom.RomData.HashSHA1(), + loader.Rom.RomData.HashMD5()); } - } - SetWindowText(); - CurrentlyOpenRom = loader.CanonicalFullPath; - HandlePlatformMenus(); - _stateSlots.Clear(); - UpdateCoreStatusBarButton(); - UpdateDumpIcon(); - SetMainformMovieInfo(); - - Global.Rewinder.CaptureRewindState(); - - Global.StickyXORAdapter.ClearStickies(); - Global.StickyXORAdapter.ClearStickyFloats(); - Global.AutofireStickyXORAdapter.ClearStickies(); - - RewireSound(); - ToolHelpers.UpdateCheatRelatedTools(null, null); - if (Global.Config.AutoLoadLastSaveSlot && _stateSlots.HasSlot(Global.Config.SaveSlot)) - { - LoadQuickSave("QuickSave" + Global.Config.SaveSlot); - } - - if (Global.FirmwareManager.RecentlyServed.Count > 0) - { - Console.WriteLine("Active Firmwares:"); - foreach (var f in Global.FirmwareManager.RecentlyServed) + if (Global.Emulator.BoardName != null) { - Console.WriteLine(" {0} : {1}", f.FirmwareId, f.Hash); + Console.WriteLine("Core reported BoardID: \"{0}\"", Global.Emulator.BoardName); } - } - return true; - } - else - { - //This shows up if there's a problem - // TODO: put all these in a single method or something - //The ROM has been loaded by a recursive invocation of the LoadROM method. - if (!(Global.Emulator is NullEmulator)) - { + // restarts the lua console if a different rom is loaded. + // im not really a fan of how this is done.. + if (Global.Config.RecentRoms.Empty || Global.Config.RecentRoms.MostRecent != loaderName) + { + GlobalWin.Tools.Restart(); + } + + Global.Config.RecentRoms.Add(loaderName); + JumpLists.AddRecentItem(loaderName, ioa.DisplayName); + + // Don't load Save Ram if a movie is being loaded + if (!Global.MovieSession.MovieIsQueued && File.Exists(PathManager.SaveRamPath(loader.Game))) + { + LoadSaveRam(); + } + + GlobalWin.Tools.Restart(); + + if (Global.Config.LoadCheatFileByGame) + { + if (Global.CheatList.AttemptToLoadCheatFile()) + { + GlobalWin.OSD.AddMessage("Cheats file loaded"); + } + } + + SetWindowText(); + CurrentlyOpenRom = loaderName; + HandlePlatformMenus(); + _stateSlots.Clear(); + UpdateCoreStatusBarButton(); + UpdateDumpIcon(); + SetMainformMovieInfo(); + CurrentlyOpenRomArgs = args; + + Global.Rewinder.CaptureRewindState(); + + Global.StickyXORAdapter.ClearStickies(); + Global.StickyXORAdapter.ClearStickyFloats(); + Global.AutofireStickyXORAdapter.ClearStickies(); + + RewireSound(); + ToolHelpers.UpdateCheatRelatedTools(null, null); + if (Global.Config.AutoLoadLastSaveSlot && _stateSlots.HasSlot(Global.Config.SaveSlot)) + { + LoadQuickSave("QuickSave" + Global.Config.SaveSlot); + } + + if (Global.FirmwareManager.RecentlyServed.Count > 0) + { + Console.WriteLine("Active Firmwares:"); + foreach (var f in Global.FirmwareManager.RecentlyServed) + { + Console.WriteLine(" {0} : {1}", f.FirmwareId, f.Hash); + } + } return true; } + else + { + //This shows up if there's a problem + // TODO: put all these in a single method or something - HandlePlatformMenus(); - _stateSlots.Clear(); - UpdateStatusSlots(); - UpdateCoreStatusBarButton(); - UpdateDumpIcon(); - SetMainformMovieInfo(); - SetWindowText(); - return false; + //The ROM has been loaded by a recursive invocation of the LoadROM method. + if (!(Global.Emulator is NullEmulator)) + { + return true; + } + + HandlePlatformMenus(); + _stateSlots.Clear(); + UpdateStatusSlots(); + UpdateCoreStatusBarButton(); + UpdateDumpIcon(); + SetMainformMovieInfo(); + SetWindowText(); + return false; + } + } + finally + { + if (firstCall) + { + CurrentLoadRomArgs = null; + } } } @@ -3709,6 +3790,8 @@ namespace BizHawk.Client.EmuHawk PauseOnFrame = null; ToolHelpers.UpdateCheatRelatedTools(null, null); UpdateStatusSlots(); + CurrentlyOpenRom = null; + CurrentlyOpenRomArgs = null; } } @@ -3824,7 +3907,7 @@ namespace BizHawk.Client.EmuHawk return master.Rewind(); } } - + var isRewinding = false; if (Global.Rewinder.RewindActive && (Global.ClientControls["Rewind"] || PressRewind) && !Global.MovieSession.Movie.IsRecording) // Rewind isn't "bulletproof" and can desync a recording movie! @@ -3910,6 +3993,5 @@ namespace BizHawk.Client.EmuHawk nesHawkToolStripMenuItem.Checked = Global.Config.NES_InQuickNES == false; } - } } diff --git a/BizHawk.Client.EmuHawk/MainForm.resx b/BizHawk.Client.EmuHawk/MainForm.resx index ac68a9029a..b1102bdcb9 100644 --- a/BizHawk.Client.EmuHawk/MainForm.resx +++ b/BizHawk.Client.EmuHawk/MainForm.resx @@ -571,6 +571,6 @@ 399, 13 - 65 + 37 \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/OpenAdvanced.cs b/BizHawk.Client.EmuHawk/OpenAdvanced.cs new file mode 100644 index 0000000000..9a9cfc1c91 --- /dev/null +++ b/BizHawk.Client.EmuHawk/OpenAdvanced.cs @@ -0,0 +1,164 @@ +using System; +using System.Text; +using System.IO; +using System.Collections.Generic; + +using BizHawk.Emulation.Cores; + +using Newtonsoft.Json; + +//this file contains some cumbersome self-"serialization" in order to gain a modicum of control over what the serialized output looks like +//I don't want them to look like crufty json + +namespace BizHawk.Client.EmuHawk +{ + public interface IOpenAdvanced + { + string TypeName { get; } + string DisplayName { get; } + + /// + /// returns a sole path to use for opening a rom (not sure if this is a good idea) + /// + string SimplePath { get; } + + void Deserialize(string str); + void Serialize(TextWriter tw); + } + + public interface IOpenAdvancedLibretro + { + string CorePath { get; set; } + } + + public static class OpenAdvancedTypes + { + public const string OpenRom = "OpenRom"; + public const string Libretro = "Libretro"; + public const string LibretroNoGame = "LibretroNoGame"; + } + + + public class OpenAdvancedSerializer + { + + public static IOpenAdvanced ParseWithLegacy(string text) + { + if (text.StartsWith("*")) + return Deserialize(text.Substring(1)); + else return new OpenAdvanced_OpenRom { Path = text }; + } + + private static IOpenAdvanced Deserialize(string text) + { + int idx = text.IndexOf('*'); + 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("IOpenAdvanced deserialization error"); + ioa.Deserialize(token); + return ioa; + } + + public static string Serialize(IOpenAdvanced ioa) + { + StringWriter sw = new StringWriter(); + sw.Write("{0}*", ioa.TypeName); + ioa.Serialize(sw); + return sw.ToString(); + } + } + + class OpenAdvanced_Libretro : IOpenAdvanced, IOpenAdvancedLibretro + { + public OpenAdvanced_Libretro() + { + } + + public struct Token + { + public string Path, CorePath; + } + public Token token = new Token(); + + public string TypeName { get { return "Libretro"; } } + public string DisplayName { get { return string.Format("{0}:{1}", Path.GetFileNameWithoutExtension(token.CorePath), token.Path); } } + public string SimplePath { get { return token.Path; } } + + public void Deserialize(string str) + { + token = JsonConvert.DeserializeObject(str); + } + + public void Serialize(TextWriter tw) + { + tw.Write(JsonConvert.SerializeObject(token)); + } + + public string CorePath { get { return token.CorePath; } set { token.CorePath = value; } } + } + + class OpenAdvanced_LibretroNoGame : IOpenAdvanced, IOpenAdvancedLibretro + { + //you might think ideally we'd fetch the libretro core name from the core info inside it + //but that would involve spinning up excess libretro core instances, which probably isnt good for stability, no matter how much we wish otherwise, not to mention slow. + //moreover it's kind of complicated here, + //and finally, I think the Displayname should really be file-based in all cases, since the user is going to be loading cores by filename and + //this is related to the recent roms filename management. + //so, leave it. + + public OpenAdvanced_LibretroNoGame() + { + } + + public OpenAdvanced_LibretroNoGame(string corepath) + { + _corePath = corepath; + } + + string _corePath; + + public string TypeName { get { return "LibretroNoGame"; } } + public string DisplayName { get { return Path.GetFileName(_corePath); } } //assume we like the filename of the core + public string SimplePath { get { return ""; } } //effectively a signal to not use a game + + public void Deserialize(string str) + { + _corePath = str; + } + + public void Serialize(TextWriter tw) + { + tw.Write(_corePath); + } + + public string CorePath { get { return _corePath; } set { _corePath = value; } } + } + + class OpenAdvanced_OpenRom : IOpenAdvanced + { + public OpenAdvanced_OpenRom() + {} + + public string Path; + + public string TypeName { get { return "OpenRom"; } } + 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); + } + } +} \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/OpenAdvancedChooser.Designer.cs b/BizHawk.Client.EmuHawk/OpenAdvancedChooser.Designer.cs new file mode 100644 index 0000000000..253af15e5e --- /dev/null +++ b/BizHawk.Client.EmuHawk/OpenAdvancedChooser.Designer.cs @@ -0,0 +1,183 @@ +namespace BizHawk.Client.EmuHawk +{ + partial class OpenAdvancedChooser + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + 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.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; + // + // 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.Text = "Open Advanced"; + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.groupBox3.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Button btnLibretroLaunchNoGame; + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.Button btnSetLibretroCore; + private System.Windows.Forms.TextBox txtLibretroCore; + private System.Windows.Forms.Button btnLibretroLaunchGame; + private System.Windows.Forms.GroupBox groupBox3; + private System.Windows.Forms.Button btnClassicLaunchGame; + } +} \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/OpenAdvancedChooser.cs b/BizHawk.Client.EmuHawk/OpenAdvancedChooser.cs new file mode 100644 index 0000000000..1dc7960a18 --- /dev/null +++ b/BizHawk.Client.EmuHawk/OpenAdvancedChooser.cs @@ -0,0 +1,103 @@ +using System; +using System.IO; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +using BizHawk.Emulation.Cores; +using BizHawk.Client.Common; + +//these match strings from OpenAdvance. should we make them constants in there? + +namespace BizHawk.Client.EmuHawk +{ + public partial class OpenAdvancedChooser : Form + { + MainForm mainForm; + + public enum Command + { + RetroLaunchNoGame, RetroLaunchGame, + ClassicLaunchGame + } + + public Command Result; + + public OpenAdvancedChooser(MainForm mainForm) + { + this.mainForm = mainForm; + + InitializeComponent(); + + RefreshLibretroCore(); + } + + private void btnOK_Click(object sender, EventArgs e) + { + DialogResult = System.Windows.Forms.DialogResult.OK; + Close(); + } + + private void btnCancel_Click(object sender, EventArgs e) + { + DialogResult = System.Windows.Forms.DialogResult.Cancel; + Close(); + } + + private void btnSetLibretroCore_Click(object sender, EventArgs e) + { + mainForm.RunLibretroCoreChooser(); + RefreshLibretroCore(); + } + + void RefreshLibretroCore() + { + txtLibretroCore.Text = ""; + btnLibretroLaunchNoGame.Enabled = false; + btnLibretroLaunchGame.Enabled = false; + + var core = Global.Config.LibretroCore; + if (string.IsNullOrEmpty(core)) + return; + + txtLibretroCore.Text = core; + btnLibretroLaunchGame.Enabled = true; + + //scan the current libretro core to see if it can be launched with NoGame + try + { + using (var retro = new LibRetroEmulator(new BizHawk.Emulation.Common.CoreComm(null, null), core)) + { + if (retro.EnvironmentInfo.SupportNoGame) + btnLibretroLaunchNoGame.Enabled = true; + } + } + catch { } + } + + private void btnLibretroLaunchGame_Click(object sender, EventArgs e) + { + Result = Command.RetroLaunchGame; + DialogResult = System.Windows.Forms.DialogResult.OK; + Close(); + } + + private void btnClassicLaunchGame_Click(object sender, EventArgs e) + { + Result = Command.ClassicLaunchGame; + DialogResult = System.Windows.Forms.DialogResult.OK; + Close(); + } + + private void btnLibretroLaunchNoGame_Click(object sender, EventArgs e) + { + Result = Command.RetroLaunchNoGame; + DialogResult = System.Windows.Forms.DialogResult.OK; + Close(); + } + } +} diff --git a/BizHawk.Client.EmuHawk/OpenAdvancedChooser.resx b/BizHawk.Client.EmuHawk/OpenAdvancedChooser.resx new file mode 100644 index 0000000000..1af7de150c --- /dev/null +++ b/BizHawk.Client.EmuHawk/OpenAdvancedChooser.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/config/ControllerConfig/ControllerConfigPanel.Designer.cs b/BizHawk.Client.EmuHawk/config/ControllerConfig/ControllerConfigPanel.Designer.cs index 1a17a0b9cf..1e38e366f2 100644 --- a/BizHawk.Client.EmuHawk/config/ControllerConfig/ControllerConfigPanel.Designer.cs +++ b/BizHawk.Client.EmuHawk/config/ControllerConfig/ControllerConfigPanel.Designer.cs @@ -39,12 +39,12 @@ this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.clearToolStripMenuItem}); this.contextMenuStrip1.Name = "contextMenuStrip1"; - this.contextMenuStrip1.Size = new System.Drawing.Size(153, 48); + this.contextMenuStrip1.Size = new System.Drawing.Size(100, 26); // // clearToolStripMenuItem // this.clearToolStripMenuItem.Name = "clearToolStripMenuItem"; - this.clearToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.clearToolStripMenuItem.Size = new System.Drawing.Size(99, 22); this.clearToolStripMenuItem.Text = "&Clear"; this.clearToolStripMenuItem.Click += new System.EventHandler(this.clearToolStripMenuItem_Click); // diff --git a/BizHawk.Common/BizHawk.Common.csproj b/BizHawk.Common/BizHawk.Common.csproj index 1a05d5b78c..dfeeffa823 100644 --- a/BizHawk.Common/BizHawk.Common.csproj +++ b/BizHawk.Common/BizHawk.Common.csproj @@ -76,6 +76,7 @@ + diff --git a/BizHawk.Common/HawkFile.cs b/BizHawk.Common/HawkFile.cs index e4a2286125..0cd336d2c5 100644 --- a/BizHawk.Common/HawkFile.cs +++ b/BizHawk.Common/HawkFile.cs @@ -105,6 +105,11 @@ namespace BizHawk.Common /// public bool IsArchive { get { return _isArchive; } } + /// + /// Indicates whether the file is an archive member (IsArchive && IsBound[to member]) + /// + public bool IsArchiveMember { get { return IsArchive && IsBound; } } + public IList ArchiveItems { get @@ -168,6 +173,19 @@ namespace BizHawk.Common } } + /// + /// attempts to read all the content from the file + /// + public byte[] ReadAllBytes() + { + using (Stream stream = GetStream()) + { + var ms = new MemoryStream((int)stream.Length); + stream.CopyTo(ms); + return ms.GetBuffer(); + } + } + /// /// these extensions won't even be tried as archives (removes spurious archive detects since some of the signatures are pretty damn weak) /// diff --git a/BizHawk.Common/InstanceDll.cs b/BizHawk.Common/InstanceDll.cs index 276a7b314a..39007a0590 100644 --- a/BizHawk.Common/InstanceDll.cs +++ b/BizHawk.Common/InstanceDll.cs @@ -14,15 +14,42 @@ namespace BizHawk.Common using (var sdll = File.OpenRead(dllPath)) sdll.CopyTo(stream); - _hModule = LoadLibrary(path); - var newfname = Path.GetFileName(path); - newfname = "bizhawk.bizdelete-" + newfname; - var newpath = Path.Combine(Path.GetDirectoryName(path), newfname); - File.Move(path, newpath); + //try to locate dlls in the current directory (for libretro cores) + //this isnt foolproof but its a little better than nothing + //setting PWD temporarily doesnt work. that'd be ideal since it supposedly gets searched early on, + //but i guess not with SetDllDirectory in effect + var envpath = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Process); + try + { + string envpath_new = Path.GetDirectoryName(dllPath) + ";" + envpath; + Environment.SetEnvironmentVariable("PATH", envpath_new, EnvironmentVariableTarget.Process); + _hModule = LoadLibrary(dllPath); //consider using LoadLibraryEx instead of shenanigans? + var newfname = Path.GetFileName(path); + newfname = "bizhawk.bizdelete-" + newfname; + var newpath = Path.Combine(Path.GetDirectoryName(path), newfname); + File.Move(path, newpath); + } + finally + { + Environment.SetEnvironmentVariable("PATH", envpath, EnvironmentVariableTarget.Process); + } } - [DllImport("kernel32.dll")] + [Flags] + enum LoadLibraryFlags : uint + { + DONT_RESOLVE_DLL_REFERENCES = 0x00000001, + LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010, + LOAD_LIBRARY_AS_DATAFILE = 0x00000002, + LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040, + LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020, + LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008 + } + + [DllImport("kernel32.dll", SetLastError = true)] static extern IntPtr LoadLibrary(string dllToLoad); + [DllImport("kernel32.dll", SetLastError = true)] + static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags); [DllImport("kernel32.dll")] static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName); [DllImport("kernel32.dll")] diff --git a/BizHawk.Common/UnmanagedResourceHeap.cs b/BizHawk.Common/UnmanagedResourceHeap.cs new file mode 100644 index 0000000000..901587fba3 --- /dev/null +++ b/BizHawk.Common/UnmanagedResourceHeap.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace BizHawk.Common +{ + public class UnmanagedResourceHeap : IDisposable + { + public IntPtr StringToHGlobalAnsi(string str) + { + var ret = Marshal.StringToHGlobalAnsi(str); + HGlobals.Add(ret); + return ret; + } + + public List HGlobals = new List(); + + public void Dispose() + { + foreach (var h in HGlobals) + Marshal.FreeHGlobal(h); + HGlobals.Clear(); + } + } +} diff --git a/BizHawk.Emulation.Common/CoreComms.cs b/BizHawk.Emulation.Common/CoreComms.cs index 557691c1a1..d963c81ec8 100644 --- a/BizHawk.Emulation.Common/CoreComms.cs +++ b/BizHawk.Emulation.Common/CoreComms.cs @@ -38,6 +38,9 @@ namespace BizHawk.Emulation.Common public bool LinkConnected = false; public bool UsesLinkCable = false; + //I know we want to get rid of CoreComm, but while it's still here, I'll use it for this + public string LaunchLibretroCore; + /// /// show a message. reasonably annoying (dialog box), shouldn't be used most of the time /// diff --git a/BizHawk.Emulation.Common/Interfaces/ICoreFileProvider.cs b/BizHawk.Emulation.Common/Interfaces/ICoreFileProvider.cs index b335f60d47..6a28a27545 100644 --- a/BizHawk.Emulation.Common/Interfaces/ICoreFileProvider.cs +++ b/BizHawk.Emulation.Common/Interfaces/ICoreFileProvider.cs @@ -17,6 +17,12 @@ namespace BizHawk.Emulation.Common /// string DllPath(); + /// + /// produces a path that contains saveram... because libretro cores need it? not sure yet + /// + string GetSaveRAMPath(); + + #region EmuLoadHelper api /// diff --git a/BizHawk.Emulation.Cores/LibRetro.cs b/BizHawk.Emulation.Cores/LibRetro.cs index 1d0444788c..124dc3cc5e 100644 --- a/BizHawk.Emulation.Cores/LibRetro.cs +++ b/BizHawk.Emulation.Cores/LibRetro.cs @@ -5,6 +5,8 @@ using System.Text; using System.Runtime.InteropServices; using System.Reflection; +using BizHawk.Common; + namespace BizHawk.Emulation.Cores { /// @@ -284,6 +286,22 @@ namespace BizHawk.Emulation.Cores SET_FRAME_TIME_CALLBACK = 21, GET_RUMBLE_INTERFACE = 23, GET_INPUT_DEVICE_CAPABILITIES = 24, + //25,26 are experimental + GET_LOG_INTERFACE = 27, + GET_PERF_INTERFACE = 28, + GET_LOCATION_INTERFACE = 29, + GET_CORE_ASSETS_DIRECTORY = 30, + GET_SAVE_DIRECTORY = 31, + SET_SYSTEM_AV_INFO = 32, + SET_PROC_ADDRESS_CALLBACK = 33, + SET_SUBSYSTEM_INFO = 34, + SET_CONTROLLER_INFO = 35, + SET_MEMORY_MAPS = 36 | EXPERIMENTAL, + SET_GEOMETRY = 37, + GET_USERNAME = 38, + GET_LANGUAGE = 39, + + EXPERIMENTAL = 0x10000 }; public enum RETRO_PIXEL_FORMAT @@ -457,65 +475,22 @@ namespace BizHawk.Emulation.Cores public epretro_get_memory_size retro_get_memory_size; #endregion - private static Dictionary AttachedCores = new Dictionary(); - private IntPtr hModule = IntPtr.Zero; - public void Dispose() { - // like many other emu cores, libretros are in general single instance, so we track some things - lock (AttachedCores) - { - if (hModule != IntPtr.Zero) - { - retro_deinit(); - ClearAllEntryPoints(); - Win32.FreeLibrary(hModule); - AttachedCores.Remove(hModule); - hModule = IntPtr.Zero; - } - } + dll.Dispose(); } + InstanceDll dll; public LibRetro(string modulename) { - // like many other emu cores, libretros are in general single instance, so we track some things - lock (AttachedCores) + dll = new InstanceDll(modulename); + if (!ConnectAllEntryPoints()) { - IntPtr newmodule = Win32.LoadLibrary(modulename); - if (newmodule == IntPtr.Zero) - throw new Exception(string.Format("LoadLibrary(\"{0}\") returned NULL", modulename)); - - if (AttachedCores.ContainsKey(newmodule)) - { - // this core is already loaded, so we must detatch the old instance - LibRetro martyr = AttachedCores[newmodule]; - martyr.retro_deinit(); - martyr.ClearAllEntryPoints(); - martyr.hModule = IntPtr.Zero; - Win32.FreeLibrary(newmodule); // decrease ref count by 1 - } - AttachedCores[newmodule] = this; - hModule = newmodule; - if (!ConnectAllEntryPoints()) - { - ClearAllEntryPoints(); - Win32.FreeLibrary(hModule); - hModule = IntPtr.Zero; - throw new Exception("ConnectAllEntryPoints() failed. The console may contain more details."); - } + dll.Dispose(); + throw new Exception("ConnectAllEntryPoints() failed. The console may contain more details."); } } - private static class Win32 - { - [DllImport("kernel32.dll")] - public static extern IntPtr LoadLibrary(string dllToLoad); - [DllImport("kernel32.dll")] - public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName); - [DllImport("kernel32.dll")] - public static extern bool FreeLibrary(IntPtr hModule); - } - private static IEnumerable GetAllEntryPoints() { return typeof(LibRetro).GetFields().Where((field) => field.FieldType.Name.StartsWith("epretro")); @@ -535,7 +510,7 @@ namespace BizHawk.Emulation.Cores foreach (var field in GetAllEntryPoints()) { string fieldname = field.Name; - IntPtr entry = Win32.GetProcAddress(hModule, fieldname); + IntPtr entry = dll.GetProcAddress(fieldname); if (entry != IntPtr.Zero) { field.SetValue(this, Marshal.GetDelegateForFunctionPointer(entry, field.FieldType)); diff --git a/BizHawk.Emulation.Cores/LibRetroEmulator.cs b/BizHawk.Emulation.Cores/LibRetroEmulator.cs index aa97549041..3698bb21ea 100644 --- a/BizHawk.Emulation.Cores/LibRetroEmulator.cs +++ b/BizHawk.Emulation.Cores/LibRetroEmulator.cs @@ -1,19 +1,96 @@ using System; +using System.IO; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; +using Newtonsoft.Json; +using BizHawk.Common; using BizHawk.Emulation.Common; +using BizHawk.Common.BufferExtensions; namespace BizHawk.Emulation.Cores { - [CoreAttributes("DEBUG ONLY DON'T USE", "natt")] - public unsafe class LibRetroEmulator : IEmulator, IVideoProvider + [CoreAttributes("Libretro", "natt&zeromus")] + public unsafe class LibRetroEmulator : IEmulator, ISettable, + ISaveRam, IStatable, IVideoProvider, IInputPollable { + #region Settings + + Settings _Settings = new Settings(); + SyncSettings _SyncSettings; + + public class SyncSettings + { + public SyncSettings Clone() + { + return JsonConvert.DeserializeObject(JsonConvert.SerializeObject(this)); + } + + public SyncSettings() + { + } + } + + + public class Settings + { + public void Validate() + { + } + + public Settings() + { + SettingsUtil.SetDefaultValues(this); + } + + public Settings Clone() + { + return (Settings)MemberwiseClone(); + } + } + + public Settings GetSettings() + { + return _Settings.Clone(); + } + + public SyncSettings GetSyncSettings() + { + return _SyncSettings.Clone(); + } + + public bool PutSettings(Settings o) + { + _Settings.Validate(); + _Settings = o; + + //TODO - store settings into core? or we can just keep doing it before frameadvance + + return false; + } + + public bool PutSyncSettings(SyncSettings o) + { + //currently LEC and pad settings changes both require reboot + bool reboot = true; + + //we could do it this way roughly if we need to + //if(JsonConvert.SerializeObject(o.FIOConfig) != JsonConvert.SerializeObject(_SyncSettings.FIOConfig) + + + _SyncSettings = o; + + + return reboot; + } + + #endregion + #region callbacks - bool retro_environment(LibRetro.RETRO_ENVIRONMENT cmd, IntPtr data) + unsafe bool retro_environment(LibRetro.RETRO_ENVIRONMENT cmd, IntPtr data) { switch (cmd) { @@ -72,6 +149,7 @@ namespace BizHawk.Emulation.Cores case LibRetro.RETRO_ENVIRONMENT.GET_VARIABLE_UPDATE: return false; case LibRetro.RETRO_ENVIRONMENT.SET_SUPPORT_NO_GAME: + EnvironmentInfo.SupportNoGame = true; return false; case LibRetro.RETRO_ENVIRONMENT.GET_LIBRETRO_PATH: return false; @@ -83,6 +161,19 @@ namespace BizHawk.Emulation.Cores return false; case LibRetro.RETRO_ENVIRONMENT.GET_INPUT_DEVICE_CAPABILITIES: return false; + case LibRetro.RETRO_ENVIRONMENT.GET_LOG_INTERFACE: + return false; + case LibRetro.RETRO_ENVIRONMENT.GET_PERF_INTERFACE: + return false; + case LibRetro.RETRO_ENVIRONMENT.GET_LOCATION_INTERFACE: + return false; + case LibRetro.RETRO_ENVIRONMENT.GET_CORE_ASSETS_DIRECTORY: + return false; + case LibRetro.RETRO_ENVIRONMENT.GET_SAVE_DIRECTORY: + //this will suffice for now. if we find evidence later it's needed we can stash a string with + //unmanagedResources and CoreFileProvider + *((IntPtr*)data.ToPointer()) = IntPtr.Zero; + return false; default: Console.WriteLine("Unknkown retro_environment command {0}", (int)cmd); return false; @@ -92,9 +183,53 @@ namespace BizHawk.Emulation.Cores { IsLagFrame = false; } + + private bool GetButton(uint pnum, string type, string button) + { + string key = string.Format("P{0} {1} {2}", pnum, type, button); + bool b = Controller[key]; + if (b == true) + { + return true; //debugging placeholder + } + else return false; + } + + //port = console physical port? + //device = logical device type + //index = sub device index? (multitap?) + //id = button id short retro_input_state(uint port, uint device, uint index, uint id) { - return 0; + switch ((LibRetro.RETRO_DEVICE)device) + { + case LibRetro.RETRO_DEVICE.JOYPAD: + { + //The JOYPAD is sometimes called RetroPad (and we'll call it that in user-facing stuff cos retroarch does) + //It is essentially a Super Nintendo controller, but with additional L2/R2/L3/R3 buttons, similar to a PS1 DualShock. + + string button = ""; + switch ((LibRetro.RETRO_DEVICE_ID_JOYPAD)id) + { + case LibRetro.RETRO_DEVICE_ID_JOYPAD.A: button = "A"; break; + case LibRetro.RETRO_DEVICE_ID_JOYPAD.B: button = "B"; break; + case LibRetro.RETRO_DEVICE_ID_JOYPAD.X: button = "X"; break; + case LibRetro.RETRO_DEVICE_ID_JOYPAD.Y: button = "Y"; break; + case LibRetro.RETRO_DEVICE_ID_JOYPAD.UP: button = "Up"; break; + case LibRetro.RETRO_DEVICE_ID_JOYPAD.DOWN: button = "Down"; break; + case LibRetro.RETRO_DEVICE_ID_JOYPAD.LEFT: button = "Left"; break; + case LibRetro.RETRO_DEVICE_ID_JOYPAD.RIGHT: button = "Right"; break; + case LibRetro.RETRO_DEVICE_ID_JOYPAD.L: button = "L"; break; + case LibRetro.RETRO_DEVICE_ID_JOYPAD.R: button = "R"; break; + case LibRetro.RETRO_DEVICE_ID_JOYPAD.SELECT: button = "Select"; break; + case LibRetro.RETRO_DEVICE_ID_JOYPAD.START: button = "Start"; break; + } + + return (short)(GetButton(port+1, "RetroPad", button) ? 1 : 0); + } + default: + return 0; + } } LibRetro.retro_environment_t retro_environment_cb; @@ -107,34 +242,24 @@ namespace BizHawk.Emulation.Cores #endregion private LibRetro retro; + private UnmanagedResourceHeap unmanagedResources = new UnmanagedResourceHeap(); - public static LibRetroEmulator CreateDebug(CoreComm nextComm, byte[] debugfile) + //todo - make private + public LibRetro.retro_system_info system_info = new LibRetro.retro_system_info(); + + public struct RetroEnvironmentInfo { - System.IO.TextReader tr = new System.IO.StreamReader(new System.IO.MemoryStream(debugfile, false)); - string modulename = tr.ReadLine(); - string romname = tr.ReadLine(); - - byte[] romdata = System.IO.File.ReadAllBytes(romname); - - var emu = new LibRetroEmulator(nextComm, modulename); - try - { - if (!emu.Load(romdata)) - throw new Exception("LibRetroEmulator.Load() failed"); - // ... - } - catch - { - emu.Dispose(); - throw; - } - return emu; + public bool SupportNoGame; } + public RetroEnvironmentInfo EnvironmentInfo = new RetroEnvironmentInfo(); + public LibRetroEmulator(CoreComm nextComm, string modulename) { ServiceProvider = new BasicServiceProvider(this); + _SyncSettings = new SyncSettings(); + retro_environment_cb = new LibRetro.retro_environment_t(retro_environment); retro_video_refresh_cb = new LibRetro.retro_video_refresh_t(retro_video_refresh); retro_audio_sample_cb = new LibRetro.retro_audio_sample_t(retro_audio_sample); @@ -147,13 +272,7 @@ namespace BizHawk.Emulation.Cores { CoreComm = nextComm; - LibRetro.retro_system_info sys = new LibRetro.retro_system_info(); - retro.retro_get_system_info(ref sys); - - if (sys.need_fullpath) - throw new ArgumentException("This libretro core needs filepaths"); - if (sys.block_extract) - throw new ArgumentException("This libretro needs non-blocked extract"); + retro.retro_get_system_info(ref system_info); retro.retro_set_environment(retro_environment_cb); retro.retro_init(); @@ -166,13 +285,16 @@ namespace BizHawk.Emulation.Cores catch { retro.Dispose(); + retro = null; throw; } } public IEmulatorServiceProvider ServiceProvider { get; private set; } - public bool Load(byte[] data) + + + public bool LoadData(byte[] data) { LibRetro.retro_game_info gi = new LibRetro.retro_game_info(); fixed (byte* p = &data[0]) @@ -181,14 +303,34 @@ namespace BizHawk.Emulation.Cores gi.meta = ""; gi.path = ""; gi.size = (uint)data.Length; - if (!retro.retro_load_game(ref gi)) - { - Console.WriteLine("retro_load_game() failed"); - return false; - } - savebuff = new byte[retro.retro_serialize_size()]; - savebuff2 = new byte[savebuff.Length + 13]; + return LoadWork(ref gi); } + } + + public bool LoadPath(string path) + { + LibRetro.retro_game_info gi = new LibRetro.retro_game_info(); + gi.path = path; //is this the right encoding? seems to be ok + return LoadWork(ref gi); + } + + public bool LoadNoGame() + { + LibRetro.retro_game_info gi = new LibRetro.retro_game_info(); + return LoadWork(ref gi); + } + + bool LoadWork(ref LibRetro.retro_game_info gi) + { + if (!retro.retro_load_game(ref gi)) + { + Console.WriteLine("retro_load_game() failed"); + return false; + } + + //TODO - libretro cores can return a varying serialize size over time. I tried to get them to write it in the docs... + savebuff = new byte[retro.retro_serialize_size()]; + savebuff2 = new byte[savebuff.Length + 13]; LibRetro.retro_system_av_info av = new LibRetro.retro_system_av_info(); retro.retro_get_system_av_info(ref av); @@ -204,33 +346,44 @@ namespace BizHawk.Emulation.Cores SetupResampler(av.timing.fps, av.timing.sample_rate); + ControllerDefinition = CreateControllerDefinition(_SyncSettings); + return true; } - - public ControllerDefinition ControllerDefinition + public static ControllerDefinition CreateControllerDefinition(SyncSettings syncSettings) { - get { return NullEmulator.NullController; } + ControllerDefinition definition = new ControllerDefinition(); + definition.Name = "LibRetro Controls"; // <-- for compatibility + + foreach(var item in new[] { + "P1 {0} Up", "P1 {0} Down", "P1 {0} Left", "P1 {0} Right", "P1 {0} Select", "P1 {0} Start", "P1 {0} Y", "P1 {0} B", "P1 {0} X", "P1 {0} A", "P1 {0} L", "P1 {0} R", + "P2 {0} Up", "P2 {0} Down", "P2 {0} Left", "P2 {0} Right", "P2 {0} Select", "P2 {0} Start", "P2 {0} Y", "P2 {0} B", "P2 {0} X", "P2 {0} A", "P2 {0} L", "P2 {0} R", + }) + definition.BoolButtons.Add(string.Format(item,"RetroPad")); + + return definition; } + public ControllerDefinition ControllerDefinition { get; private set; } public IController Controller { get; set; } public void FrameAdvance(bool render, bool rendersound = true) { + //TODO - consider changing directory and using Libretro subdir of bizhawk as a kind of sandbox, for the duration of the run? + CloneSaveRam(); IsLagFrame = true; Frame++; nsamprecv = 0; retro.retro_run(); - Console.WriteLine("[{0}]", nsamprecv); + //Console.WriteLine("[{0}]", nsamprecv); } public int Frame { get; private set; } - public int LagCount { get; set; } - public bool IsLagFrame { get; private set; } public string SystemId { - get { return "TEST"; } + get { return "Libretro"; } } public bool DeterministicEmulation @@ -244,7 +397,8 @@ namespace BizHawk.Emulation.Cores get { return null; } } - #region saveram + #region ISaveRam + //TODO - terrible things will happen if this changes at runtime byte[] saverambuff = new byte[0]; @@ -256,8 +410,8 @@ namespace BizHawk.Emulation.Cores IntPtr src = retro.retro_get_memory_data(LibRetro.RETRO_MEMORY.SAVE_RAM); if (src == IntPtr.Zero) - throw new Exception("retro_get_memory_data(RETRO_MEMORY_SAVE_RAM) returned NULL"); - + return null; + Marshal.Copy(src, saverambuff, 0, size); return (byte[])saverambuff.Clone(); } @@ -265,8 +419,9 @@ namespace BizHawk.Emulation.Cores public void StoreSaveRam(byte[] data) { int size = (int)retro.retro_get_memory_size(LibRetro.RETRO_MEMORY.SAVE_RAM); - if (data.Length != size) - throw new Exception("Passed saveram does not match retro_get_memory_size(RETRO_MEMORY_SAVE_RAM"); + + if (size == 0) + return; IntPtr dst = retro.retro_get_memory_data(LibRetro.RETRO_MEMORY.SAVE_RAM); if (dst == IntPtr.Zero) @@ -278,7 +433,14 @@ namespace BizHawk.Emulation.Cores public bool SaveRamModified { [FeatureNotImplemented] - get { return true; } + get + { + //if we dont have saveram, it isnt modified. otherwise, assume iti s + int size = (int)retro.retro_get_memory_size(LibRetro.RETRO_MEMORY.SAVE_RAM); + if (size == 0) + return false; + return true; + } [FeatureNotImplemented] set { throw new NotImplementedException(); } @@ -298,16 +460,18 @@ namespace BizHawk.Emulation.Cores private byte[] savebuff; private byte[] savebuff2; - [FeatureNotImplemented] public void SaveStateText(System.IO.TextWriter writer) { - throw new NotImplementedException(); + var temp = SaveStateBinary(); + temp.SaveAsHex(writer); } - [FeatureNotImplemented] public void LoadStateText(System.IO.TextReader reader) { - throw new NotImplementedException(); + string hex = reader.ReadLine(); + byte[] state = new byte[hex.Length / 2]; + state.ReadFromHex(hex); + LoadStateBinary(new BinaryReader(new MemoryStream(state))); } public void SaveStateBinary(System.IO.BinaryWriter writer) @@ -384,6 +548,8 @@ namespace BizHawk.Emulation.Cores retro.Dispose(); retro = null; } + unmanagedResources.Dispose(); + unmanagedResources = null; } #region ISoundProvider @@ -470,13 +636,13 @@ namespace BizHawk.Emulation.Cores { short ci = *row; int r = ci & 0x001f; - int g = ci & 0x07e0; - int b = ci & 0xf800; + int g = (ci & 0x07e0)>>5; + int b = (ci & 0xf800)>>11; r = (r << 3) | (r >> 2); - g = (g >> 3) | (g >> 9); - b = (b >> 8) | (b >> 13); - int co = r | g | b | unchecked((int)0xff000000); + g = (g << 2) | (g >> 4); + b = (b << 3) | (b >> 2); + int co = (b<<16) | (g<<8) | r; *dst = co; dst++; @@ -534,7 +700,7 @@ namespace BizHawk.Emulation.Cores get { if (dar > 1.0f) - return (int)(BufferWidth * dar); + return (int)(BufferHeight * dar); else return BufferWidth; } @@ -544,7 +710,7 @@ namespace BizHawk.Emulation.Cores get { if (dar < 1.0f) - return (int)(BufferHeight / dar); + return (int)(BufferWidth / dar); else return BufferHeight; } @@ -555,5 +721,16 @@ namespace BizHawk.Emulation.Cores public int BackgroundColor { get { return unchecked((int)0xff000000); } } #endregion + + #region IInputPollable + public int LagCount { get; set; } + public bool IsLagFrame { get; private set; } + public IInputCallbackSystem InputCallbacks + { + [FeatureNotImplemented] + get + { throw new NotImplementedException(); } + } + #endregion } } diff --git a/output/defctrl.json b/output/defctrl.json index 0a7b965818..480ee87cee 100644 --- a/output/defctrl.json +++ b/output/defctrl.json @@ -1,5 +1,31 @@ { "AllTrollers": { + "LibRetro Controls": { + "P1 RetroPad Up": "UpArrow,J1 POV1U, X1 DpadUp, X1 LStickUp", + "P1 RetroPad Down": "DownArrow,J1 POV1D, X1 DpadDown, X1 LStickDown", + "P1 RetroPad Left": "LeftArrow,J1 POV1L, X1 DpadLeft, X1 LStickLeft", + "P1 RetroPad Right": "RightArrow,J1 POV1R, X1 DpadRight, X1 LStickRight", + "P1 RetroPad Select": "Space, J1 B9, X1 Back", + "P1 RetroPad Start": "Return, J1 B10, X1 Start", + "P1 RetroPad Y": "A, J1 B1, X1 X", + "P1 RetroPad B": "Z, J1 B2, X1 A", + "P1 RetroPad X": "S, J1 B4, X1 Y", + "P1 RetroPad A": "X, J1 B3, X1 B", + "P1 RetroPad L": "W, J1 B5, X1 LeftShoulder", + "P1 RetroPad R": "E, J1 B6, X1 RightShoulder", + "P2 RetroPad Up": "", + "P2 RetroPad Down": "", + "P2 RetroPad Left": "", + "P2 RetroPad Right": "", + "P2 RetroPad Select": "", + "P2 RetroPad Start": "", + "P2 RetroPad Y": "", + "P2 RetroPad B": "", + "P2 RetroPad X": "", + "P2 RetroPad A": "", + "P2 RetroPad L": "", + "P2 RetroPad R": "" + }, "NES Controller": { "P1 Up": "UpArrow, J1 POV1U, X1 DpadUp, X1 LStickUp", "P1 Down": "DownArrow, J1 POV1D, X1 DpadDown, X1 LStickDown",