diff --git a/RombaSharp/Features/Build.cs b/RombaSharp/Features/Build.cs index 6d397dfa..17bbbf01 100644 --- a/RombaSharp/Features/Build.cs +++ b/RombaSharp/Features/Build.cs @@ -58,7 +58,7 @@ structure according to the original DAT master directory tree structure."; // Create the new output directory if it doesn't exist string outputFolder = Path.Combine(outdat, Path.GetFileNameWithoutExtension(foundDats[key])); - DirectoryExtensions.Ensure(outputFolder, create: true); + outputFolder.Ensure(create: true); // Get all online depots List onlineDepots = _depots.Where(d => d.Value.Item2).Select(d => d.Key).ToList(); diff --git a/RombaSharp/Features/Diffdat.cs b/RombaSharp/Features/Diffdat.cs index 1b4f81d2..2abde148 100644 --- a/RombaSharp/Features/Diffdat.cs +++ b/RombaSharp/Features/Diffdat.cs @@ -40,7 +40,7 @@ in -old DAT file. Ignores those entries in -old that are not in -new."; string outdat = GetString(features, OutStringValue); // Ensure the output directory - DirectoryExtensions.Ensure(outdat, create: true); + outdat.Ensure(create: true); // Check that all required files exist if (!File.Exists(olddat)) diff --git a/RombaSharp/Features/Dir2Dat.cs b/RombaSharp/Features/Dir2Dat.cs index 693ac9b2..a454fa62 100644 --- a/RombaSharp/Features/Dir2Dat.cs +++ b/RombaSharp/Features/Dir2Dat.cs @@ -40,7 +40,7 @@ namespace RombaSharp.Features string outdat = GetString(features, OutStringValue); // Ensure the output directory - DirectoryExtensions.Ensure(outdat, create: true); + outdat.Ensure(create: true); // Check that all required directories exist if (!Directory.Exists(source)) diff --git a/RombaSharp/Features/EDiffdat.cs b/RombaSharp/Features/EDiffdat.cs index cf96aa4b..3ea743ab 100644 --- a/RombaSharp/Features/EDiffdat.cs +++ b/RombaSharp/Features/EDiffdat.cs @@ -35,7 +35,7 @@ namespace RombaSharp.Features string newdat = GetString(features, NewStringValue); // Ensure the output directory - DirectoryExtensions.Ensure(outdat, create: true); + outdat.Ensure(create: true); // Check that all required files exist if (!File.Exists(olddat)) diff --git a/RombaSharp/Features/Import.cs b/RombaSharp/Features/Import.cs index 3e25d566..678e091d 100644 --- a/RombaSharp/Features/Import.cs +++ b/RombaSharp/Features/Import.cs @@ -29,7 +29,7 @@ namespace RombaSharp.Features logger.Error("This feature is not yet implemented: import"); // First ensure the inputs and database connection - Inputs = DirectoryExtensions.GetFilesOnly(Inputs).Select(p => p.CurrentPath).ToList(); + Inputs = PathTool.GetFilesOnly(Inputs).Select(p => p.CurrentPath).ToList(); SqliteConnection dbc = new SqliteConnection(_connectionString); SqliteCommand slc = new SqliteCommand(); dbc.Open(); diff --git a/RombaSharp/Features/Merge.cs b/RombaSharp/Features/Merge.cs index cea5769c..d5fbe1d3 100644 --- a/RombaSharp/Features/Merge.cs +++ b/RombaSharp/Features/Merge.cs @@ -40,7 +40,7 @@ namespace RombaSharp.Features logger.Error("This feature is not yet implemented: merge"); // Verify that the inputs are valid directories - Inputs = DirectoryExtensions.GetDirectoriesOnly(Inputs).Select(p => p.CurrentPath).ToList(); + Inputs = PathTool.GetDirectoriesOnly(Inputs).Select(p => p.CurrentPath).ToList(); // Loop over all input directories foreach (string input in Inputs) diff --git a/RombaSharp/Features/Miss.cs b/RombaSharp/Features/Miss.cs index 13991e95..5bbc43c0 100644 --- a/RombaSharp/Features/Miss.cs +++ b/RombaSharp/Features/Miss.cs @@ -31,7 +31,7 @@ namespace RombaSharp.Features Dictionary foundDats = GetValidDats(Inputs); // Create the new output directory if it doesn't exist - DirectoryExtensions.Ensure(Path.Combine(Globals.ExeDir, "out"), create: true); + Path.Combine(Globals.ExeDir, "out").Ensure(create: true); // Now that we have the dictionary, we can loop through and output to a new folder for each foreach (string key in foundDats.Keys) diff --git a/SabreTools.IO/Aaru/IChecksum.cs b/SabreTools.Core/Tools/Aaru/IChecksum.cs similarity index 100% rename from SabreTools.IO/Aaru/IChecksum.cs rename to SabreTools.Core/Tools/Aaru/IChecksum.cs diff --git a/SabreTools.IO/Aaru/SpamSumContext.cs b/SabreTools.Core/Tools/Aaru/SpamSumContext.cs similarity index 100% rename from SabreTools.IO/Aaru/SpamSumContext.cs rename to SabreTools.Core/Tools/Aaru/SpamSumContext.cs diff --git a/SabreTools.IO/Hasher.cs b/SabreTools.Core/Tools/Hasher.cs similarity index 98% rename from SabreTools.IO/Hasher.cs rename to SabreTools.Core/Tools/Hasher.cs index fdd6931f..52f1afeb 100644 --- a/SabreTools.IO/Hasher.cs +++ b/SabreTools.Core/Tools/Hasher.cs @@ -3,9 +3,8 @@ using System.Linq; using System.Security.Cryptography; using Aaru.Checksums; -using SabreTools.Core; -namespace SabreTools.IO +namespace SabreTools.Core.Tools { /// /// Async hashing class wraper diff --git a/SabreTools.IO/OptimizedCRC.cs b/SabreTools.Core/Tools/OptimizedCRC.cs similarity index 100% rename from SabreTools.IO/OptimizedCRC.cs rename to SabreTools.Core/Tools/OptimizedCRC.cs diff --git a/SabreTools.Core/Tools/Utilities.cs b/SabreTools.Core/Tools/Utilities.cs index 5b5002b9..099db57c 100644 --- a/SabreTools.Core/Tools/Utilities.cs +++ b/SabreTools.Core/Tools/Utilities.cs @@ -116,6 +116,36 @@ namespace SabreTools.Core.Tools (int)((msDosDateTime >> 11) & 0x1F), (int)((msDosDateTime >> 5) & 0x3F), (int)((msDosDateTime & 0x1F) * 2)); } + /// + /// Get a proper romba sub path + /// + /// SHA-1 hash to get the path for + /// Positive value representing the depth of the depot + /// Subfolder path for the given hash + public static string GetDepotPath(string hash, int depth) + { + // If the hash isn't the right size, then we return null + if (hash.Length != Constants.SHA1Length) + return null; + + // Cap the depth between 0 and 20, for now + if (depth < 0) + depth = 0; + else if (depth > Constants.SHA1ZeroBytes.Length) + depth = Constants.SHA1ZeroBytes.Length; + + // Loop through and generate the subdirectory + string path = string.Empty; + for (int i = 0; i < depth; i++) + { + path += hash.Substring(i * 2, 2) + Path.DirectorySeparatorChar; + } + + // Now append the filename + path += $"{hash}.gz"; + return path; + } + /// Indicates whether the specified array is null or has a length of zero /// /// The array to test @@ -123,7 +153,7 @@ namespace SabreTools.Core.Tools /// https://stackoverflow.com/questions/8560106/isnullorempty-equivalent-for-array-c-sharp public static bool IsNullOrEmpty(this Array array) { - return (array == null || array.Length == 0); + return array == null || array.Length == 0; } /// @@ -136,5 +166,33 @@ namespace SabreTools.Core.Tools List invalidPath = Path.GetInvalidPathChars().ToList(); return new string(s.Where(c => !invalidPath.Contains(c)).ToArray()); } + + /// + /// Returns if the first byte array starts with the second array + /// + /// First byte array to compare + /// Second byte array to compare + /// True if the input arrays should match exactly, false otherwise (default) + /// True if the first byte array starts with the second, false otherwise + public static bool StartsWith(this byte[] arr1, byte[] arr2, bool exact = false) + { + // If we have any invalid inputs, we return false + if (arr1 == null || arr2 == null + || arr1.Length == 0 || arr2.Length == 0 + || arr2.Length > arr1.Length + || (exact && arr1.Length != arr2.Length)) + { + return false; + } + + // Otherwise, loop through and see + for (int i = 0; i < arr2.Length; i++) + { + if (arr1[i] != arr2[i]) + return false; + } + + return true; + } } } diff --git a/SabreTools.DatFiles/DatFile.cs b/SabreTools.DatFiles/DatFile.cs index ff8afc6b..483ce5a6 100644 --- a/SabreTools.DatFiles/DatFile.cs +++ b/SabreTools.DatFiles/DatFile.cs @@ -5,9 +5,9 @@ using System.Linq; using System.Xml.Serialization; using SabreTools.Core; +using SabreTools.Core.Tools; using SabreTools.DatFiles.Formats; using SabreTools.DatItems; -using SabreTools.IO; using SabreTools.Logging; using Newtonsoft.Json; @@ -467,7 +467,7 @@ namespace SabreTools.DatFiles // We can only write out if there's a SHA-1 if (!string.IsNullOrWhiteSpace(disk.SHA1)) { - name = PathExtensions.GetDepotPath(disk.SHA1, Header.OutputDepot.Depth).Replace('\\', '/'); + name = Utilities.GetDepotPath(disk.SHA1, Header.OutputDepot.Depth).Replace('\\', '/'); item.SetFields(new Dictionary { [Field.DatItem_Name] = $"{pre}{name}{post}" } ); } } @@ -478,7 +478,7 @@ namespace SabreTools.DatFiles // We can only write out if there's a SHA-1 if (!string.IsNullOrWhiteSpace(media.SHA1)) { - name = PathExtensions.GetDepotPath(media.SHA1, Header.OutputDepot.Depth).Replace('\\', '/'); + name = Utilities.GetDepotPath(media.SHA1, Header.OutputDepot.Depth).Replace('\\', '/'); item.SetFields(new Dictionary { [Field.DatItem_Name] = $"{pre}{name}{post}" }); } } @@ -489,7 +489,7 @@ namespace SabreTools.DatFiles // We can only write out if there's a SHA-1 if (!string.IsNullOrWhiteSpace(rom.SHA1)) { - name = PathExtensions.GetDepotPath(rom.SHA1, Header.OutputDepot.Depth).Replace('\\', '/'); + name = Utilities.GetDepotPath(rom.SHA1, Header.OutputDepot.Depth).Replace('\\', '/'); item.SetFields(new Dictionary { [Field.DatItem_Name] = $"{pre}{name}{post}" }); } } diff --git a/SabreTools.DatFiles/DatFromDir.cs b/SabreTools.DatFiles/DatFromDir.cs index 9d480e0b..be49bfc8 100644 --- a/SabreTools.DatFiles/DatFromDir.cs +++ b/SabreTools.DatFiles/DatFromDir.cs @@ -45,7 +45,7 @@ namespace SabreTools.DatFiles Hash hashes = Hash.Standard) { // Clean the temp directory path - Globals.TempDir = DirectoryExtensions.Ensure(Globals.TempDir, temp: true); + Globals.TempDir = Globals.TempDir.Ensure(temp: true); // Set the progress variables long totalSize = 0; @@ -269,7 +269,7 @@ namespace SabreTools.DatFiles if (datFile.Header.OutputDepot?.IsActive == true) return; - List empties = DirectoryExtensions.ListEmpty(basePath); + List empties = basePath.ListEmpty(); Parallel.ForEach(empties, Globals.ParallelOptions, dir => { // Get the full path for the directory diff --git a/SabreTools.DatFiles/DatHeader.cs b/SabreTools.DatFiles/DatHeader.cs index 1f7b377e..8a28e71f 100644 --- a/SabreTools.DatFiles/DatHeader.cs +++ b/SabreTools.DatFiles/DatHeader.cs @@ -1058,10 +1058,10 @@ namespace SabreTools.DatFiles /// String containing the new filename private string CreateOutFileNamesHelper(string outDir, string extension, bool overwrite) { - string filename = (string.IsNullOrWhiteSpace(FileName) ? Description : FileName); + string filename = string.IsNullOrWhiteSpace(FileName) ? Description : FileName; // Strip off the extension if it's a holdover from the DAT - if (PathExtensions.HasValidDatExtension(filename)) + if (Parser.HasValidDatExtension(filename)) filename = Path.GetFileNameWithoutExtension(filename); string outfile = $"{outDir}{filename}{extension}"; diff --git a/SabreTools.DatFiles/Formats/AttractMode.cs b/SabreTools.DatFiles/Formats/AttractMode.cs index 892f6e26..64efc6aa 100644 --- a/SabreTools.DatFiles/Formats/AttractMode.cs +++ b/SabreTools.DatFiles/Formats/AttractMode.cs @@ -35,7 +35,7 @@ namespace SabreTools.DatFiles.Formats public override void ParseFile(string filename, int indexId, bool keep, bool throwOnError = false) { // Open a file reader - Encoding enc = FileExtensions.GetEncoding(filename); + Encoding enc = filename.GetEncoding(); SeparatedValueReader svr = new SeparatedValueReader(File.OpenRead(filename), enc) { Header = true, diff --git a/SabreTools.DatFiles/Formats/ClrMamePro.cs b/SabreTools.DatFiles/Formats/ClrMamePro.cs index 6c0956ae..20d55d70 100644 --- a/SabreTools.DatFiles/Formats/ClrMamePro.cs +++ b/SabreTools.DatFiles/Formats/ClrMamePro.cs @@ -47,7 +47,7 @@ namespace SabreTools.DatFiles.Formats public override void ParseFile(string filename, int indexId, bool keep, bool throwOnError = false) { // Open a file reader - Encoding enc = FileExtensions.GetEncoding(filename); + Encoding enc = filename.GetEncoding(); ClrMameProReader cmpr = new ClrMameProReader(File.OpenRead(filename), enc) { DosCenter = false, diff --git a/SabreTools.DatFiles/Formats/DosCenter.cs b/SabreTools.DatFiles/Formats/DosCenter.cs index 8ce60273..6e1ce0db 100644 --- a/SabreTools.DatFiles/Formats/DosCenter.cs +++ b/SabreTools.DatFiles/Formats/DosCenter.cs @@ -37,7 +37,7 @@ namespace SabreTools.DatFiles.Formats public override void ParseFile(string filename, int indexId, bool keep, bool throwOnError = false) { // Open a file reader - Encoding enc = FileExtensions.GetEncoding(filename); + Encoding enc = filename.GetEncoding(); ClrMameProReader cmpr = new ClrMameProReader(File.OpenRead(filename), enc) { DosCenter = true diff --git a/SabreTools.DatFiles/Formats/EverdriveSmdb.cs b/SabreTools.DatFiles/Formats/EverdriveSmdb.cs index be93a2e0..739172f6 100644 --- a/SabreTools.DatFiles/Formats/EverdriveSmdb.cs +++ b/SabreTools.DatFiles/Formats/EverdriveSmdb.cs @@ -35,7 +35,7 @@ namespace SabreTools.DatFiles.Formats public override void ParseFile(string filename, int indexId, bool keep, bool throwOnError = false) { // Open a file reader - Encoding enc = FileExtensions.GetEncoding(filename); + Encoding enc = filename.GetEncoding(); SeparatedValueReader svr = new SeparatedValueReader(File.OpenRead(filename), enc) { Header = false, diff --git a/SabreTools.DatFiles/Formats/Hashfile.cs b/SabreTools.DatFiles/Formats/Hashfile.cs index 3c1a6479..1b277009 100644 --- a/SabreTools.DatFiles/Formats/Hashfile.cs +++ b/SabreTools.DatFiles/Formats/Hashfile.cs @@ -39,7 +39,7 @@ namespace SabreTools.DatFiles.Formats public override void ParseFile(string filename, int indexId, bool keep, bool throwOnError = false) { // Open a file reader - Encoding enc = FileExtensions.GetEncoding(filename); + Encoding enc = filename.GetEncoding(); StreamReader sr = new StreamReader(File.OpenRead(filename), enc); while (!sr.EndOfStream) diff --git a/SabreTools.DatFiles/Formats/Listrom.cs b/SabreTools.DatFiles/Formats/Listrom.cs index df0ef52c..37386791 100644 --- a/SabreTools.DatFiles/Formats/Listrom.cs +++ b/SabreTools.DatFiles/Formats/Listrom.cs @@ -44,7 +44,7 @@ namespace SabreTools.DatFiles.Formats public override void ParseFile(string filename, int indexId, bool keep, bool throwOnError = false) { // Open a file reader - Encoding enc = FileExtensions.GetEncoding(filename); + Encoding enc = filename.GetEncoding(); StreamReader sr = new StreamReader(File.OpenRead(filename), enc); string gamename = string.Empty; diff --git a/SabreTools.DatFiles/Formats/OfflineList.cs b/SabreTools.DatFiles/Formats/OfflineList.cs index 94123134..227723eb 100644 --- a/SabreTools.DatFiles/Formats/OfflineList.cs +++ b/SabreTools.DatFiles/Formats/OfflineList.cs @@ -898,7 +898,7 @@ namespace SabreTools.DatFiles.Formats if (datItem.ItemType == ItemType.Rom) { var rom = datItem as Rom; - string tempext = "." + PathExtensions.GetNormalizedExtension(rom.Name); + string tempext = "." + rom.Name.GetNormalizedExtension(); xtw.WriteStartElement("files"); if (!string.IsNullOrWhiteSpace(rom.CRC)) diff --git a/SabreTools.DatFiles/Formats/SeparatedValue.cs b/SabreTools.DatFiles/Formats/SeparatedValue.cs index 0f666d0f..51133982 100644 --- a/SabreTools.DatFiles/Formats/SeparatedValue.cs +++ b/SabreTools.DatFiles/Formats/SeparatedValue.cs @@ -41,7 +41,7 @@ namespace SabreTools.DatFiles.Formats public override void ParseFile(string filename, int indexId, bool keep, bool throwOnError = false) { // Open a file reader - Encoding enc = FileExtensions.GetEncoding(filename); + Encoding enc = filename.GetEncoding(); SeparatedValueReader svr = new SeparatedValueReader(File.OpenRead(filename), enc) { Header = true, diff --git a/SabreTools.DatFiles/ItemDictionary.cs b/SabreTools.DatFiles/ItemDictionary.cs index adfae42d..43c03486 100644 --- a/SabreTools.DatFiles/ItemDictionary.cs +++ b/SabreTools.DatFiles/ItemDictionary.cs @@ -1369,13 +1369,13 @@ namespace SabreTools.DatFiles reportName = "report"; // Get the proper output directory name - outDir = DirectoryExtensions.Ensure(outDir); + outDir = outDir.Ensure(); // Get the dictionary of desired output report names Dictionary outputs = CreateOutStatsNames(outDir, statDatFormat, reportName); // Make sure we have all files and then order them - List files = DirectoryExtensions.GetFilesOnly(inputs); + List files = PathTool.GetFilesOnly(inputs); files = files .OrderBy(i => Path.GetDirectoryName(i.CurrentPath)) .ThenBy(i => Path.GetFileName(i.CurrentPath)) diff --git a/SabreTools.DatFiles/Parser.cs b/SabreTools.DatFiles/Parser.cs index 016bb23d..d774b733 100644 --- a/SabreTools.DatFiles/Parser.cs +++ b/SabreTools.DatFiles/Parser.cs @@ -78,7 +78,7 @@ namespace SabreTools.DatFiles string currentPath = input.CurrentPath; // Check the file extension first as a safeguard - if (!PathExtensions.HasValidDatExtension(currentPath)) + if (!HasValidDatExtension(currentPath)) return; // If the output filename isn't set already, get the internal filename @@ -105,6 +105,39 @@ namespace SabreTools.DatFiles } } + /// + /// Get if the given path has a valid DAT extension + /// + /// Path to check + /// True if the extension is valid, false otherwise + public static bool HasValidDatExtension(string path) + { + // Get the extension from the path, if possible + string ext = path.GetNormalizedExtension(); + + // Check against the list of known DAT extensions + switch (ext) + { + case "csv": + case "dat": + case "json": + case "md5": + case "ripemd160": + case "sfv": + case "sha1": + case "sha256": + case "sha384": + case "sha512": + case "ssv": + case "tsv": + case "txt": + case "xml": + return true; + default: + return false; + } + } + /// /// Get what type of DAT the input file is /// @@ -113,11 +146,11 @@ namespace SabreTools.DatFiles private static DatFormat GetDatFormat(string filename) { // Limit the output formats based on extension - if (!PathExtensions.HasValidDatExtension(filename)) + if (!HasValidDatExtension(filename)) return 0; // Get the extension from the filename - string ext = PathExtensions.GetNormalizedExtension(filename); + string ext = filename.GetNormalizedExtension(); // Check if file exists if (!File.Exists(filename)) diff --git a/SabreTools.DatFiles/Rebuilder.cs b/SabreTools.DatFiles/Rebuilder.cs index 81d110fd..1632dbc2 100644 --- a/SabreTools.DatFiles/Rebuilder.cs +++ b/SabreTools.DatFiles/Rebuilder.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Threading.Tasks; using SabreTools.Core; +using SabreTools.Core.Tools; using SabreTools.DatItems; using SabreTools.FileTypes; using SabreTools.IO; @@ -54,7 +55,7 @@ namespace SabreTools.DatFiles } // Check that the output directory exists - outDir = DirectoryExtensions.Ensure(outDir, create: true); + outDir = outDir.Ensure(create: true); // Now we want to get forcepack flag if it's not overridden if (outputFormat == OutputFormat.Folder && datFile.Header.ForcePacking != PackingFlag.None) @@ -102,7 +103,7 @@ namespace SabreTools.DatFiles logger.User($"Checking hash '{hash}'"); // Get the extension path for the hash - string subpath = PathExtensions.GetDepotPath(hash, datFile.Header.InputDepot.Depth); + string subpath = Utilities.GetDepotPath(hash, datFile.Header.InputDepot.Depth); // Find the first depot that includes the hash string foundpath = null; @@ -542,7 +543,7 @@ namespace SabreTools.DatFiles // Get the proper output path string sha1 = (datItem as Rom).SHA1 ?? string.Empty; if (outputFormat == OutputFormat.TorrentGzipRomba) - outDir = Path.Combine(outDir, PathExtensions.GetDepotPath(sha1, datFile.Header.OutputDepot.Depth)); + outDir = Path.Combine(outDir, Utilities.GetDepotPath(sha1, datFile.Header.OutputDepot.Depth)); else outDir = Path.Combine(outDir, sha1 + ".gz"); @@ -586,7 +587,7 @@ namespace SabreTools.DatFiles // Get the proper output path string sha1 = (datItem as Rom).SHA1 ?? string.Empty; if (outputFormat == OutputFormat.TorrentXZRomba) - outDir = Path.Combine(outDir, PathExtensions.GetDepotPath(sha1, datFile.Header.OutputDepot.Depth)).Replace(".gz", ".xz"); + outDir = Path.Combine(outDir, Utilities.GetDepotPath(sha1, datFile.Header.OutputDepot.Depth)).Replace(".gz", ".xz"); else outDir = Path.Combine(outDir, sha1 + ".xz"); diff --git a/SabreTools.DatFiles/Splitter.cs b/SabreTools.DatFiles/Splitter.cs index f9a19801..8da17071 100644 --- a/SabreTools.DatFiles/Splitter.cs +++ b/SabreTools.DatFiles/Splitter.cs @@ -63,11 +63,11 @@ namespace SabreTools.DatFiles List items = datFile.Items[key]; foreach (DatItem item in items) { - if (newExtA.Contains(PathExtensions.GetNormalizedExtension(item.GetName() ?? string.Empty))) + if (newExtA.Contains((item.GetName() ?? string.Empty).GetNormalizedExtension())) { extADat.Items.Add(key, item); } - else if (newExtB.Contains(PathExtensions.GetNormalizedExtension(item.GetName() ?? string.Empty))) + else if (newExtB.Contains((item.GetName() ?? string.Empty).GetNormalizedExtension())) { extBDat.Items.Add(key, item); } diff --git a/SabreTools.DatFiles/Verification.cs b/SabreTools.DatFiles/Verification.cs index 07990b33..a4e0eb11 100644 --- a/SabreTools.DatFiles/Verification.cs +++ b/SabreTools.DatFiles/Verification.cs @@ -3,9 +3,9 @@ using System.IO; using System.Linq; using SabreTools.Core; +using SabreTools.Core.Tools; using SabreTools.DatItems; using SabreTools.FileTypes; -using SabreTools.IO; using SabreTools.Logging; // This file represents all methods related to verifying with a DatFile @@ -64,7 +64,7 @@ namespace SabreTools.DatFiles logger.User($"Checking hash '{hash}'"); // Get the extension path for the hash - string subpath = PathExtensions.GetDepotPath(hash, datFile.Header.InputDepot.Depth); + string subpath = Utilities.GetDepotPath(hash, datFile.Header.InputDepot.Depth); // Find the first depot that includes the hash string foundpath = null; diff --git a/SabreTools.DatFiles/Writer.cs b/SabreTools.DatFiles/Writer.cs index 08ba3dda..960c3f7c 100644 --- a/SabreTools.DatFiles/Writer.cs +++ b/SabreTools.DatFiles/Writer.cs @@ -48,7 +48,7 @@ namespace SabreTools.DatFiles } // Ensure the output directory is set and created - outDir = DirectoryExtensions.Ensure(outDir, create: true); + outDir = outDir.Ensure(create: true); // If the DAT has no output format, default to XML if (datFile.Header.DatFormat == 0) diff --git a/SabreTools.FileTypes/AaruFormat.cs b/SabreTools.FileTypes/AaruFormat.cs index 91ae2a82..d596b2ee 100644 --- a/SabreTools.FileTypes/AaruFormat.cs +++ b/SabreTools.FileTypes/AaruFormat.cs @@ -1,6 +1,7 @@ using System.IO; using System.Text; +using SabreTools.Core.Tools; using SabreTools.IO; using SabreTools.FileTypes.Aaru; diff --git a/SabreTools.FileTypes/BaseFile.cs b/SabreTools.FileTypes/BaseFile.cs index 0ba02268..4ba97fc5 100644 --- a/SabreTools.FileTypes/BaseFile.cs +++ b/SabreTools.FileTypes/BaseFile.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Threading.Tasks; using SabreTools.Core; +using SabreTools.Core.Tools; using SabreTools.IO; using SabreTools.Logging; using SabreTools.Skippers; @@ -207,7 +208,7 @@ namespace SabreTools.FileTypes return outFileType; // First line of defense is going to be the extension, for better or worse - if (!PathExtensions.HasValidArchiveExtension(input)) + if (!HasValidArchiveExtension(input)) return outFileType; // Read the first bytes of the file and get the magic number @@ -451,6 +452,48 @@ namespace SabreTools.FileTypes } } + /// + /// Get if the given path has a valid DAT extension + /// + /// Path to check + /// True if the extension is valid, false otherwise + private static bool HasValidArchiveExtension(string path) + { + // Get the extension from the path, if possible + string ext = path.GetNormalizedExtension(); + + // Check against the list of known archive extensions + switch (ext) + { + // Aaruformat + case "aaru": + case "aaruf": + case "aaruformat": + case "aif": + case "dicf": + + // Archives + case "7z": + case "gz": + case "lzma": + case "rar": + case "rev": + case "r00": + case "r01": + case "tar": + case "tgz": + case "tlz": + case "zip": + case "zipx": + + // CHD + case "chd": + return true; + default: + return false; + } + } + #endregion } } diff --git a/SabreTools.FileTypes/Folder.cs b/SabreTools.FileTypes/Folder.cs index e8fea6a6..1fe0bd34 100644 --- a/SabreTools.FileTypes/Folder.cs +++ b/SabreTools.FileTypes/Folder.cs @@ -201,7 +201,7 @@ namespace SabreTools.FileTypes Directory.CreateDirectory(outDir); // Get all files from the input directory - List files = DirectoryExtensions.GetFilesOrdered(this.Filename); + List files = PathTool.GetFilesOrdered(this.Filename); // Now sort through to find the first file that matches string match = files.Where(s => s.EndsWith(entryName)).FirstOrDefault(); @@ -239,7 +239,7 @@ namespace SabreTools.FileTypes Directory.CreateDirectory(this.Filename); // Get all files from the input directory - List files = DirectoryExtensions.GetFilesOrdered(this.Filename); + List files = PathTool.GetFilesOrdered(this.Filename); // Now sort through to find the first file that matches string match = files.Where(s => s.EndsWith(entryName)).FirstOrDefault(); @@ -296,7 +296,7 @@ namespace SabreTools.FileTypes /// List of empty folders in the folder public virtual List GetEmptyFolders() { - return DirectoryExtensions.ListEmpty(this.Filename); + return this.Filename.ListEmpty(); } #endregion diff --git a/SabreTools.FileTypes/GZipArchive.cs b/SabreTools.FileTypes/GZipArchive.cs index 776a6989..5a0a53fb 100644 --- a/SabreTools.FileTypes/GZipArchive.cs +++ b/SabreTools.FileTypes/GZipArchive.cs @@ -445,7 +445,7 @@ namespace SabreTools.FileTypes baseFile = GetInfo(inputStream, keepReadOpen: true); // Get the output file name - string outfile = Path.Combine(outDir, PathExtensions.GetDepotPath(Utilities.ByteArrayToString(baseFile.SHA1), Depth)); + string outfile = Path.Combine(outDir, Utilities.GetDepotPath(Utilities.ByteArrayToString(baseFile.SHA1), Depth)); // Check to see if the folder needs to be created if (!Directory.Exists(Path.GetDirectoryName(outfile))) diff --git a/SabreTools.FileTypes/XZArchive.cs b/SabreTools.FileTypes/XZArchive.cs index 38fcaefe..148b2b0a 100644 --- a/SabreTools.FileTypes/XZArchive.cs +++ b/SabreTools.FileTypes/XZArchive.cs @@ -337,7 +337,7 @@ namespace SabreTools.FileTypes baseFile = GetInfo(inputStream, keepReadOpen: true); // Get the output file name - string outfile = Path.Combine(outDir, PathExtensions.GetDepotPath(Utilities.ByteArrayToString(baseFile.SHA1), Depth)); + string outfile = Path.Combine(outDir, Utilities.GetDepotPath(Utilities.ByteArrayToString(baseFile.SHA1), Depth)); outfile = outfile.Replace(".gz", ".xz"); // Check to see if the folder needs to be created diff --git a/SabreTools.IO/FileExtensions.cs b/SabreTools.IO/FileExtensions.cs deleted file mode 100644 index be9a15fa..00000000 --- a/SabreTools.IO/FileExtensions.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System.IO; -using System.Text; - -namespace SabreTools.IO -{ - /// - /// Extensions to File functionality - /// - public static class FileExtensions - { - /// - /// Determines a text file's encoding by analyzing its byte order mark (BOM). - /// Defaults to ASCII when detection of the text file's endianness fails. - /// - /// The text file to analyze. - /// The detected encoding. - /// http://stackoverflow.com/questions/3825390/effective-way-to-find-any-files-encoding - public static Encoding GetEncoding(string filename) - { - if (!File.Exists(filename)) - return Encoding.Default; - - // Try to open the file - try - { - FileStream file = File.OpenRead(filename); - if (file == null) - return Encoding.Default; - - // Read the BOM - var bom = new byte[4]; - file.Read(bom, 0, 4); - file.Dispose(); - - // Analyze the BOM - if (bom[0] == 0x2b && bom[1] == 0x2f && bom[2] == 0x76) return Encoding.UTF7; - if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf) return Encoding.UTF8; - if (bom[0] == 0xff && bom[1] == 0xfe) return Encoding.Unicode; //UTF-16LE - if (bom[0] == 0xfe && bom[1] == 0xff) return Encoding.BigEndianUnicode; //UTF-16BE - if (bom[0] == 0 && bom[1] == 0 && bom[2] == 0xfe && bom[3] == 0xff) return Encoding.UTF32; - return Encoding.Default; - } - catch - { - return Encoding.Default; - } - } - - /// - /// Returns if the first byte array starts with the second array - /// - /// First byte array to compare - /// Second byte array to compare - /// True if the input arrays should match exactly, false otherwise (default) - /// True if the first byte array starts with the second, false otherwise - public static bool StartsWith(this byte[] arr1, byte[] arr2, bool exact = false) - { - // If we have any invalid inputs, we return false - if (arr1 == null || arr2 == null - || arr1.Length == 0 || arr2.Length == 0 - || arr2.Length > arr1.Length - || (exact && arr1.Length != arr2.Length)) - { - return false; - } - - // Otherwise, loop through and see - for (int i = 0; i < arr2.Length; i++) - { - if (arr1[i] != arr2[i]) - return false; - } - - return true; - } - } -} diff --git a/SabreTools.IO/IOExtensions.cs b/SabreTools.IO/IOExtensions.cs new file mode 100644 index 00000000..8328ec43 --- /dev/null +++ b/SabreTools.IO/IOExtensions.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace SabreTools.IO +{ + /// + /// Methods around path operations + /// + public static class IOExtensions + { + /// + /// Ensure the output directory is a proper format and can be created + /// + /// Directory to check + /// True if the directory should be created, false otherwise (default) + /// True if this is a temp directory, false otherwise + /// Full path to the directory + public static string Ensure(this string dir, bool create = false, bool temp = false) + { + // If the output directory is invalid + if (string.IsNullOrWhiteSpace(dir)) + { + if (temp) + dir = Path.GetTempPath(); + else + dir = Environment.CurrentDirectory; + } + + // Get the full path for the output directory + dir = Path.GetFullPath(dir); + + // If we're creating the output folder, do so + if (create) + Directory.CreateDirectory(dir); + + return dir; + } + + /// + /// Determines a text file's encoding by analyzing its byte order mark (BOM). + /// Defaults to ASCII when detection of the text file's endianness fails. + /// + /// The text file to analyze. + /// The detected encoding. + /// http://stackoverflow.com/questions/3825390/effective-way-to-find-any-files-encoding + public static Encoding GetEncoding(this string filename) + { + if (string.IsNullOrEmpty(filename)) + return Encoding.Default; + + if (!File.Exists(filename)) + return Encoding.Default; + + // Try to open the file + try + { + FileStream file = File.OpenRead(filename); + if (file == null) + return Encoding.Default; + + // Read the BOM + var bom = new byte[4]; + file.Read(bom, 0, 4); + file.Dispose(); + + // Analyze the BOM + if (bom[0] == 0x2b && bom[1] == 0x2f && bom[2] == 0x76) return Encoding.UTF7; + if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf) return Encoding.UTF8; + if (bom[0] == 0xff && bom[1] == 0xfe) return Encoding.Unicode; //UTF-16LE + if (bom[0] == 0xfe && bom[1] == 0xff) return Encoding.BigEndianUnicode; //UTF-16BE + if (bom[0] == 0 && bom[1] == 0 && bom[2] == 0xfe && bom[3] == 0xff) return Encoding.UTF32; + return Encoding.Default; + } + catch + { + return Encoding.Default; + } + } + + /// + /// Get the extension from the path, if possible + /// + /// Path to get extension from + /// Extension, if possible + public static string GetNormalizedExtension(this string path) + { + // Check null or empty first + if (string.IsNullOrWhiteSpace(path)) + return null; + + // Get the extension from the path, if possible + string ext = Path.GetExtension(path)?.ToLowerInvariant(); + + // Check if the extension is null or empty + if (string.IsNullOrWhiteSpace(ext)) + return null; + + // Make sure that extensions are valid + ext = ext.TrimStart('.'); + + return ext; + } + + /// + /// Get all empty folders within a root folder + /// + /// Root directory to parse + /// IEumerable containing all directories that are empty, an empty enumerable if the root is empty, null otherwise + public static List ListEmpty(this string root) + { + // Check null or empty first + if (string.IsNullOrEmpty(root)) + return null; + + // Then, check if the root exists + if (!Directory.Exists(root)) + return null; + + // If it does and it is empty, return a blank enumerable + if (Directory.EnumerateFileSystemEntries(root, "*", SearchOption.AllDirectories).Count() == 0) + return new List(); + + // Otherwise, get the complete list + return Directory.EnumerateDirectories(root, "*", SearchOption.AllDirectories) + .Where(dir => Directory.EnumerateFileSystemEntries(dir, "*", SearchOption.AllDirectories).Count() == 0) + .ToList(); + } + } +} diff --git a/SabreTools.IO/ParentablePath.cs b/SabreTools.IO/ParentablePath.cs index 5d6e0720..7fb4616d 100644 --- a/SabreTools.IO/ParentablePath.cs +++ b/SabreTools.IO/ParentablePath.cs @@ -71,7 +71,7 @@ namespace SabreTools.IO public string GetOutputPath(string outDir, bool inplace) { // First, we need to ensure the output directory - outDir = DirectoryExtensions.Ensure(outDir); + outDir = outDir.Ensure(); // Check if we have a split path or not bool splitpath = !string.IsNullOrWhiteSpace(ParentPath); diff --git a/SabreTools.IO/PathExtensions.cs b/SabreTools.IO/PathExtensions.cs deleted file mode 100644 index 86d69c7a..00000000 --- a/SabreTools.IO/PathExtensions.cs +++ /dev/null @@ -1,141 +0,0 @@ -using System.IO; - -using SabreTools.Core; - -namespace SabreTools.IO -{ - /// - /// Extensions to Path functionality - /// - public static class PathExtensions - { - /// - /// Get the extension from the path, if possible - /// - /// Path to get extension from - /// Extension, if possible - public static string GetNormalizedExtension(string path) - { - // Check null or empty first - if (string.IsNullOrWhiteSpace(path)) - return null; - - // Get the extension from the path, if possible - string ext = Path.GetExtension(path)?.ToLowerInvariant(); - - // Check if the extension is null or empty - if (string.IsNullOrWhiteSpace(ext)) - return null; - - // Make sure that extensions are valid - ext = ext.TrimStart('.'); - - return ext; - } - - /// - /// Get a proper romba sub path - /// - /// SHA-1 hash to get the path for - /// Positive value representing the depth of the depot - /// Subfolder path for the given hash - public static string GetDepotPath(string hash, int depth) - { - // If the hash isn't the right size, then we return null - if (hash.Length != Constants.SHA1Length) - return null; - - // Cap the depth between 0 and 20, for now - if (depth < 0) - depth = 0; - else if (depth > Constants.SHA1ZeroBytes.Length) - depth = Constants.SHA1ZeroBytes.Length; - - // Loop through and generate the subdirectory - string path = string.Empty; - for (int i = 0; i < depth; i++) - { - path += hash.Substring(i * 2, 2) + Path.DirectorySeparatorChar; - } - - // Now append the filename - path += $"{hash}.gz"; - return path; - } - - /// - /// Get if the given path has a valid DAT extension - /// - /// Path to check - /// True if the extension is valid, false otherwise - public static bool HasValidArchiveExtension(string path) - { - // Get the extension from the path, if possible - string ext = GetNormalizedExtension(path); - - // Check against the list of known archive extensions - switch (ext) - { - // Aaruformat - case "aaru": - case "aaruf": - case "aaruformat": - case "aif": - case "dicf": - - // Archives - case "7z": - case "gz": - case "lzma": - case "rar": - case "rev": - case "r00": - case "r01": - case "tar": - case "tgz": - case "tlz": - case "zip": - case "zipx": - - // CHD - case "chd": - return true; - default: - return false; - } - } - - /// - /// Get if the given path has a valid DAT extension - /// - /// Path to check - /// True if the extension is valid, false otherwise - public static bool HasValidDatExtension(string path) - { - // Get the extension from the path, if possible - string ext = GetNormalizedExtension(path); - - // Check against the list of known DAT extensions - switch (ext) - { - case "csv": - case "dat": - case "json": - case "md5": - case "ripemd160": - case "sfv": - case "sha1": - case "sha256": - case "sha384": - case "sha512": - case "ssv": - case "tsv": - case "txt": - case "xml": - return true; - default: - return false; - } - } - } -} diff --git a/SabreTools.IO/DirectoryExtensions.cs b/SabreTools.IO/PathTool.cs similarity index 70% rename from SabreTools.IO/DirectoryExtensions.cs rename to SabreTools.IO/PathTool.cs index 6e04f6f7..37ca2bf6 100644 --- a/SabreTools.IO/DirectoryExtensions.cs +++ b/SabreTools.IO/PathTool.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -8,65 +7,10 @@ using NaturalSort; namespace SabreTools.IO { /// - /// Extensions to Directory functionality + /// Methods around path operations /// - public static class DirectoryExtensions + public static class PathTool { - /// - /// Cleans out the temporary directory - /// - /// Name of the directory to clean out - public static void Clean(string dir) - { - foreach (string file in Directory.EnumerateFiles(dir, "*", SearchOption.TopDirectoryOnly)) - { - try - { - if (File.Exists(file)) - File.Delete(file); - } - catch { } - } - - foreach (string subdir in Directory.EnumerateDirectories(dir, "*", SearchOption.TopDirectoryOnly)) - { - try - { - if (Directory.Exists(subdir)) - Directory.Delete(subdir); - } - catch { } - } - } - - /// - /// Ensure the output directory is a proper format and can be created - /// - /// Directory to check - /// True if the directory should be created, false otherwise (default) - /// True if this is a temp directory, false otherwise - /// Full path to the directory - public static string Ensure(string dir, bool create = false, bool temp = false) - { - // If the output directory is invalid - if (string.IsNullOrWhiteSpace(dir)) - { - if (temp) - dir = Path.GetTempPath(); - else - dir = Environment.CurrentDirectory; - } - - // Get the full path for the output directory - dir = Path.GetFullPath(dir); - - // If we're creating the output folder, do so - if (create) - Directory.CreateDirectory(dir); - - return dir; - } - /// /// Retrieve a list of just directories from inputs /// @@ -222,26 +166,5 @@ namespace SabreTools.IO // Return the new list return infiles; } - - /// - /// Get all empty folders within a root folder - /// - /// Root directory to parse - /// IEumerable containing all directories that are empty, an empty enumerable if the root is empty, null otherwise - public static List ListEmpty(string root) - { - // Check if the root exists first - if (!Directory.Exists(root)) - return null; - - // If it does and it is empty, return a blank enumerable - if (Directory.EnumerateFileSystemEntries(root, "*", SearchOption.AllDirectories).Count() == 0) - return new List(); - - // Otherwise, get the complete list - return Directory.EnumerateDirectories(root, "*", SearchOption.AllDirectories) - .Where(dir => Directory.EnumerateFileSystemEntries(dir, "*", SearchOption.AllDirectories).Count() == 0) - .ToList(); - } } } diff --git a/SabreTools.Logging/LoggerImpl.cs b/SabreTools.Logging/LoggerImpl.cs index 1dcfa835..47b03e22 100644 --- a/SabreTools.Logging/LoggerImpl.cs +++ b/SabreTools.Logging/LoggerImpl.cs @@ -87,7 +87,7 @@ namespace SabreTools.Logging { // Set and create the output if (addDate) - Filename = $"{Path.GetFileNameWithoutExtension(filename)} ({DateTime.Now:yyyy-MM-dd HH-mm-ss}).{PathExtensions.GetNormalizedExtension(filename)}"; + Filename = $"{Path.GetFileNameWithoutExtension(filename)} ({DateTime.Now:yyyy-MM-dd HH-mm-ss}).{filename.GetNormalizedExtension()}"; else Filename = filename; } diff --git a/SabreTools/Features/Batch.cs b/SabreTools/Features/Batch.cs index 141fb7d6..53604f41 100644 --- a/SabreTools/Features/Batch.cs +++ b/SabreTools/Features/Batch.cs @@ -128,7 +128,7 @@ Reset the internal state: reset();"; } // Get only files from inputs - List datFilePaths = DirectoryExtensions.GetFilesOnly(command.Arguments); + List datFilePaths = PathTool.GetFilesOnly(command.Arguments); // Assume there could be multiple foreach (ParentablePath datFilePath in datFilePaths) diff --git a/SabreTools/Features/Extract.cs b/SabreTools/Features/Extract.cs index fd4075e2..e9681cee 100644 --- a/SabreTools/Features/Extract.cs +++ b/SabreTools/Features/Extract.cs @@ -46,7 +46,7 @@ The following systems have headers that this program can work with: bool nostore = GetBoolean(features, NoStoreHeaderValue); // Get only files from the inputs - List files = DirectoryExtensions.GetFilesOnly(Inputs); + List files = PathTool.GetFilesOnly(Inputs); foreach (ParentablePath file in files) { DetectTransformStore(file.CurrentPath, OutputDir, nostore); diff --git a/SabreTools/Features/Restore.cs b/SabreTools/Features/Restore.cs index 97e10e81..ad985a2e 100644 --- a/SabreTools/Features/Restore.cs +++ b/SabreTools/Features/Restore.cs @@ -41,7 +41,7 @@ The following systems have headers that this program can work with: base.ProcessFeatures(features); // Get only files from the inputs - List files = DirectoryExtensions.GetFilesOnly(Inputs); + List files = PathTool.GetFilesOnly(Inputs); foreach (ParentablePath file in files) { RestoreHeader(file.CurrentPath, OutputDir); diff --git a/SabreTools/Features/Sort.cs b/SabreTools/Features/Sort.cs index c5e020ae..34177984 100644 --- a/SabreTools/Features/Sort.cs +++ b/SabreTools/Features/Sort.cs @@ -82,7 +82,7 @@ namespace SabreTools.Features // Get a list of files from the input datfiles var datfiles = GetList(features, DatListValue); - var datfilePaths = DirectoryExtensions.GetFilesOnly(datfiles); + var datfilePaths = PathTool.GetFilesOnly(datfiles); // If we are in individual mode, process each DAT on their own, appending the DAT name to the output dir if (GetBoolean(features, IndividualValue)) diff --git a/SabreTools/Features/Split.cs b/SabreTools/Features/Split.cs index ec4e2762..d6fa1675 100644 --- a/SabreTools/Features/Split.cs +++ b/SabreTools/Features/Split.cs @@ -48,7 +48,7 @@ namespace SabreTools.Features return; // Get only files from the inputs - List files = DirectoryExtensions.GetFilesOnly(Inputs, appendparent: true); + List files = PathTool.GetFilesOnly(Inputs, appendparent: true); // Loop over the input files foreach (ParentablePath file in files) diff --git a/SabreTools/Features/Update.cs b/SabreTools/Features/Update.cs index ae4b1e23..b8a1a44f 100644 --- a/SabreTools/Features/Update.cs +++ b/SabreTools/Features/Update.cs @@ -149,8 +149,8 @@ namespace SabreTools.Features updateFields = new List() { Field.DatItem_Name }; // Ensure we only have files in the inputs - List inputPaths = DirectoryExtensions.GetFilesOnly(Inputs, appendparent: true); - List basePaths = DirectoryExtensions.GetFilesOnly(GetList(features, BaseDatListValue)); + List inputPaths = PathTool.GetFilesOnly(Inputs, appendparent: true); + List basePaths = PathTool.GetFilesOnly(GetList(features, BaseDatListValue)); // If we're in standard update mode, run through all of the inputs if (updateMode == UpdateMode.None) diff --git a/SabreTools/Features/Verify.cs b/SabreTools/Features/Verify.cs index 66fa6d55..8a4c29ba 100644 --- a/SabreTools/Features/Verify.cs +++ b/SabreTools/Features/Verify.cs @@ -43,7 +43,7 @@ namespace SabreTools.Features // Get a list of files from the input datfiles var datfiles = GetList(features, DatListValue); - var datfilePaths = DirectoryExtensions.GetFilesOnly(datfiles); + var datfilePaths = PathTool.GetFilesOnly(datfiles); // Get feature flags TreatAsFile asFiles = GetTreatAsFiles(features);