diff --git a/BizHawk.Client.Common/CoreFileProvider.cs b/BizHawk.Client.Common/CoreFileProvider.cs index 78e9e09a78..532cba5926 100644 --- a/BizHawk.Client.Common/CoreFileProvider.cs +++ b/BizHawk.Client.Common/CoreFileProvider.cs @@ -26,7 +26,7 @@ namespace BizHawk.Client.Common _firmwareUserSpecifications = firmwareUserSpecifications; } - public string DllPath() => PathUtils.GetDllDirectory(); + public string DllPath() => PathUtils.DllDirectoryPath; // Poop public string GetRetroSaveRAMDirectory(GameInfo game) diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index 87573fbf3e..ca46ad5c13 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -1082,7 +1082,7 @@ namespace BizHawk.Client.Common } break; case "A78": - var gameDbPath = Path.Combine(PathUtils.GetExeDirectoryAbsolute(), "gamedb", "gamedb_a7800.csv"); + var gameDbPath = Path.Combine(PathUtils.ExeDirectoryPath, "gamedb", "gamedb_a7800.csv"); nextEmulator = new A7800Hawk(nextComm, game, rom.RomData, gameDbPath, GetCoreSettings(), GetCoreSyncSettings()); break; case "C64": diff --git a/BizHawk.Client.Common/config/Config.cs b/BizHawk.Client.Common/config/Config.cs index 134732aff7..29520cdf0c 100644 --- a/BizHawk.Client.Common/config/Config.cs +++ b/BizHawk.Client.Common/config/Config.cs @@ -8,9 +8,9 @@ namespace BizHawk.Client.Common { public class Config { - public static string ControlDefaultPath => Path.Combine(PathUtils.GetExeDirectoryAbsolute(), "defctrl.json"); + public static string ControlDefaultPath => Path.Combine(PathUtils.ExeDirectoryPath, "defctrl.json"); - public static string DefaultIniPath { get; private set; } = Path.Combine(PathUtils.GetExeDirectoryAbsolute(), "config.ini"); + public static string DefaultIniPath { get; private set; } = Path.Combine(PathUtils.ExeDirectoryPath, "config.ini"); // Shenanigans public static void SetDefaultIniPath(string newDefaultIniPath) diff --git a/BizHawk.Client.Common/config/PathEntryCollectionExtensions.cs b/BizHawk.Client.Common/config/PathEntryCollectionExtensions.cs index d44b8c8fcf..1bfae3d8b5 100644 --- a/BizHawk.Client.Common/config/PathEntryCollectionExtensions.cs +++ b/BizHawk.Client.Common/config/PathEntryCollectionExtensions.cs @@ -26,7 +26,7 @@ namespace BizHawk.Client.Common // if %exe% prefixed then substitute exe path and repeat if (globalBase.StartsWith("%exe%", StringComparison.InvariantCultureIgnoreCase)) { - globalBase = PathUtils.GetExeDirectoryAbsolute() + globalBase.Substring(5); + globalBase = PathUtils.ExeDirectoryPath + globalBase.Substring(5); } // rooted paths get returned without change @@ -37,7 +37,7 @@ namespace BizHawk.Client.Common } // not-rooted things are relative to exe path - globalBase = Path.Combine(PathUtils.GetExeDirectoryAbsolute(), globalBase); + globalBase = Path.Combine(PathUtils.ExeDirectoryPath, globalBase); return globalBase; } @@ -94,7 +94,7 @@ namespace BizHawk.Client.Common if (path.StartsWith("%exe%")) { - return PathUtils.GetExeDirectoryAbsolute() + path.Substring(5); + return PathUtils.ExeDirectoryPath + path.Substring(5); } if (path.StartsWith("%rom%")) @@ -133,7 +133,7 @@ namespace BizHawk.Client.Common //handling of file:// or file:\\ was removed (can Path.GetFullPath handle it? not sure) // all bad paths default to EXE - return PathUtils.GetExeDirectoryAbsolute(); + return PathUtils.ExeDirectoryPath; } public static string MovieAbsolutePath(this PathEntryCollection collection) @@ -224,7 +224,7 @@ namespace BizHawk.Client.Common public static string SaveRamAbsolutePath(this PathEntryCollection collection, GameInfo game, bool movieIsActive) { - var name = game.Name.FilesystemSafeName(); + var name = game.FilesystemSafeName(); if (movieIsActive) { name += $".{Path.GetFileNameWithoutExtension(Global.MovieSession.Movie.Filename)}"; @@ -239,11 +239,11 @@ namespace BizHawk.Client.Common // Shenanigans public static string RetroSaveRamAbsolutePath(this PathEntryCollection collection, GameInfo game, bool movieIsActive, string movieFilename) { - var name = game.Name.FilesystemSafeName(); + var name = game.FilesystemSafeName(); name = Path.GetDirectoryName(name); if (name == "") { - name = game.Name.FilesystemSafeName(); + name = game.FilesystemSafeName(); } if (movieIsActive) @@ -260,11 +260,11 @@ namespace BizHawk.Client.Common // Shenanigans public static string RetroSystemAbsolutePath(this PathEntryCollection collection, GameInfo game) { - var name = game.Name.FilesystemSafeName(); + var name = game.FilesystemSafeName(); name = Path.GetDirectoryName(name); if (string.IsNullOrEmpty(name)) { - name = game.Name.FilesystemSafeName(); + name = game.FilesystemSafeName(); } var pathEntry = collection[game.System, "System"] @@ -312,31 +312,11 @@ namespace BizHawk.Client.Common /// Takes an absolute path and attempts to convert it to a relative, based on the system, /// or global base if no system is supplied, if it is not a subfolder of the base, it will return the path unaltered /// - public static string TryMakeRelative(this PathEntryCollection collection, string absolutePath, string system = null) - { - var parentPath = string.IsNullOrWhiteSpace(system) + public static string TryMakeRelative(this PathEntryCollection collection, string absolutePath, string system = null) => absolutePath.MakeRelativeTo( + string.IsNullOrWhiteSpace(system) ? collection.GlobalBaseAbsolutePath() - : collection.AbsolutePathFor(collection.BaseFor(system), system); -#if true - if (!absolutePath.IsSubfolderOf(parentPath)) - { - return absolutePath; - } - - return OSTailoredCode.IsUnixHost - ? "./" + OSTailoredCode.SimpleSubshell("realpath", $"--relative-to=\"{parentPath}\" \"{absolutePath}\"", $"invalid path {absolutePath} or missing realpath binary") - : absolutePath.Replace(parentPath, "."); -#else // written for Unix port but may be useful for .NET Core - if (!IsSubfolder(parentPath, absolutePath)) - { - return OSTailoredCode.IsUnixHost && parentPath.TrimEnd('.') == $"{absolutePath}/" ? "." : absolutePath; - } - - return OSTailoredCode.IsUnixHost - ? absolutePath.Replace(parentPath.TrimEnd('.'), "./") - : absolutePath.Replace(parentPath, "."); -#endif - } + : collection.AbsolutePathFor(collection.BaseFor(system), system) + ); /// /// Puts the currently configured temp path into the environment for use as actual temp directory diff --git a/BizHawk.Client.Common/movie/conversions/MovieConversionExtensions.cs b/BizHawk.Client.Common/movie/conversions/MovieConversionExtensions.cs index fd34d851f7..b7d999e6d0 100644 --- a/BizHawk.Client.Common/movie/conversions/MovieConversionExtensions.cs +++ b/BizHawk.Client.Common/movie/conversions/MovieConversionExtensions.cs @@ -293,7 +293,7 @@ namespace BizHawk.Client.Common.MovieConversionExtensions if (Global.Game != null) { - movie.GameName = Global.Game.Name.FilesystemSafeName(); + movie.GameName = Global.Game.FilesystemSafeName(); movie.Hash = Global.Game.Hash; if (Global.Game.FirmwareHash != null) { diff --git a/BizHawk.Client.EmuHawk/AVOut/FFmpegWriter.cs b/BizHawk.Client.EmuHawk/AVOut/FFmpegWriter.cs index 80f6642c9d..713ed0e695 100644 --- a/BizHawk.Client.EmuHawk/AVOut/FFmpegWriter.cs +++ b/BizHawk.Client.EmuHawk/AVOut/FFmpegWriter.cs @@ -87,7 +87,7 @@ namespace BizHawk.Client.EmuHawk try { _ffmpeg = OSTailoredCode.ConstructSubshell( - OSTailoredCode.IsUnixHost ? "ffmpeg" : Path.Combine(PathUtils.GetDllDirectory(), "ffmpeg.exe"), + OSTailoredCode.IsUnixHost ? "ffmpeg" : Path.Combine(PathUtils.DllDirectoryPath, "ffmpeg.exe"), $"-y -f nut -i - {_token.Commandline} \"{_baseName}{(_segment == 0 ? string.Empty : $"_{_segment}")}{_ext}\"", checkStdout: false, checkStderr: true // ffmpeg sends informative display to stderr, and nothing to stdout diff --git a/BizHawk.Client.EmuHawk/AVOut/SynclessRecordingTools.cs b/BizHawk.Client.EmuHawk/AVOut/SynclessRecordingTools.cs index 3393b16ec1..32a269b589 100644 --- a/BizHawk.Client.EmuHawk/AVOut/SynclessRecordingTools.cs +++ b/BizHawk.Client.EmuHawk/AVOut/SynclessRecordingTools.cs @@ -6,7 +6,7 @@ using System.Windows.Forms; using BizHawk.Bizware.BizwareGL; using BizHawk.Client.Common; -using BizHawk.Common.PathExtensions; +using BizHawk.Emulation.Common; namespace BizHawk.Client.EmuHawk { @@ -33,7 +33,7 @@ namespace BizHawk.Client.EmuHawk { var ofd = new OpenFileDialog { - FileName = $"{Global.Game.Name.FilesystemSafeName()}.syncless.txt", + FileName = $"{Global.Game.FilesystemSafeName()}.syncless.txt", InitialDirectory = Global.Config.PathEntries.AvAbsolutePath() }; diff --git a/BizHawk.Client.EmuHawk/DisplayManager/DisplayManager.cs b/BizHawk.Client.EmuHawk/DisplayManager/DisplayManager.cs index fb710893c8..7bf67e7499 100644 --- a/BizHawk.Client.EmuHawk/DisplayManager/DisplayManager.cs +++ b/BizHawk.Client.EmuHawk/DisplayManager/DisplayManager.cs @@ -82,17 +82,17 @@ namespace BizHawk.Client.EmuHawk if (GL is IGL_TK || GL is IGL_SlimDX9) { - var fiHq2x = new FileInfo(Path.Combine(PathUtils.GetExeDirectoryAbsolute(), "Shaders/BizHawk/hq2x.cgp")); + var fiHq2x = new FileInfo(Path.Combine(PathUtils.ExeDirectoryPath, "Shaders/BizHawk/hq2x.cgp")); if (fiHq2x.Exists) { using var stream = fiHq2x.OpenRead(); - ShaderChain_hq2x = new Filters.RetroShaderChain(GL, new Filters.RetroShaderPreset(stream), Path.Combine(PathUtils.GetExeDirectoryAbsolute(), "Shaders/BizHawk")); + ShaderChain_hq2x = new Filters.RetroShaderChain(GL, new Filters.RetroShaderPreset(stream), Path.Combine(PathUtils.ExeDirectoryPath, "Shaders/BizHawk")); } - var fiScanlines = new FileInfo(Path.Combine(PathUtils.GetExeDirectoryAbsolute(), "Shaders/BizHawk/BizScanlines.cgp")); + var fiScanlines = new FileInfo(Path.Combine(PathUtils.ExeDirectoryPath, "Shaders/BizHawk/BizScanlines.cgp")); if (fiScanlines.Exists) { using var stream = fiScanlines.OpenRead(); - ShaderChain_scanlines = new Filters.RetroShaderChain(GL, new Filters.RetroShaderPreset(stream), Path.Combine(PathUtils.GetExeDirectoryAbsolute(), "Shaders/BizHawk")); + ShaderChain_scanlines = new Filters.RetroShaderChain(GL, new Filters.RetroShaderPreset(stream), Path.Combine(PathUtils.ExeDirectoryPath, "Shaders/BizHawk")); } string bicubicPath = "Shaders/BizHawk/bicubic-fast.cgp"; @@ -100,11 +100,11 @@ namespace BizHawk.Client.EmuHawk { bicubicPath = "Shaders/BizHawk/bicubic-normal.cgp"; } - var fiBicubic = new FileInfo(Path.Combine(PathUtils.GetExeDirectoryAbsolute(), bicubicPath)); + var fiBicubic = new FileInfo(Path.Combine(PathUtils.ExeDirectoryPath, bicubicPath)); if (fiBicubic.Exists) { using var stream = fiBicubic.Open(FileMode.Open, FileAccess.Read, FileShare.Read); - ShaderChain_bicubic = new Filters.RetroShaderChain(GL, new Filters.RetroShaderPreset(stream), Path.Combine(PathUtils.GetExeDirectoryAbsolute(), "Shaders/BizHawk")); + ShaderChain_bicubic = new Filters.RetroShaderChain(GL, new Filters.RetroShaderPreset(stream), Path.Combine(PathUtils.ExeDirectoryPath, "Shaders/BizHawk")); } } diff --git a/BizHawk.Client.EmuHawk/LogWindow.cs b/BizHawk.Client.EmuHawk/LogWindow.cs index f32fc4c195..4943291118 100644 --- a/BizHawk.Client.EmuHawk/LogWindow.cs +++ b/BizHawk.Client.EmuHawk/LogWindow.cs @@ -156,7 +156,7 @@ namespace BizHawk.Client.EmuHawk if (result.IsOk()) { var gameDbEntry = Emulator.AsGameDBEntryGenerator().GenerateGameDbEntry(); - var userDb = Path.Combine(PathUtils.GetExeDirectoryAbsolute(), "gamedb", "gamedb_user.txt"); + var userDb = Path.Combine(PathUtils.ExeDirectoryPath, "gamedb", "gamedb_user.txt"); Global.Game.Status = gameDbEntry.Status = picker.PickedStatus; Database.SaveDatabaseEntry(userDb, gameDbEntry); MainForm.UpdateDumpIcon(); diff --git a/BizHawk.Client.EmuHawk/MainForm.Events.cs b/BizHawk.Client.EmuHawk/MainForm.Events.cs index 1b111942c9..d43db3cdf0 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Events.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Events.cs @@ -536,7 +536,7 @@ namespace BizHawk.Client.EmuHawk var filename = MovieSession.Movie.Filename; if (string.IsNullOrWhiteSpace(filename)) { - filename = Game.Name.FilesystemSafeName(); + filename = Game.FilesystemSafeName(); } var file = ToolFormBase.SaveFileDialog( diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index 9245be6155..fe18c9c5fa 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -237,8 +237,8 @@ namespace BizHawk.Client.EmuHawk // we could background thread this later instead if we wanted to be real clever BootGodDb.GetDatabaseBytes = () => { - string xmlPath = Path.Combine(PathUtils.GetExeDirectoryAbsolute(), "gamedb", "NesCarts.xml"); - string x7zPath = Path.Combine(PathUtils.GetExeDirectoryAbsolute(), "gamedb", "NesCarts.7z"); + string xmlPath = Path.Combine(PathUtils.ExeDirectoryPath, "gamedb", "NesCarts.xml"); + string x7zPath = Path.Combine(PathUtils.ExeDirectoryPath, "gamedb", "NesCarts.7z"); bool loadXml = File.Exists(xmlPath); using var nesCartFile = new HawkFile(loadXml ? xmlPath : x7zPath); if (!loadXml) @@ -259,11 +259,11 @@ namespace BizHawk.Client.EmuHawk MessageBox.Show(e.Message); } - Database.LoadDatabase(Path.Combine(PathUtils.GetExeDirectoryAbsolute(), "gamedb", "gamedb.txt")); + Database.LoadDatabase(Path.Combine(PathUtils.ExeDirectoryPath, "gamedb", "gamedb.txt")); // TODO GL - a lot of disorganized wiring-up here // installed separately on Unix (via package manager or from https://developer.nvidia.com/cg-toolkit-download), look in $PATH - CGC.CGCBinPath = OSTailoredCode.IsUnixHost ? "cgc" : Path.Combine(PathUtils.GetDllDirectory(), "cgc.exe"); + CGC.CGCBinPath = OSTailoredCode.IsUnixHost ? "cgc" : Path.Combine(PathUtils.DllDirectoryPath, "cgc.exe"); PresentationPanel = new PresentationPanel(this, Config, GlobalWin.GL) { GraphicsControl = { MainWindow = true } @@ -1045,7 +1045,7 @@ namespace BizHawk.Client.EmuHawk private string ScreenshotPrefix() { var screenPath = Config.PathEntries.ScreenshotAbsolutePathFor(Game.System); - var name = Game.Name.FilesystemSafeName(); + var name = Game.FilesystemSafeName(); return Path.Combine(screenPath, name); } @@ -3241,7 +3241,7 @@ namespace BizHawk.Client.EmuHawk using var sfd = new SaveFileDialog(); if (Game != null) { - sfd.FileName = $"{Game.Name.FilesystemSafeName()}.{ext}"; // don't use Path.ChangeExtension, it might wreck game names with dots in them + sfd.FileName = $"{Game.FilesystemSafeName()}.{ext}"; // don't use Path.ChangeExtension, it might wreck game names with dots in them sfd.InitialDirectory = Config.PathEntries.AvAbsolutePath(); } else @@ -3467,7 +3467,7 @@ namespace BizHawk.Client.EmuHawk public string SaveStatePrefix() { - var name = Game.Name.FilesystemSafeName(); + var name = Game.FilesystemSafeName(); // Neshawk and Quicknes have incompatible savestates, store the name to keep them separate if (Emulator.SystemId == "NES") diff --git a/BizHawk.Client.EmuHawk/config/GB/BmpView.cs b/BizHawk.Client.EmuHawk/config/GB/BmpView.cs index 9a550b5f5f..7955885c5a 100644 --- a/BizHawk.Client.EmuHawk/config/GB/BmpView.cs +++ b/BizHawk.Client.EmuHawk/config/GB/BmpView.cs @@ -8,7 +8,7 @@ using System.Windows.Forms; using BizHawk.Client.Common; using BizHawk.Common; -using BizHawk.Common.PathExtensions; +using BizHawk.Emulation.Common; namespace BizHawk.Client.EmuHawk { @@ -103,7 +103,7 @@ namespace BizHawk.Client.EmuHawk using var sfd = new SaveFileDialog { - FileName = $"{Global.Game.Name.FilesystemSafeName()}-Palettes", + FileName = $"{Global.Game.FilesystemSafeName()}-Palettes", InitialDirectory = path, Filter = FilesystemFilterSet.Screenshots.ToString(), RestoreDirectory = true diff --git a/BizHawk.Client.EmuHawk/movie/PlayMovie.cs b/BizHawk.Client.EmuHawk/movie/PlayMovie.cs index 7c8a307666..952c512546 100644 --- a/BizHawk.Client.EmuHawk/movie/PlayMovie.cs +++ b/BizHawk.Client.EmuHawk/movie/PlayMovie.cs @@ -168,7 +168,7 @@ namespace BizHawk.Client.EmuHawk // Pull out matching names for (var i = 0; i < _movieList.Count; i++) { - if (_game.Name.FilesystemSafeName() == _movieList[i].GameName) + if (_game.FilesystemSafeName() == _movieList[i].GameName) { indices.Add(i); } diff --git a/BizHawk.Client.EmuHawk/movie/RecordMovie.cs b/BizHawk.Client.EmuHawk/movie/RecordMovie.cs index aeb880e92b..dab23d58c7 100644 --- a/BizHawk.Client.EmuHawk/movie/RecordMovie.cs +++ b/BizHawk.Client.EmuHawk/movie/RecordMovie.cs @@ -198,7 +198,7 @@ namespace BizHawk.Client.EmuHawk private void RecordMovie_Load(object sender, EventArgs e) { - RecordBox.Text = _game.Name.FilesystemSafeName(); + RecordBox.Text = _game.FilesystemSafeName(); StartFromCombo.SelectedIndex = 0; DefaultAuthorCheckBox.Checked = _config.UseDefaultAuthor; if (_config.UseDefaultAuthor) diff --git a/BizHawk.Client.EmuHawk/tools/CDL.cs b/BizHawk.Client.EmuHawk/tools/CDL.cs index a532d9cfeb..e851b9e5a6 100644 --- a/BizHawk.Client.EmuHawk/tools/CDL.cs +++ b/BizHawk.Client.EmuHawk/tools/CDL.cs @@ -494,7 +494,7 @@ namespace BizHawk.Client.EmuHawk try { _autoloading = true; - var autoResumeFile = $"{Global.Game.Name.FilesystemSafeName()}.cdl"; + var autoResumeFile = $"{Global.Game.FilesystemSafeName()}.cdl"; var autoResumeDir = Config.PathEntries.LogAbsolutePath(); var autoResumePath = Path.Combine(autoResumeDir, autoResumeFile); if (File.Exists(autoResumePath)) diff --git a/BizHawk.Client.EmuHawk/tools/HexEditor/HexEditor.cs b/BizHawk.Client.EmuHawk/tools/HexEditor/HexEditor.cs index b337c2e3fd..3d6b082348 100644 --- a/BizHawk.Client.EmuHawk/tools/HexEditor/HexEditor.cs +++ b/BizHawk.Client.EmuHawk/tools/HexEditor/HexEditor.cs @@ -970,7 +970,7 @@ namespace BizHawk.Client.EmuHawk , FileName = _domain.Name == "File on Disk" ? RomName - : Global.Game.Name.FilesystemSafeName() + : Global.Game.FilesystemSafeName() }; var result = sfd.ShowHawkDialog(); @@ -983,7 +983,7 @@ namespace BizHawk.Client.EmuHawk { FileName = _domain.Name == "File on Disk" ? $"{Path.GetFileNameWithoutExtension(RomName)}.txt" - : Global.Game.Name.FilesystemSafeName(), + : Global.Game.FilesystemSafeName(), Filter = new FilesystemFilterSet(FilesystemFilter.TextFiles).ToString(), InitialDirectory = RomDirectory, RestoreDirectory = true diff --git a/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs b/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs index a444840d0c..c44e5c1776 100644 --- a/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs +++ b/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs @@ -570,7 +570,7 @@ namespace BizHawk.Client.EmuHawk } else if (Global.Game != null) { - sfd.FileName = Global.Game.Name.FilesystemSafeName(); + sfd.FileName = Global.Game.FilesystemSafeName(); sfd.InitialDirectory = Config.PathEntries.LuaAbsolutePath(); } else diff --git a/BizHawk.Client.EmuHawk/tools/Macros/MacroInput.cs b/BizHawk.Client.EmuHawk/tools/Macros/MacroInput.cs index 5cb0943948..6be4f6f177 100644 --- a/BizHawk.Client.EmuHawk/tools/Macros/MacroInput.cs +++ b/BizHawk.Client.EmuHawk/tools/Macros/MacroInput.cs @@ -280,7 +280,7 @@ namespace BizHawk.Client.EmuHawk { return Config.PathEntries.AbsolutePathFor(Path.Combine( Config.PathEntries["Global", "Macros"].Path, - Global.Game.Name.FilesystemSafeName()), null); + Global.Game.FilesystemSafeName()), null); } #endregion diff --git a/BizHawk.Client.EmuHawk/tools/MultiDiskBundler/MultiDiskBundler.cs b/BizHawk.Client.EmuHawk/tools/MultiDiskBundler/MultiDiskBundler.cs index 262584d18b..f344885abb 100644 --- a/BizHawk.Client.EmuHawk/tools/MultiDiskBundler/MultiDiskBundler.cs +++ b/BizHawk.Client.EmuHawk/tools/MultiDiskBundler/MultiDiskBundler.cs @@ -240,7 +240,7 @@ namespace BizHawk.Client.EmuHawk filename = NameBox.Text; if (string.IsNullOrWhiteSpace(filename)) { - filename = Path.ChangeExtension(Global.Game.Name.FilesystemSafeName(), ".xml"); + filename = Path.ChangeExtension(Global.Game.FilesystemSafeName(), ".xml"); } initialDirectory = Path.GetDirectoryName(filename); @@ -265,27 +265,9 @@ namespace BizHawk.Client.EmuHawk /// Algorithm for Windows taken from https://stackoverflow.com/a/485516/7467292 public static string GetRelativePath(string fromPath, string toPath) { - if (OSTailoredCode.IsUnixHost) - { -#if true - return fromPath.IsSubfolderOf(toPath) - ? "./" + OSTailoredCode.SimpleSubshell("realpath", $"--relative-to=\"{toPath}\" \"{fromPath}\"", $"invalid path {fromPath} or missing realpath binary") - : fromPath; -#else // written for Unix port but may be useful for .NET Core - // algorithm taken from https://stackoverflow.com/a/340454/7467292 - var dirSepChar = Path.DirectorySeparatorChar; - var fromUri = new Uri(fromPath.EndsWith(dirSepChar.ToString()) ? fromPath : fromPath + dirSepChar); - var toUri = new Uri(toPath.EndsWith(dirSepChar.ToString()) ? toPath : toPath + dirSepChar); - if (fromUri.Scheme != toUri.Scheme) return toPath; - - var relativePath = Uri.UnescapeDataString(fromUri.MakeRelativeUri(toUri).ToString()); - return (toUri.Scheme.Equals(Uri.UriSchemeFile, StringComparison.OrdinalIgnoreCase) - ? relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar) - : relativePath - ).TrimEnd(dirSepChar); -#endif - } + if (OSTailoredCode.IsUnixHost) return fromPath.MakeRelativeTo(toPath); + //TODO merge this with the Windows implementation in PathExtensions.MakeRelativeTo static FileAttributes GetPathAttribute(string path1) { var di = new DirectoryInfo(path1.Split('|').First()); diff --git a/BizHawk.Client.EmuHawk/tools/NES/NameTableViewer.cs b/BizHawk.Client.EmuHawk/tools/NES/NameTableViewer.cs index 2d59b56d2a..8f7d168c59 100644 --- a/BizHawk.Client.EmuHawk/tools/NES/NameTableViewer.cs +++ b/BizHawk.Client.EmuHawk/tools/NES/NameTableViewer.cs @@ -4,7 +4,7 @@ using System.IO; using System.Drawing.Imaging; using BizHawk.Client.Common; -using BizHawk.Common.PathExtensions; +using BizHawk.Emulation.Common; namespace BizHawk.Client.EmuHawk { @@ -69,7 +69,7 @@ namespace BizHawk.Client.EmuHawk { using var sfd = new SaveFileDialog { - FileName = $"{Global.Game.Name.FilesystemSafeName()}-Nametables", + FileName = $"{Global.Game.FilesystemSafeName()}-Nametables", InitialDirectory = Global.Config.PathEntries.ScreenshotAbsolutePathFor("NES"), Filter = FilesystemFilterSet.Screenshots.ToString(), RestoreDirectory = true diff --git a/BizHawk.Client.EmuHawk/tools/NES/PaletteViewer.cs b/BizHawk.Client.EmuHawk/tools/NES/PaletteViewer.cs index dbc34c080e..9fb1c6b930 100644 --- a/BizHawk.Client.EmuHawk/tools/NES/PaletteViewer.cs +++ b/BizHawk.Client.EmuHawk/tools/NES/PaletteViewer.cs @@ -4,7 +4,7 @@ using System.IO; using System.Drawing.Imaging; using BizHawk.Client.Common; -using BizHawk.Common.PathExtensions; +using BizHawk.Emulation.Common; namespace BizHawk.Client.EmuHawk { @@ -75,7 +75,7 @@ namespace BizHawk.Client.EmuHawk { var sfd = new SaveFileDialog { - FileName = $"{Global.Game.Name.FilesystemSafeName()}-Palettes", + FileName = $"{Global.Game.FilesystemSafeName()}-Palettes", InitialDirectory = Global.Config.PathEntries.ScreenshotAbsolutePathFor("NES"), Filter = FilesystemFilterSet.Screenshots.ToString(), RestoreDirectory = true diff --git a/BizHawk.Client.EmuHawk/tools/NES/PatternViewer.cs b/BizHawk.Client.EmuHawk/tools/NES/PatternViewer.cs index badafbb3a2..9a7448ee5e 100644 --- a/BizHawk.Client.EmuHawk/tools/NES/PatternViewer.cs +++ b/BizHawk.Client.EmuHawk/tools/NES/PatternViewer.cs @@ -4,7 +4,7 @@ using System.IO; using System.Drawing.Imaging; using BizHawk.Client.Common; -using BizHawk.Common.PathExtensions; +using BizHawk.Emulation.Common; namespace BizHawk.Client.EmuHawk { @@ -37,7 +37,7 @@ namespace BizHawk.Client.EmuHawk { var sfd = new SaveFileDialog { - FileName = $"{Global.Game.Name.FilesystemSafeName()}-Patterns", + FileName = $"{Global.Game.FilesystemSafeName()}-Patterns", InitialDirectory = Global.Config.PathEntries.ScreenshotAbsolutePathFor("NES"), Filter = FilesystemFilterSet.Screenshots.ToString(), RestoreDirectory = true diff --git a/BizHawk.Client.EmuHawk/tools/NES/SpriteViewer.cs b/BizHawk.Client.EmuHawk/tools/NES/SpriteViewer.cs index 1f55225be3..14e3685b40 100644 --- a/BizHawk.Client.EmuHawk/tools/NES/SpriteViewer.cs +++ b/BizHawk.Client.EmuHawk/tools/NES/SpriteViewer.cs @@ -4,7 +4,7 @@ using System.IO; using System.Drawing.Imaging; using BizHawk.Client.Common; -using BizHawk.Common.PathExtensions; +using BizHawk.Emulation.Common; namespace BizHawk.Client.EmuHawk { @@ -39,7 +39,7 @@ namespace BizHawk.Client.EmuHawk { var sfd = new SaveFileDialog { - FileName = $"{Global.Game.Name.FilesystemSafeName()}-Sprites", + FileName = $"{Global.Game.FilesystemSafeName()}-Sprites", InitialDirectory = Global.Config.PathEntries.ScreenshotAbsolutePathFor("NES"), Filter = FilesystemFilterSet.Screenshots.ToString(), RestoreDirectory = true diff --git a/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.cs b/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.cs index aeeaf0c2be..92ca38b1f1 100644 --- a/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.cs +++ b/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.cs @@ -867,7 +867,7 @@ namespace BizHawk.Client.EmuHawk { return Path.Combine( Global.Config.PathEntries.MovieAbsolutePath(), - $"{Global.Game.Name.FilesystemSafeName()}.{TasMovie.Extension}"); + $"{Global.Game.FilesystemSafeName()}.{TasMovie.Extension}"); } private void SaveTas() diff --git a/BizHawk.Client.EmuHawk/tools/ToolFormBase.cs b/BizHawk.Client.EmuHawk/tools/ToolFormBase.cs index 459c61bf8b..220e14edcc 100644 --- a/BizHawk.Client.EmuHawk/tools/ToolFormBase.cs +++ b/BizHawk.Client.EmuHawk/tools/ToolFormBase.cs @@ -26,7 +26,7 @@ namespace BizHawk.Client.EmuHawk { FileName = !string.IsNullOrWhiteSpace(currentFile) ? Path.GetFileName(currentFile) - : $"{Global.Game.Name.FilesystemSafeName()}.{fileExt}", + : $"{Global.Game.FilesystemSafeName()}.{fileExt}", InitialDirectory = path, Filter = new FilesystemFilterSet(new FilesystemFilter(fileType, new[] { fileExt })).ToString(), RestoreDirectory = true @@ -52,7 +52,7 @@ namespace BizHawk.Client.EmuHawk { FileName = !string.IsNullOrWhiteSpace(currentFile) ? Path.GetFileName(currentFile) - : $"{Global.Game.Name.FilesystemSafeName()}.{fileExt}", + : $"{Global.Game.FilesystemSafeName()}.{fileExt}", InitialDirectory = path, Filter = new FilesystemFilterSet(new FilesystemFilter(fileType, new[] { fileExt })).ToString(), RestoreDirectory = true, diff --git a/BizHawk.Client.EmuHawk/tools/ToolManager.cs b/BizHawk.Client.EmuHawk/tools/ToolManager.cs index d60119fc3f..051c2b5ebe 100644 --- a/BizHawk.Client.EmuHawk/tools/ToolManager.cs +++ b/BizHawk.Client.EmuHawk/tools/ToolManager.cs @@ -871,7 +871,7 @@ namespace BizHawk.Client.EmuHawk f.Directory.Create(); } - return Path.Combine(path, $"{Global.Game.Name.FilesystemSafeName()}.cht"); + return Path.Combine(path, $"{Global.Game.FilesystemSafeName()}.cht"); } public void UpdateCheatRelatedTools(object sender, CheatCollection.CheatListEventArgs e) diff --git a/BizHawk.Client.EmuHawk/tools/TraceLogger.cs b/BizHawk.Client.EmuHawk/tools/TraceLogger.cs index a0a20b635d..4059a6ebf0 100644 --- a/BizHawk.Client.EmuHawk/tools/TraceLogger.cs +++ b/BizHawk.Client.EmuHawk/tools/TraceLogger.cs @@ -285,12 +285,12 @@ namespace BizHawk.Client.EmuHawk using var sfd = new SaveFileDialog(); if (LogFile == null) { - sfd.FileName = Global.Game.Name.FilesystemSafeName() + _extension; + sfd.FileName = Global.Game.FilesystemSafeName() + _extension; sfd.InitialDirectory = Config.PathEntries.LogAbsolutePath(); } else if (!string.IsNullOrWhiteSpace(LogFile.FullName)) { - sfd.FileName = Global.Game.Name.FilesystemSafeName(); + sfd.FileName = Global.Game.FilesystemSafeName(); sfd.InitialDirectory = Path.GetDirectoryName(LogFile.FullName); } else @@ -458,7 +458,7 @@ namespace BizHawk.Client.EmuHawk { FileBox.Visible = true; BrowseBox.Visible = true; - var name = Global.Game.Name.FilesystemSafeName(); + var name = Global.Game.FilesystemSafeName(); var filename = Path.Combine(Config.PathEntries.LogAbsolutePath(), name) + _extension; LogFile = new FileInfo(filename); if (LogFile.Directory != null && !LogFile.Directory.Exists) diff --git a/BizHawk.Common/Extensions/PathExtensions.cs b/BizHawk.Common/Extensions/PathExtensions.cs index 19f4019f6b..8c09d34e87 100644 --- a/BizHawk.Common/Extensions/PathExtensions.cs +++ b/BizHawk.Common/Extensions/PathExtensions.cs @@ -1,32 +1,20 @@ -#nullable disable - -using System; -using System.Linq; +using System; using System.IO; using System.Reflection; +using BizHawk.Common.StringExtensions; + namespace BizHawk.Common.PathExtensions { public static class PathExtensions { - public static string RemoveInvalidFileSystemChars(this string name) + /// iff indicates a child of , with being returned if either path is + /// algorithm for Windows taken from https://stackoverflow.com/a/7710620/7467292 + public static bool IsSubfolderOf(this string? childPath, string? parentPath) { - var newStr = name; - var chars = Path.GetInvalidFileNameChars(); - return chars.Aggregate(newStr, (current, c) => current.Replace(c.ToString(), "")); - } + if (childPath == null || parentPath == null) return false; + if (childPath == parentPath || childPath.StartsWith($"{parentPath}{Path.DirectorySeparatorChar}")) return true; - /// - /// Decides if a path is non-empty, not . and not .\ - /// - public static bool PathIsSet(this string? path) - { - return !string.IsNullOrWhiteSpace(path) && path != "." && path != ".\\"; - } - - /// Algorithm for Windows taken from https://stackoverflow.com/a/7710620/7467292 - public static bool IsSubfolderOf(this string childPath, string parentPath) - { if (OSTailoredCode.IsUnixHost) { #if true @@ -36,7 +24,7 @@ namespace BizHawk.Common.PathExtensions var parentUriPath = new Uri(parentPath.TrimEnd('.')).AbsolutePath.TrimEnd('/'); try { - for (var childUri = new DirectoryInfo(childPath).Parent; childUri != null; childUri = childUri?.Parent) + for (var childUri = new DirectoryInfo(childPath).Parent; childUri != null; childUri = childUri.Parent) { if (new Uri(childUri.FullName).AbsolutePath.TrimEnd('/') == parentUriPath) return true; } @@ -54,80 +42,69 @@ namespace BizHawk.Common.PathExtensions { if (new Uri(childUri.FullName) == parentUri) return true; } - return false; } - public static string MakeRelativeTo(this string absolutePath, string basePath) - { - if (absolutePath.IsSubfolderOf(basePath)) - { - return absolutePath.Replace(basePath, "."); - } + /// the absolute path equivalent to which contains %exe% (expanded) as a prefix + /// + /// returned string omits trailing slash
+ /// note that the returned string is an absolute path and not a relative path; but TODO it was intended to be relative + ///
+ public static string MakeProgramRelativePath(this string path) => Path.Combine(PathUtils.ExeDirectoryPath, path); - return absolutePath; + /// the relative path which is equivalent to when the CWD is , or if either path is + /// returned string omits trailing slash; implementation calls for you + public static string? MakeRelativeTo(this string? absolutePath, string? basePath) + { + if (absolutePath == null || basePath == null) return null; + if (!absolutePath.IsSubfolderOf(basePath)) return absolutePath; + if (!OSTailoredCode.IsUnixHost) return absolutePath.Replace(basePath, ".").RemoveSuffix(Path.DirectorySeparatorChar); +#if true // Unix implementation using realpath + var realpathOutput = OSTailoredCode.SimpleSubshell("realpath", $"--relative-to=\"{basePath}\" \"{absolutePath}\"", $"invalid path {absolutePath}, invalid path {basePath}, or missing realpath binary"); + return !realpathOutput.StartsWith("../") && realpathOutput != "." && realpathOutput != ".." ? $"./{realpathOutput}" : realpathOutput; +#else // for some reason there were two Unix implementations in the codebase before me? --yoshi + // alt. #1 + if (!IsSubfolder(basePath, absolutePath)) return OSTailoredCode.IsUnixHost && basePath.TrimEnd('.') == $"{absolutePath}/" ? "." : absolutePath; + return OSTailoredCode.IsUnixHost ? absolutePath.Replace(basePath.TrimEnd('.'), "./") : absolutePath.Replace(basePath, "."); + + // alt. #2; algorithm taken from https://stackoverflow.com/a/340454/7467292 + var dirSepChar = Path.DirectorySeparatorChar; + var fromUri = new Uri(absolutePath.EndsWith(dirSepChar.ToString()) ? absolutePath : absolutePath + dirSepChar); + var toUri = new Uri(basePath.EndsWith(dirSepChar.ToString()) ? basePath : basePath + dirSepChar); + if (fromUri.Scheme != toUri.Scheme) return basePath; + + var relativePath = Uri.UnescapeDataString(fromUri.MakeRelativeUri(toUri).ToString()); + return (toUri.Scheme.Equals(Uri.UriSchemeFile, StringComparison.OrdinalIgnoreCase) + ? relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar) + : relativePath + ).TrimEnd(dirSepChar); +#endif } - public static string FilesystemSafeName(this string name) - { - name ??= ""; + /// iff is blank, or is "." (relative path to CWD), regardless of trailing slash + public static bool PathIsSet(this string path) => !string.IsNullOrWhiteSpace(path) && path != "." && path != "./" && path != ".\\"; - var filesystemSafeName = name - .Replace("|", "+") - .Replace(":", " -") // Path.GetFileName scraps everything to the left of a colon unfortunately, so we need this hack here - .Replace("\"", "") // Ivan IronMan Stewart's Super Off-Road has quotes in game name - .Replace("/", "+"); // Mario Bros / Duck hunt has a slash in the name which GetDirectoryName and GetFileName treat as if it were a folder - - // zero 06-nov-2015 - regarding the below, i changed my mind. for libretro i want subdirectories here. - var filesystemDir = Path.GetDirectoryName(filesystemSafeName); - filesystemSafeName = Path.GetFileName(filesystemSafeName); - - filesystemSafeName = filesystemSafeName.RemoveInvalidFileSystemChars(); - - // zero 22-jul-2012 - i don't think this is used the same way it used to. game.Name shouldn't be a path, so this stuff is illogical. - // if game.Name is a path, then someone should have made it not-a-path already. - // return Path.Combine(Path.GetDirectoryName(filesystemSafeName), Path.GetFileNameWithoutExtension(filesystemSafeName)); - - // adelikat: - // This hack is to prevent annoying things like Super Mario Bros..bk2 - if (filesystemSafeName.EndsWith(".")) - { - filesystemSafeName = filesystemSafeName.Remove(filesystemSafeName.Length - 1, 1); - } - - return Path.Combine(filesystemDir, filesystemSafeName); - } - - - // TODO: this always makes an absolute path! - // Needs to be fixed, the intent was to turn an absolute path - // into one relative to the exe - // for instance: C:\BizHawk\Lua becomes .\Lua (if EmuHawk.Exe is in C:\BizHawk) - /// - /// Makes a path relative to the %exe% directory - /// - public static string MakeProgramRelativePath(this string path) - { - return Path.Combine(PathUtils.GetExeDirectoryAbsolute(), path); - } + public static string RemoveInvalidFileSystemChars(this string name) => string.Concat(name.Split(Path.GetInvalidFileNameChars())); } public static class PathUtils { - public static string GetExeDirectoryAbsolute() - { - var path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); - if (path.EndsWith(Path.DirectorySeparatorChar.ToString())) - { - path = path.Remove(path.Length - 1, 1); - } + /// absolute path of the dll dir (sibling of EmuHawk.exe) + /// returned string omits trailing slash + public static readonly string DllDirectoryPath; - return path; - } + /// absolute path of the parent dir of DiscoHawk.exe/EmuHawk.exe, commonly referred to as %exe% though none of our code adds it to the environment + /// returned string omits trailing slash + public static readonly string ExeDirectoryPath; - public static string GetDllDirectory() + static PathUtils() { - return Path.Combine(GetExeDirectoryAbsolute(), "dll"); + var dirPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location); + ExeDirectoryPath = OSTailoredCode.IsUnixHost + ? (string.IsNullOrEmpty(dirPath) || dirPath == "/" ? string.Empty : dirPath) + : (string.IsNullOrEmpty(dirPath) ? throw new Exception("failed to get location of executable, very bad things must have happened") : dirPath.RemoveSuffix('\\')); + DllDirectoryPath = Path.Combine(OSTailoredCode.IsUnixHost && ExeDirectoryPath == string.Empty ? "/" : ExeDirectoryPath, "dll"); + // yes, this is a lot of extra code to make sure BizHawk can run in `/` on Unix, but I've made up for it by caching these for the program lifecycle --yoshi } } } diff --git a/BizHawk.Common/Extensions/StringExtensions.cs b/BizHawk.Common/Extensions/StringExtensions.cs index bfa8149f7b..3deba6cc8e 100644 --- a/BizHawk.Common/Extensions/StringExtensions.cs +++ b/BizHawk.Common/Extensions/StringExtensions.cs @@ -201,5 +201,11 @@ namespace BizHawk.Common.StringExtensions } #pragma warning restore CS8602 + + /// with the last char removed (iff the last char is , otherwise unmodified) + public static string RemoveSuffix(this string str, char suffix) => str[str.Length - 1] == suffix ? str.Substring(0, str.Length - 1) : str; + + /// with the trailing substring removed (iff ends with , otherwise unmodified) + public static string RemoveSuffix(this string str, string suffix) => str.EndsWith(suffix) ? str.Substring(0, str.Length - suffix.Length) : str; } } diff --git a/BizHawk.Emulation.Common/Extensions.cs b/BizHawk.Emulation.Common/Extensions.cs index 57f39b20b2..dcf2998b2c 100644 --- a/BizHawk.Emulation.Common/Extensions.cs +++ b/BizHawk.Emulation.Common/Extensions.cs @@ -1,9 +1,13 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; +using BizHawk.Common.PathExtensions; +using BizHawk.Common.StringExtensions; + namespace BizHawk.Emulation.Common { public static class EmulatorExtensions @@ -341,5 +345,17 @@ namespace BizHawk.Emulation.Common return buttons; } + + public static string FilesystemSafeName(this GameInfo game) + { + var pass1 = game.Name + .Replace('/', '+') // '/' is the path dir separator, obviously (methods in Path will treat it as such, even on Windows) + .Replace('|', '+') // '|' is the filename-member separator for archives in HawkFile + .Replace(":", " -") // ':' is the path separator in lists (Path.GetFileName will drop all but the last entry in such a list) + .Replace("\"", ""); // '"' is just annoying as it needs escaping on the command-line + var filesystemDir = Path.GetDirectoryName(pass1); + var pass2 = Path.GetFileName(pass1).RemoveInvalidFileSystemChars(); + return Path.Combine(filesystemDir, pass2.RemoveSuffix('.')); // trailing '.' would be duplicated when file extension is added + } } } \ No newline at end of file