using System; using System.Linq; using System.IO; using System.Reflection; using BizHawk.Emulation.Common; using BizHawk.Emulation.Cores.Nintendo.SNES; namespace BizHawk.Client.Common { public static class PathManager { public static string GetExeDirectoryAbsolute() { var path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); if (path.EndsWith(Path.DirectorySeparatorChar.ToString())) { path = path.Remove(path.Length - 1, 1); } return path; } /// /// Makes a path relative to the %exe% dir /// public static string MakeProgramRelativePath(string path) { return MakeAbsolutePath("%exe%/" + path, null); } /// /// The location of the default INI file /// public static string DefaultIniPath { get { return MakeProgramRelativePath("config.ini"); } } /// /// Gets absolute base as derived from EXE /// /// public static string GetBasePathAbsolute() { if (Global.Config.PathEntries.GlobalBaseFragment.Length < 1) // If empty, then EXE path { return GetExeDirectoryAbsolute(); } if (Global.Config.PathEntries.GlobalBaseFragment.Length >= 5 && Global.Config.PathEntries.GlobalBaseFragment.Substring(0, 5) == "%exe%") { return GetExeDirectoryAbsolute(); } if (Global.Config.PathEntries.GlobalBaseFragment[0] == '.') { if (Global.Config.PathEntries.GlobalBaseFragment.Length == 1) { return GetExeDirectoryAbsolute(); } if (Global.Config.PathEntries.GlobalBaseFragment.Length == 2 && Global.Config.PathEntries.GlobalBaseFragment == ".\\") { return GetExeDirectoryAbsolute(); } var tmp = Global.Config.PathEntries.GlobalBaseFragment.Remove(0, 1); tmp = tmp.Insert(0, GetExeDirectoryAbsolute()); return tmp; } if (Global.Config.PathEntries.GlobalBaseFragment.Substring(0, 2) == "..") { return RemoveParents(Global.Config.PathEntries.GlobalBaseFragment, GetExeDirectoryAbsolute()); } // In case of error, return EXE path return GetExeDirectoryAbsolute(); } public static string GetPlatformBase(string system) { return Global.Config.PathEntries[system, "Base"].Path; } public static string StandardFirmwareName(string name) { return Path.Combine(MakeAbsolutePath(Global.Config.PathEntries.FirmwaresPathFragment, null), name); } public static string MakeAbsolutePath(string path, string system) { // Hack if (system == "Global") { system = null; } // This function translates relative path and special identifiers in absolute paths if (path.Length < 1) { return GetBasePathAbsolute(); } if (path == "%recent%") { return Environment.SpecialFolder.Recent.ToString(); } if (path.Length >= 5 && path.Substring(0, 5) == "%exe%") { if (path.Length == 5) { return GetExeDirectoryAbsolute(); } var tmp = path.Remove(0, 5); tmp = tmp.Insert(0, GetExeDirectoryAbsolute()); return tmp; } if (path[0] == '.') { if (!string.IsNullOrWhiteSpace(system)) { path = path.Remove(0, 1); path = path.Insert(0, GetPlatformBase(system)); } if (path.Length == 1) { return GetBasePathAbsolute(); } if (path[0] == '.') { path = path.Remove(0, 1); path = path.Insert(0, GetBasePathAbsolute()); } return path; } // If begins wtih .. do alorithm to determine how many ..\.. combos and deal with accordingly, return drive letter only if too many .. if ((path[0] > 'A' && path[0] < 'Z') || (path[0] > 'a' && path[0] < 'z')) { // C:\ if (path.Length > 2 && path[1] == ':' && path[2] == '\\') { return path; } // file:\ is an acceptable path as well, and what FileBrowserDialog returns if (path.Length >= 6 && path.Substring(0, 6) == "file:\\") { return path; } return GetExeDirectoryAbsolute(); // bad path } // all pad paths default to EXE return GetExeDirectoryAbsolute(); } public static string RemoveParents(string path, string workingpath) { // determines number of parents, then removes directories from working path, return absolute path result // Ex: "..\..\Bob\", "C:\Projects\Emulators\Bizhawk" will return "C:\Projects\Bob\" int x = NumParentDirectories(path); if (x > 0) { int y = StringHelpers.HowMany(path, "..\\"); int z = StringHelpers.HowMany(workingpath, "\\"); if (y >= z) { //Return drive letter only, working path must be absolute? } return string.Empty; } return path; } public static int NumParentDirectories(string path) { // determine the number of parent directories in path and return result int x = StringHelpers.HowMany(path, '\\'); if (x > 0) { return StringHelpers.HowMany(path, "..\\"); } return 0; } public static bool IsRecent(string path) { return path == "%recent%"; } public static string GetLuaPath() { return MakeAbsolutePath(Global.Config.PathEntries.LuaPathFragment, null); } public static string GetRomsPath(string sysID) { if (Global.Config.UseRecentForROMs) { return Environment.SpecialFolder.Recent.ToString(); } var path = Global.Config.PathEntries[sysID, "ROM"] ?? Global.Config.PathEntries[sysID, "Base"]; return MakeAbsolutePath(path.Path, sysID); } public static string RemoveInvalidFileSystemChars(string name) { var newStr = name; var chars = Path.GetInvalidFileNameChars(); return chars.Aggregate(newStr, (current, c) => current.Replace(c.ToString(), string.Empty)); } public static string FilesystemSafeName(GameInfo game) { var filesystemSafeName = game.Name.Replace("|", "+"); 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. // if game.Name is a path, then someone shouldve made it not-a-path already. // return Path.Combine(Path.GetDirectoryName(filesystemSafeName), Path.GetFileNameWithoutExtension(filesystemSafeName)); return filesystemSafeName; } public static string SaveRamPath(GameInfo game) { var name = FilesystemSafeName(game); if (Global.MovieSession.Movie.IsActive) { name += "." + Path.GetFileNameWithoutExtension(Global.MovieSession.Movie.Filename); } var pathEntry = Global.Config.PathEntries[game.System, "Save RAM"] ?? Global.Config.PathEntries[game.System, "Base"]; return Path.Combine(MakeAbsolutePath(pathEntry.Path, game.System), name) + ".SaveRAM"; } public static string GetSaveStatePath(GameInfo game) { var pathEntry = Global.Config.PathEntries[game.System, "Savestates"] ?? Global.Config.PathEntries[game.System, "Base"]; return MakeAbsolutePath(pathEntry.Path, game.System); } public static string SaveStatePrefix(GameInfo game) { var name = FilesystemSafeName(game); // Neshawk and Quicknes have incompatible savestates, store the name to keep them separate if (Global.Emulator.SystemId == "NES") { name += "." + Global.Emulator.Attributes().CoreName; } // Bsnes profiles have incompatible savestates so save the profile name if (Global.Emulator is LibsnesCore) { name += "." + ((LibsnesCore.SnesSyncSettings)Global.Emulator.GetSyncSettings()).Profile; } if (Global.MovieSession.Movie.IsActive) { name += "." + Path.GetFileNameWithoutExtension(Global.MovieSession.Movie.Filename); } var pathEntry = Global.Config.PathEntries[game.System, "Savestates"] ?? Global.Config.PathEntries[game.System, "Base"]; return Path.Combine(MakeAbsolutePath(pathEntry.Path, game.System), name); } public static string GetCheatsPath(GameInfo game) { var pathEntry = Global.Config.PathEntries[game.System, "Cheats"] ?? Global.Config.PathEntries[game.System, "Base"]; return MakeAbsolutePath(pathEntry.Path, game.System); } public static string ScreenshotPrefix(GameInfo game) { var name = FilesystemSafeName(game); var pathEntry = Global.Config.PathEntries[game.System, "Screenshots"] ?? Global.Config.PathEntries[game.System, "Base"]; return Path.Combine(MakeAbsolutePath(pathEntry.Path, game.System), name); } /// /// 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(string absolutePath, string system = null) { var parentPath = string.IsNullOrWhiteSpace(system) ? GetBasePathAbsolute() : MakeAbsolutePath(GetPlatformBase(system), system); if (IsSubfolder(parentPath, absolutePath)) { return absolutePath.Replace(parentPath, "."); } return absolutePath; } //http://stackoverflow.com/questions/3525775/how-to-check-if-directory-1-is-a-subdirectory-of-dir2-and-vice-versa public static bool IsSubfolder(string parentPath, string childPath) { var parentUri = new Uri(parentPath); var childUri = new DirectoryInfo(childPath).Parent; while (childUri != null) { if (new Uri(childUri.FullName) == parentUri) { return true; } childUri = childUri.Parent; } return false; } } }