From 33a14327b79afc489f77f3f3a1324e9455e658fe Mon Sep 17 00:00:00 2001 From: adelikat Date: Sun, 15 Mar 2020 15:45:11 -0500 Subject: [PATCH] move most logic from PathManager to extension methods in BizHawk.Common --- BizHawk.Client.Common/PathManager.cs | 95 +----------------- .../config/PathEntryCollection.cs | 3 +- .../config/PathEntryCollectionExtensions.cs | 20 ++-- BizHawk.Client.Common/lua/LuaFileList.cs | 4 +- .../conversions/MovieConversionExtensions.cs | 3 +- .../AVOut/SynclessRecordingTools.cs | 3 +- BizHawk.Client.EmuHawk/MainForm.Events.cs | 3 +- BizHawk.Client.EmuHawk/MainForm.cs | 7 +- BizHawk.Client.EmuHawk/config/GB/BmpView.cs | 3 +- BizHawk.Client.EmuHawk/movie/PlayMovie.cs | 3 +- BizHawk.Client.EmuHawk/movie/RecordMovie.cs | 3 +- BizHawk.Client.EmuHawk/tools/CDL.cs | 3 +- .../tools/HexEditor/HexEditor.cs | 5 +- .../tools/Lua/LuaConsole.cs | 3 +- .../tools/Macros/MacroInput.cs | 3 +- .../MultiDiskBundler/MultiDiskBundler.cs | 5 +- .../tools/NES/NameTableViewer.cs | 3 +- .../tools/NES/PaletteViewer.cs | 3 +- .../tools/NES/PatternViewer.cs | 3 +- .../tools/NES/SpriteViewer.cs | 3 +- .../tools/TAStudio/TAStudio.cs | 3 +- BizHawk.Client.EmuHawk/tools/ToolFormBase.cs | 10 +- BizHawk.Client.EmuHawk/tools/ToolManager.cs | 3 +- BizHawk.Client.EmuHawk/tools/TraceLogger.cs | 9 +- BizHawk.Common/Extensions/PathExtensions.cs | 98 +++++++++++++++++++ 25 files changed, 165 insertions(+), 136 deletions(-) create mode 100644 BizHawk.Common/Extensions/PathExtensions.cs diff --git a/BizHawk.Client.Common/PathManager.cs b/BizHawk.Client.Common/PathManager.cs index 932ec07f8d..32c707939b 100644 --- a/BizHawk.Client.Common/PathManager.cs +++ b/BizHawk.Client.Common/PathManager.cs @@ -1,11 +1,6 @@ -using System; -using System.Linq; -using System.IO; +using System.IO; using System.Reflection; -using BizHawk.Common; -using BizHawk.Emulation.Common; - namespace BizHawk.Client.Common { public static class PathManager @@ -53,93 +48,5 @@ namespace BizHawk.Client.Common { DefaultIniPath = newDefaultIniPath; } - - // Decides if a path is non-empty, not . and not .\ - public static bool PathIsSet(string path) - { - if (!string.IsNullOrWhiteSpace(path)) - { - return path != "." && path != ".\\"; - } - - return false; - } - - public static string RemoveInvalidFileSystemChars(string name) - { - var newStr = name; - var chars = Path.GetInvalidFileNameChars(); - return chars.Aggregate(newStr, (current, c) => current.Replace(c.ToString(), "")); - } - - public static string FilesystemSafeName(GameInfo game) - { - var filesystemSafeName = game.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 = RemoveInvalidFileSystemChars(filesystemSafeName); - - // 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); - } - - public static string MakeRelativeTo(string absolutePath, string basePath) - { - if (IsSubfolder(basePath, absolutePath)) - { - return absolutePath.Replace(basePath, "."); - } - - return absolutePath; - } - - /// Algorithm for Windows taken from https://stackoverflow.com/a/7710620/7467292 - public static bool IsSubfolder(string parentPath, string childPath) - { - if (OSTailoredCode.IsUnixHost) - { -#if true - return OSTailoredCode.SimpleSubshell("realpath", $"-L \"{childPath}\"", $"invalid path {childPath} or missing realpath binary") - .StartsWith(OSTailoredCode.SimpleSubshell("realpath", $"-L \"{parentPath}\"", $"invalid path {parentPath} or missing realpath binary")); -#else // written for Unix port but may be useful for Windows when moving to .NET Core - var parentUriPath = new Uri(parentPath.TrimEnd('.')).AbsolutePath.TrimEnd('/'); - try - { - for (var childUri = new DirectoryInfo(childPath).Parent; childUri != null; childUri = childUri?.Parent) - { - if (new Uri(childUri.FullName).AbsolutePath.TrimEnd('/') == parentUriPath) return true; - } - } - catch - { - // ignored - } - return false; -#endif - } - var parentUri = new Uri(parentPath); - for (var childUri = new DirectoryInfo(childPath).Parent; childUri != null; childUri = childUri?.Parent) - { - if (new Uri(childUri.FullName) == parentUri) return true; - } - return false; - } } } diff --git a/BizHawk.Client.Common/config/PathEntryCollection.cs b/BizHawk.Client.Common/config/PathEntryCollection.cs index db77c805f5..dec8227ac2 100644 --- a/BizHawk.Client.Common/config/PathEntryCollection.cs +++ b/BizHawk.Client.Common/config/PathEntryCollection.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using BizHawk.Common.PathExtensions; using Newtonsoft.Json; namespace BizHawk.Client.Common @@ -47,7 +48,7 @@ namespace BizHawk.Client.Common } // we don't have anything for the system in question. add a set of stock paths - var systemPath = $"{PathManager.RemoveInvalidFileSystemChars(system)}_INTERIM"; + var systemPath = $"{system.RemoveInvalidFileSystemChars()}_INTERIM"; var systemDisp = $"{system} (INTERIM)"; Paths.AddRange(new[] diff --git a/BizHawk.Client.Common/config/PathEntryCollectionExtensions.cs b/BizHawk.Client.Common/config/PathEntryCollectionExtensions.cs index 4d79d08e75..c4e674be34 100644 --- a/BizHawk.Client.Common/config/PathEntryCollectionExtensions.cs +++ b/BizHawk.Client.Common/config/PathEntryCollectionExtensions.cs @@ -1,6 +1,7 @@ using System; using System.IO; using BizHawk.Common; +using BizHawk.Common.PathExtensions; using BizHawk.Emulation.Common; namespace BizHawk.Client.Common @@ -208,11 +209,11 @@ namespace BizHawk.Client.Common var path = collection[systemId, "ROM"]; - if (path == null || !PathManager.PathIsSet(path.Path)) + if (!path.Path.PathIsSet()) { path = collection["Global", "ROM"]; - if (path != null && PathManager.PathIsSet(path.Path)) + if (path.Path.PathIsSet()) { return collection.AbsolutePathFor(path.Path, null); } @@ -223,7 +224,7 @@ namespace BizHawk.Client.Common public static string SaveRamAbsolutePath(this PathEntryCollection collection, GameInfo game, bool movieIsActive) { - var name = PathManager.FilesystemSafeName(game); + var name = game.Name.FilesystemSafeName(); if (movieIsActive) { name += $".{Path.GetFileNameWithoutExtension(Global.MovieSession.Movie.Filename)}"; @@ -238,11 +239,11 @@ namespace BizHawk.Client.Common // Shenanigans public static string RetroSaveRamAbsolutePath(this PathEntryCollection collection, GameInfo game, bool movieIsActive, string movieFilename) { - var name = PathManager.FilesystemSafeName(game); + var name = game.Name.FilesystemSafeName(); name = Path.GetDirectoryName(name); if (name == "") { - name = PathManager.FilesystemSafeName(game); + name = game.Name.FilesystemSafeName(); } if (movieIsActive) @@ -259,11 +260,11 @@ namespace BizHawk.Client.Common // Shenanigans public static string RetroSystemAbsolutePath(this PathEntryCollection collection, GameInfo game) { - var name = PathManager.FilesystemSafeName(game); + var name = game.Name.FilesystemSafeName(); name = Path.GetDirectoryName(name); if (string.IsNullOrEmpty(name)) { - name = PathManager.FilesystemSafeName(game); + name = game.Name.FilesystemSafeName(); } var pathEntry = collection[game.System, "System"] @@ -317,7 +318,10 @@ namespace BizHawk.Client.Common ? collection.GlobalBaseAbsolutePath() : collection.AbsolutePathFor(collection.BaseFor(system), system); #if true - if (!PathManager.IsSubfolder(parentPath, absolutePath)) return absolutePath; + if (!absolutePath.IsSubfolderOf(parentPath)) + { + return absolutePath; + } return OSTailoredCode.IsUnixHost ? "./" + OSTailoredCode.SimpleSubshell("realpath", $"--relative-to=\"{parentPath}\" \"{absolutePath}\"", $"invalid path {absolutePath} or missing realpath binary") diff --git a/BizHawk.Client.Common/lua/LuaFileList.cs b/BizHawk.Client.Common/lua/LuaFileList.cs index 8bd16ac3b0..c907901320 100644 --- a/BizHawk.Client.Common/lua/LuaFileList.cs +++ b/BizHawk.Client.Common/lua/LuaFileList.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Text; +using BizHawk.Common.PathExtensions; namespace BizHawk.Client.Common { @@ -138,8 +139,7 @@ namespace BizHawk.Client.Common sb .Append(file.Enabled ? "1" : "0") .Append(' ') - .Append(PathManager.MakeRelativeTo(Global.Config.PathEntries.AbsolutePathFor(file.Path, ""), - Path.GetDirectoryName(path))) + .Append(Global.Config.PathEntries.AbsolutePathFor(file.Path, "").MakeRelativeTo(Path.GetDirectoryName(path))) .AppendLine(); } } diff --git a/BizHawk.Client.Common/movie/conversions/MovieConversionExtensions.cs b/BizHawk.Client.Common/movie/conversions/MovieConversionExtensions.cs index 850a53b75b..fd34d851f7 100644 --- a/BizHawk.Client.Common/movie/conversions/MovieConversionExtensions.cs +++ b/BizHawk.Client.Common/movie/conversions/MovieConversionExtensions.cs @@ -2,6 +2,7 @@ using System.IO; using System.Linq; +using BizHawk.Common.PathExtensions; using BizHawk.Emulation.Common; using BizHawk.Emulation.Cores.Nintendo.Gameboy; using BizHawk.Emulation.Cores.Nintendo.GBHawk; @@ -292,7 +293,7 @@ namespace BizHawk.Client.Common.MovieConversionExtensions if (Global.Game != null) { - movie.GameName = PathManager.FilesystemSafeName(Global.Game); + movie.GameName = Global.Game.Name.FilesystemSafeName(); movie.Hash = Global.Game.Hash; if (Global.Game.FirmwareHash != null) { diff --git a/BizHawk.Client.EmuHawk/AVOut/SynclessRecordingTools.cs b/BizHawk.Client.EmuHawk/AVOut/SynclessRecordingTools.cs index 74cc3bd490..3393b16ec1 100644 --- a/BizHawk.Client.EmuHawk/AVOut/SynclessRecordingTools.cs +++ b/BizHawk.Client.EmuHawk/AVOut/SynclessRecordingTools.cs @@ -6,6 +6,7 @@ using System.Windows.Forms; using BizHawk.Bizware.BizwareGL; using BizHawk.Client.Common; +using BizHawk.Common.PathExtensions; namespace BizHawk.Client.EmuHawk { @@ -32,7 +33,7 @@ namespace BizHawk.Client.EmuHawk { var ofd = new OpenFileDialog { - FileName = $"{PathManager.FilesystemSafeName(Global.Game)}.syncless.txt", + FileName = $"{Global.Game.Name.FilesystemSafeName()}.syncless.txt", InitialDirectory = Global.Config.PathEntries.AvAbsolutePath() }; diff --git a/BizHawk.Client.EmuHawk/MainForm.Events.cs b/BizHawk.Client.EmuHawk/MainForm.Events.cs index 0bd03f2970..3bc2824e64 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Events.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Events.cs @@ -19,6 +19,7 @@ using BizHawk.Client.EmuHawk.ToolExtensions; using BizHawk.Emulation.Cores.Computers.AppleII; using BizHawk.Client.ApiHawk; using BizHawk.Common; +using BizHawk.Common.PathExtensions; using BizHawk.Emulation.Cores.Atari.A7800Hawk; using BizHawk.Emulation.Cores.Computers.Commodore64; using BizHawk.Emulation.Cores.Nintendo.Gameboy; @@ -535,7 +536,7 @@ namespace BizHawk.Client.EmuHawk var filename = MovieSession.Movie.Filename; if (string.IsNullOrWhiteSpace(filename)) { - filename = PathManager.FilesystemSafeName(Game); + filename = Game.Name.FilesystemSafeName(); } var file = ToolFormBase.SaveFileDialog( diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index 9c47a64ec1..d72ba21497 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -27,6 +27,7 @@ using BizHawk.Emulation.Cores.Nintendo.GBHawkLink; using BizHawk.Client.EmuHawk.ToolExtensions; using BizHawk.Client.EmuHawk.CoreExtensions; using BizHawk.Client.ApiHawk; +using BizHawk.Common.PathExtensions; using BizHawk.Emulation.Common.Base_Implementations; using BizHawk.Emulation.Cores.Nintendo.SNES9X; using BizHawk.Emulation.Cores.Consoles.SNK; @@ -1045,7 +1046,7 @@ namespace BizHawk.Client.EmuHawk private string ScreenshotPrefix() { var screenPath = Config.PathEntries.ScreenshotAbsolutePathFor(Game.System); - var name = PathManager.FilesystemSafeName(Game); + var name = Game.Name.FilesystemSafeName(); return Path.Combine(screenPath, name); } @@ -3241,7 +3242,7 @@ namespace BizHawk.Client.EmuHawk using var sfd = new SaveFileDialog(); if (Game != null) { - sfd.FileName = $"{PathManager.FilesystemSafeName(Game)}.{ext}"; // don't use Path.ChangeExtension, it might wreck game names with dots in them + sfd.FileName = $"{Game.Name.FilesystemSafeName()}.{ext}"; // don't use Path.ChangeExtension, it might wreck game names with dots in them sfd.InitialDirectory = Config.PathEntries.AvAbsolutePath(); } else @@ -3467,7 +3468,7 @@ namespace BizHawk.Client.EmuHawk public string SaveStatePrefix() { - var name = PathManager.FilesystemSafeName(Game); + var name = Game.Name.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 331bd2c575..9a550b5f5f 100644 --- a/BizHawk.Client.EmuHawk/config/GB/BmpView.cs +++ b/BizHawk.Client.EmuHawk/config/GB/BmpView.cs @@ -8,6 +8,7 @@ using System.Windows.Forms; using BizHawk.Client.Common; using BizHawk.Common; +using BizHawk.Common.PathExtensions; namespace BizHawk.Client.EmuHawk { @@ -102,7 +103,7 @@ namespace BizHawk.Client.EmuHawk using var sfd = new SaveFileDialog { - FileName = $"{PathManager.FilesystemSafeName(Global.Game)}-Palettes", + FileName = $"{Global.Game.Name.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 69469a8b40..7c8a307666 100644 --- a/BizHawk.Client.EmuHawk/movie/PlayMovie.cs +++ b/BizHawk.Client.EmuHawk/movie/PlayMovie.cs @@ -9,6 +9,7 @@ using System.Windows.Forms; using BizHawk.Client.Common; using BizHawk.Common; +using BizHawk.Common.PathExtensions; using BizHawk.Emulation.Common; namespace BizHawk.Client.EmuHawk @@ -167,7 +168,7 @@ namespace BizHawk.Client.EmuHawk // Pull out matching names for (var i = 0; i < _movieList.Count; i++) { - if (PathManager.FilesystemSafeName(_game) == _movieList[i].GameName) + if (_game.Name.FilesystemSafeName() == _movieList[i].GameName) { indices.Add(i); } diff --git a/BizHawk.Client.EmuHawk/movie/RecordMovie.cs b/BizHawk.Client.EmuHawk/movie/RecordMovie.cs index 228773eb5d..aeb880e92b 100644 --- a/BizHawk.Client.EmuHawk/movie/RecordMovie.cs +++ b/BizHawk.Client.EmuHawk/movie/RecordMovie.cs @@ -6,6 +6,7 @@ using System.Linq; using BizHawk.Emulation.Common; using BizHawk.Client.Common; using BizHawk.Client.Common.MovieConversionExtensions; +using BizHawk.Common.PathExtensions; namespace BizHawk.Client.EmuHawk { @@ -197,7 +198,7 @@ namespace BizHawk.Client.EmuHawk private void RecordMovie_Load(object sender, EventArgs e) { - RecordBox.Text = PathManager.FilesystemSafeName(_game); + RecordBox.Text = _game.Name.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 4343e9c8bf..a532d9cfeb 100644 --- a/BizHawk.Client.EmuHawk/tools/CDL.cs +++ b/BizHawk.Client.EmuHawk/tools/CDL.cs @@ -6,6 +6,7 @@ using BizHawk.Emulation.Common; using BizHawk.Client.Common; using BizHawk.Client.EmuHawk.ToolExtensions; +using BizHawk.Common.PathExtensions; // TODO - select which memorydomains go out to the CDL file. will this cause a problem when re-importing it? // perhaps missing domains shouldn't fail a check @@ -493,7 +494,7 @@ namespace BizHawk.Client.EmuHawk try { _autoloading = true; - var autoResumeFile = $"{PathManager.FilesystemSafeName(Global.Game)}.cdl"; + var autoResumeFile = $"{Global.Game.Name.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 a019c46159..b337c2e3fd 100644 --- a/BizHawk.Client.EmuHawk/tools/HexEditor/HexEditor.cs +++ b/BizHawk.Client.EmuHawk/tools/HexEditor/HexEditor.cs @@ -12,6 +12,7 @@ using BizHawk.Common; using BizHawk.Common.NumberExtensions; using BizHawk.Common.StringExtensions; using BizHawk.Common.IOExtensions; +using BizHawk.Common.PathExtensions; using BizHawk.Emulation.Common; using BizHawk.Client.Common; using BizHawk.Client.EmuHawk.ToolExtensions; @@ -969,7 +970,7 @@ namespace BizHawk.Client.EmuHawk , FileName = _domain.Name == "File on Disk" ? RomName - : PathManager.FilesystemSafeName(Global.Game) + : Global.Game.Name.FilesystemSafeName() }; var result = sfd.ShowHawkDialog(); @@ -982,7 +983,7 @@ namespace BizHawk.Client.EmuHawk { FileName = _domain.Name == "File on Disk" ? $"{Path.GetFileNameWithoutExtension(RomName)}.txt" - : PathManager.FilesystemSafeName(Global.Game), + : Global.Game.Name.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 edd6eb2943..acb223c3d1 100644 --- a/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs +++ b/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs @@ -11,6 +11,7 @@ using System.Windows.Forms; using BizHawk.Client.Common; using BizHawk.Client.EmuHawk.ToolExtensions; using BizHawk.Common; +using BizHawk.Common.PathExtensions; using BizHawk.Emulation.Common; namespace BizHawk.Client.EmuHawk @@ -569,7 +570,7 @@ namespace BizHawk.Client.EmuHawk } else if (Global.Game != null) { - sfd.FileName = PathManager.FilesystemSafeName(Global.Game); + sfd.FileName = Global.Game.Name.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 05e54f60b5..5cb0943948 100644 --- a/BizHawk.Client.EmuHawk/tools/Macros/MacroInput.cs +++ b/BizHawk.Client.EmuHawk/tools/Macros/MacroInput.cs @@ -6,6 +6,7 @@ using System.IO; using BizHawk.Emulation.Common; using BizHawk.Client.Common; using BizHawk.Client.EmuHawk.ToolExtensions; +using BizHawk.Common.PathExtensions; namespace BizHawk.Client.EmuHawk { @@ -279,7 +280,7 @@ namespace BizHawk.Client.EmuHawk { return Config.PathEntries.AbsolutePathFor(Path.Combine( Config.PathEntries["Global", "Macros"].Path, - PathManager.FilesystemSafeName(Global.Game)), null); + Global.Game.Name.FilesystemSafeName()), null); } #endregion diff --git a/BizHawk.Client.EmuHawk/tools/MultiDiskBundler/MultiDiskBundler.cs b/BizHawk.Client.EmuHawk/tools/MultiDiskBundler/MultiDiskBundler.cs index 7c0fb69e0e..262584d18b 100644 --- a/BizHawk.Client.EmuHawk/tools/MultiDiskBundler/MultiDiskBundler.cs +++ b/BizHawk.Client.EmuHawk/tools/MultiDiskBundler/MultiDiskBundler.cs @@ -10,6 +10,7 @@ using System.Xml.Linq; using BizHawk.Emulation.Common; using BizHawk.Client.Common; using BizHawk.Common; +using BizHawk.Common.PathExtensions; using BizHawk.Emulation.Cores.Sega.MasterSystem; namespace BizHawk.Client.EmuHawk @@ -239,7 +240,7 @@ namespace BizHawk.Client.EmuHawk filename = NameBox.Text; if (string.IsNullOrWhiteSpace(filename)) { - filename = Path.ChangeExtension(PathManager.FilesystemSafeName(Global.Game), ".xml"); + filename = Path.ChangeExtension(Global.Game.Name.FilesystemSafeName(), ".xml"); } initialDirectory = Path.GetDirectoryName(filename); @@ -267,7 +268,7 @@ namespace BizHawk.Client.EmuHawk if (OSTailoredCode.IsUnixHost) { #if true - return PathManager.IsSubfolder(toPath, fromPath) + 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 diff --git a/BizHawk.Client.EmuHawk/tools/NES/NameTableViewer.cs b/BizHawk.Client.EmuHawk/tools/NES/NameTableViewer.cs index 3b25288014..2d59b56d2a 100644 --- a/BizHawk.Client.EmuHawk/tools/NES/NameTableViewer.cs +++ b/BizHawk.Client.EmuHawk/tools/NES/NameTableViewer.cs @@ -4,6 +4,7 @@ using System.IO; using System.Drawing.Imaging; using BizHawk.Client.Common; +using BizHawk.Common.PathExtensions; namespace BizHawk.Client.EmuHawk { @@ -68,7 +69,7 @@ namespace BizHawk.Client.EmuHawk { using var sfd = new SaveFileDialog { - FileName = $"{PathManager.FilesystemSafeName(Global.Game)}-Nametables", + FileName = $"{Global.Game.Name.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 43a45ba89a..dbc34c080e 100644 --- a/BizHawk.Client.EmuHawk/tools/NES/PaletteViewer.cs +++ b/BizHawk.Client.EmuHawk/tools/NES/PaletteViewer.cs @@ -4,6 +4,7 @@ using System.IO; using System.Drawing.Imaging; using BizHawk.Client.Common; +using BizHawk.Common.PathExtensions; namespace BizHawk.Client.EmuHawk { @@ -74,7 +75,7 @@ namespace BizHawk.Client.EmuHawk { var sfd = new SaveFileDialog { - FileName = $"{PathManager.FilesystemSafeName(Global.Game)}-Palettes", + FileName = $"{Global.Game.Name.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 383667a460..badafbb3a2 100644 --- a/BizHawk.Client.EmuHawk/tools/NES/PatternViewer.cs +++ b/BizHawk.Client.EmuHawk/tools/NES/PatternViewer.cs @@ -4,6 +4,7 @@ using System.IO; using System.Drawing.Imaging; using BizHawk.Client.Common; +using BizHawk.Common.PathExtensions; namespace BizHawk.Client.EmuHawk { @@ -36,7 +37,7 @@ namespace BizHawk.Client.EmuHawk { var sfd = new SaveFileDialog { - FileName = $"{PathManager.FilesystemSafeName(Global.Game)}-Patterns", + FileName = $"{Global.Game.Name.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 5ff4f746d4..1f55225be3 100644 --- a/BizHawk.Client.EmuHawk/tools/NES/SpriteViewer.cs +++ b/BizHawk.Client.EmuHawk/tools/NES/SpriteViewer.cs @@ -4,6 +4,7 @@ using System.IO; using System.Drawing.Imaging; using BizHawk.Client.Common; +using BizHawk.Common.PathExtensions; namespace BizHawk.Client.EmuHawk { @@ -38,7 +39,7 @@ namespace BizHawk.Client.EmuHawk { var sfd = new SaveFileDialog { - FileName = $"{PathManager.FilesystemSafeName(Global.Game)}-Sprites", + FileName = $"{Global.Game.Name.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 a03aa0f3be..6ada6b56f4 100644 --- a/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.cs +++ b/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.cs @@ -11,6 +11,7 @@ using BizHawk.Emulation.Common; using BizHawk.Client.Common; using BizHawk.Client.Common.MovieConversionExtensions; using BizHawk.Client.EmuHawk.ToolExtensions; +using BizHawk.Common.PathExtensions; using BizHawk.Emulation.Cores.Nintendo.N64; namespace BizHawk.Client.EmuHawk @@ -866,7 +867,7 @@ namespace BizHawk.Client.EmuHawk { return Path.Combine( Global.Config.PathEntries.MovieAbsolutePath(), - $"{PathManager.FilesystemSafeName(Global.Game)}.{TasMovie.Extension}"); + $"{Global.Game.Name.FilesystemSafeName()}.{TasMovie.Extension}"); } private void SaveTas() diff --git a/BizHawk.Client.EmuHawk/tools/ToolFormBase.cs b/BizHawk.Client.EmuHawk/tools/ToolFormBase.cs index d61528bd89..459c61bf8b 100644 --- a/BizHawk.Client.EmuHawk/tools/ToolFormBase.cs +++ b/BizHawk.Client.EmuHawk/tools/ToolFormBase.cs @@ -1,11 +1,11 @@ using System.Collections.Generic; +using System.Drawing; using System.IO; using System.Windows.Forms; -using BizHawk.Emulation.Common; - using BizHawk.Client.Common; -using System.Drawing; +using BizHawk.Common.PathExtensions; +using BizHawk.Emulation.Common; namespace BizHawk.Client.EmuHawk { @@ -26,7 +26,7 @@ namespace BizHawk.Client.EmuHawk { FileName = !string.IsNullOrWhiteSpace(currentFile) ? Path.GetFileName(currentFile) - : $"{PathManager.FilesystemSafeName(Global.Game)}.{fileExt}", + : $"{Global.Game.Name.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) - : $"{PathManager.FilesystemSafeName(Global.Game)}.{fileExt}", + : $"{Global.Game.Name.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 3536c76e0c..846c42979a 100644 --- a/BizHawk.Client.EmuHawk/tools/ToolManager.cs +++ b/BizHawk.Client.EmuHawk/tools/ToolManager.cs @@ -12,6 +12,7 @@ using BizHawk.Client.ApiHawk; using BizHawk.Client.Common; using BizHawk.Client.EmuHawk.CoreExtensions; using BizHawk.Common; +using BizHawk.Common.PathExtensions; using BizHawk.Common.ReflectionExtensions; using BizHawk.Emulation.Common; @@ -873,7 +874,7 @@ namespace BizHawk.Client.EmuHawk f.Directory.Create(); } - return Path.Combine(path, $"{PathManager.FilesystemSafeName(Global.Game)}.cht"); + return Path.Combine(path, $"{Global.Game.Name.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 b46d7062c4..a0a20b635d 100644 --- a/BizHawk.Client.EmuHawk/tools/TraceLogger.cs +++ b/BizHawk.Client.EmuHawk/tools/TraceLogger.cs @@ -6,8 +6,9 @@ using System.Linq; using System.Text; using System.Windows.Forms; -using BizHawk.Emulation.Common; using BizHawk.Client.Common; +using BizHawk.Common.PathExtensions; +using BizHawk.Emulation.Common; namespace BizHawk.Client.EmuHawk { @@ -284,12 +285,12 @@ namespace BizHawk.Client.EmuHawk using var sfd = new SaveFileDialog(); if (LogFile == null) { - sfd.FileName = PathManager.FilesystemSafeName(Global.Game) + _extension; + sfd.FileName = Global.Game.Name.FilesystemSafeName() + _extension; sfd.InitialDirectory = Config.PathEntries.LogAbsolutePath(); } else if (!string.IsNullOrWhiteSpace(LogFile.FullName)) { - sfd.FileName = PathManager.FilesystemSafeName(Global.Game); + sfd.FileName = Global.Game.Name.FilesystemSafeName(); sfd.InitialDirectory = Path.GetDirectoryName(LogFile.FullName); } else @@ -457,7 +458,7 @@ namespace BizHawk.Client.EmuHawk { FileBox.Visible = true; BrowseBox.Visible = true; - var name = PathManager.FilesystemSafeName(Global.Game); + var name = Global.Game.Name.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 new file mode 100644 index 0000000000..baade0e839 --- /dev/null +++ b/BizHawk.Common/Extensions/PathExtensions.cs @@ -0,0 +1,98 @@ +using System; +using System.Linq; +using System.IO; + +namespace BizHawk.Common.PathExtensions +{ + public static class PathExtensions + { + public static string RemoveInvalidFileSystemChars(this string name) + { + var newStr = name; + var chars = Path.GetInvalidFileNameChars(); + return chars.Aggregate(newStr, (current, c) => current.Replace(c.ToString(), "")); + } + + /// + /// 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 + return OSTailoredCode.SimpleSubshell("realpath", $"-L \"{childPath}\"", $"invalid path {childPath} or missing realpath binary") + .StartsWith(OSTailoredCode.SimpleSubshell("realpath", $"-L \"{parentPath}\"", $"invalid path {parentPath} or missing realpath binary")); +#else // written for Unix port but may be useful for Windows when moving to .NET Core + var parentUriPath = new Uri(parentPath.TrimEnd('.')).AbsolutePath.TrimEnd('/'); + try + { + for (var childUri = new DirectoryInfo(childPath).Parent; childUri != null; childUri = childUri?.Parent) + { + if (new Uri(childUri.FullName).AbsolutePath.TrimEnd('/') == parentUriPath) return true; + } + } + catch + { + // ignored + } + return false; +#endif + } + + var parentUri = new Uri(parentPath); + for (var childUri = new DirectoryInfo(childPath).Parent; childUri != null; childUri = childUri?.Parent) + { + 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, "."); + } + + return absolutePath; + } + + public static string FilesystemSafeName(this string? name) + { + name ??= ""; + + 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); + } + } +}