diff --git a/RombaSharp/RombaSharp.Help.cs b/RombaSharp/RombaSharp.Help.cs index 1ea6922b..fff6994d 100644 --- a/RombaSharp/RombaSharp.Help.cs +++ b/RombaSharp/RombaSharp.Help.cs @@ -1,86 +1,107 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Mono.Data.Sqlite; using SabreTools.Library.Data; +using SabreTools.Library.DatFiles; +using SabreTools.Library.DatItems; using SabreTools.Library.Help; +using SabreTools.Library.Tools; namespace RombaSharp { + // TODO: Do same overhaul here as in SabreTools.Help.cs public partial class RombaSharp { #region Private Flag features + public const string CopyValue = "copy"; private static Feature copyFlag { get { return new Feature( - "copy", + CopyValue, "-copy", "Copy files to output instead of rebuilding", FeatureType.Flag); } } // Unique to RombaSharp + + public const string FixdatOnlyValue = "fixdat-only"; private static Feature fixdatOnlyFlag { get { return new Feature( - "fixdatOnly", + FixdatOnlyValue, "-fixdatOnly", "only fix dats and don't generate torrentzips", FeatureType.Flag); } } + + public const string LogOnlyValue = "log-only"; private static Feature logOnlyFlag { get { return new Feature( - "log-only", + LogOnlyValue, "-log-only", "Only write out actions to log", FeatureType.Flag); } } + + public const string NoDbValue = "no-db"; private static Feature noDbFlag { get { return new Feature( - "no-db", + NoDbValue, "-no-db", "archive into depot but do not touch DB index and ignore only-needed flag", FeatureType.Flag); } } + + public const string OnlyNeededValue = "only-needed"; private static Feature onlyNeededFlag { get { return new Feature( - "only-needed", + OnlyNeededValue, "-only-needed", "only archive ROM files actually referenced by DAT files from the DAT index", FeatureType.Flag); } } + + public const string SkipInitialScanValue = "skip-initial-scan"; private static Feature skipInitialScanFlag { get { return new Feature( - "skip-initial-scan", + SkipInitialScanValue, "-skip-initial-scan", "skip the initial scan of the files to determine amount of work", FeatureType.Flag); } } + + public const string UseGolangZipValue = "use-golang-zip"; private static Feature useGolangZipFlag { get { return new Feature( - "use-golang-zip", + UseGolangZipValue, "-use-golang-zip", "use go zip implementation instead of zlib", FeatureType.Flag); @@ -91,56 +112,65 @@ namespace RombaSharp #region Private Int32 features + public const string Include7ZipsInt32Value = "include-7zips"; private static Feature include7ZipsInt32Input { get { return new Feature( - "include-7zips", + Include7ZipsInt32Value, "-include-7zips", "flag value == 0 means: add 7zip files themselves into the depot in addition to their contents, flag value == 2 means add 7zip files themselves but don't add content", FeatureType.Int32); } } + + public const string IncludeGZipsInt32Value = "include-gzips"; private static Feature includeGZipsInt32Input { get { return new Feature( - "include-gzips", + IncludeGZipsInt32Value, "-include-gzips", "flag value == 0 means: add gzip files themselves into the depot in addition to their contents, flag value == 2 means add gzip files themselves but don't add content", FeatureType.Int32); } } + + public const string IncludeZipsInt32Value = "include-zips"; private static Feature includeZipsInt32Input { get { return new Feature( - "include-zips", + IncludeZipsInt32Value, "-include-zips", "flag value == 0 means: add zip files themselves into the depot in addition to their contents, flag value == 2 means add zip files themselves but don't add content", FeatureType.Int32); } } + + public const string SubworkersInt32Value = "subworkers"; private static Feature subworkersInt32Input { get { return new Feature( - "subworkers", + SubworkersInt32Value, "-subworkers", "how many subworkers to launch for each worker", FeatureType.Int32); } } // Defaults to Workers count in config + + public const string WorkersInt32Value = "workers"; private static Feature workersInt32Input { get { return new Feature( - "workers", + WorkersInt32Value, "-workers", "how many workers to launch for the job", FeatureType.Int32); @@ -151,12 +181,13 @@ namespace RombaSharp #region Private Int64 features + public const string SizeInt64Value = "size"; private static Feature sizeInt64Input { get { return new Feature( - "size", + SizeInt64Value, "-size", "size of the rom to lookup", FeatureType.Int64); @@ -167,23 +198,26 @@ namespace RombaSharp #region Private List features + public const string DatsListStringValue = "dats"; private static Feature datsListStringInput { get { return new Feature( - "dats", + DatsListStringValue, "-dats", "purge only roms declared in these dats", FeatureType.List); } } + + public const string DepotListStringValue = "depot"; private static Feature depotListStringInput { get { return new Feature( - "depot", + DepotListStringValue, "-depot", "work only on specified depot path", FeatureType.List); @@ -194,100 +228,117 @@ namespace RombaSharp #region Private String features + public const string BackupStringValue = "backup"; private static Feature backupStringInput { get { return new Feature( - "backup", + BackupStringValue, "-backup", "backup directory where backup files are moved to", FeatureType.String); } } + + public const string DescriptionStringValue = "description"; private static Feature descriptionStringInput { get { return new Feature( - "description", + DescriptionStringValue, "-description", "description value in DAT header", FeatureType.String); } } + + public const string MissingSha1sStringValue = "missing-sha1s"; private static Feature missingSha1sStringInput { get { return new Feature( - "missingSha1s", + MissingSha1sStringValue, "-missingSha1s", "write paths of dats with missing sha1s into this file", FeatureType.String); } } + + public const string NameStringValue = "name"; private static Feature nameStringInput { get { return new Feature( - "name", + NameStringValue, "-name", "name value in DAT header", FeatureType.String); } } + + public const string NewStringValue = "new"; private static Feature newStringInput { get { return new Feature( - "new", + NewStringValue, "-new", "new DAT file", FeatureType.String); } } + + public const string OldStringValue = "old"; private static Feature oldStringInput { get { return new Feature( - "old", + OldStringValue, "-old", "old DAT file", FeatureType.String); } } + + public const string OutStringValue = "out"; private static Feature outStringInput { get { return new Feature( - "out", + OutStringValue, "-out", "output file", FeatureType.String); } } + + public const string ResumeStringValue = "resume"; private static Feature resumeStringInput { get { return new Feature( - "resume", + ResumeStringValue, "-resume", "resume a previously interrupted operation from the specified path", FeatureType.String); } } + + public const string SourceStringValue = "source"; private static Feature sourceStringInput { get { return new Feature( - "source", + SourceStringValue, "-source", "source directory", FeatureType.String); @@ -305,367 +356,1510 @@ namespace RombaSharp "RombaSharp - C# port of the Romba rom management tool", barrier, "Usage: RombaSharp [option] [filename|dirname] ...", - "" + string.Empty }; + + // Create the base help object with header Help help = new Help(helpHeader); - #region Help + // Add all of the features + help.Add(new HelpFeature()); + help.Add(new DetailedHelpFeature()); + help.Add(new ScriptFeature()); + help.Add(new ArchiveFeature()); + help.Add(new BuildFeature()); + help.Add(new CancelFeature()); + help.Add(new DatStatsFeature()); + help.Add(new DbStatsFeature()); + help.Add(new DiffdatFeature()); + help.Add(new Dir2DatFeature()); + help.Add(new EDiffdatFeature()); + help.Add(new ExportFeature()); + help.Add(new FixdatFeature()); + help.Add(new ImportFeature()); + help.Add(new LookupFeature()); + help.Add(new MemstatsFeature()); + help.Add(new MergeFeature()); + help.Add(new MissFeature()); + help.Add(new PurgeBackupFeature()); + help.Add(new PurgeDeleteFeature()); + help.Add(new RefreshDatsFeature()); + help.Add(new RescanDepotsFeature()); + help.Add(new ProgressFeature()); + help.Add(new ShutdownFeature()); + help.Add(new VersionFeature()); - Feature helpFeature = new Feature( - "Help", - new List() { "-?", "-h", "--help" }, - "Show this help", - FeatureType.Flag); + return help; + } - #endregion + #region Top-level Features - #region Script + private class RombaSharpFeature : TopLevel + { + } - Feature script = new Feature( - "Script", - "--script", - "Enable script mode (no clear screen)", - FeatureType.Flag, - longDescription: "For times when RombaSharp is being used in a scripted environment, the user may not want the screen to be cleared every time that it is called. This flag allows the user to skip clearing the screen on run just like if the console was being redirected."); + private class ArchiveFeature : RombaSharpFeature + { + public const string Value = "Archive"; - #endregion - - #region Archive - - Feature archive = new Feature( - "Archive", - "archive", - "Adds ROM files from the specified directories to the ROM archive.", - FeatureType.Flag, - longDescription: @"Adds ROM files from the specified directories to the ROM archive. + public ArchiveFeature() + { + this.Name = Value; + this.Flags = new List() { "archive" }; + this.Description = "Adds ROM files from the specified directories to the ROM archive."; + this._featureType = FeatureType.Flag; + this.LongDescription = @"Adds ROM files from the specified directories to the ROM archive. Traverses the specified directory trees looking for zip files and normal files. Unpacked files will be stored as individual entries. Prior to unpacking a zip file, the external SHA1 is checked against the DAT index. If -only-needed is set, only those files are put in the ROM archive that -have a current entry in the DAT index."); - archive.AddFeature(onlyNeededFlag); - archive.AddFeature(resumeStringInput); - archive.AddFeature(includeZipsInt32Input); // Defaults to 0 - archive.AddFeature(workersInt32Input); - archive.AddFeature(includeGZipsInt32Input); // Defaults to 0 - archive.AddFeature(include7ZipsInt32Input); // Defaults to 0 - archive.AddFeature(skipInitialScanFlag); - archive.AddFeature(useGolangZipFlag); - archive.AddFeature(noDbFlag); +have a current entry in the DAT index."; + this.Features = new Dictionary(); - #endregion + AddFeature(onlyNeededFlag); + AddFeature(resumeStringInput); + AddFeature(includeZipsInt32Input); // Defaults to 0 + AddFeature(workersInt32Input); + AddFeature(includeGZipsInt32Input); // Defaults to 0 + AddFeature(include7ZipsInt32Input); // Defaults to 0 + AddFeature(skipInitialScanFlag); + AddFeature(useGolangZipFlag); + AddFeature(noDbFlag); + } - #region Build + public override void ProcessFeatures(Dictionary features) + { + // Get the archive scanning level + int sevenzip = GetInt32(features, Include7ZipsInt32Value); + sevenzip = sevenzip == Int32.MinValue ? 1 : sevenzip; - Feature build = new Feature( - "Build", - "build", - "For each specified DAT file it creates the torrentzip files.", - FeatureType.Flag, - longDescription: @"For each specified DAT file it creates the torrentzip files in the specified + int gz = GetInt32(features, IncludeGZipsInt32Value); + gz = gz == Int32.MinValue ? 1 : gz; + + int zip = GetInt32(features, IncludeZipsInt32Value); + zip = zip == Int32.MinValue ? 1 : zip; + + var asl = Utilities.GetArchiveScanLevelFromNumbers(sevenzip, gz, 2, zip); + + // Get feature flags + bool noDb = GetBoolean(features, NoDbValue); + bool onlyNeeded = GetBoolean(features, OnlyNeededValue); + + // First we want to get just all directories from the inputs + List onlyDirs = new List(); + foreach (string input in Inputs) + { + if (Directory.Exists(input)) + onlyDirs.Add(Path.GetFullPath(input)); + } + + // Then process all of the input directories into an internal DAT + DatFile df = new DatFile(); + foreach (string dir in onlyDirs) + { + // TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually + df.PopulateFromDir(dir, Hash.DeepHashes, false, false, SkipFileType.None, false, false, _tmpdir, false, null, true, null); + df.PopulateFromDir(dir, Hash.DeepHashes, false, true, SkipFileType.None, false, false, _tmpdir, false, null, true, null); + } + + // Create an empty Dat for files that need to be rebuilt + DatFile need = new DatFile(); + + // Open the database connection + SqliteConnection dbc = new SqliteConnection(_connectionString); + dbc.Open(); + + // Now that we have the Dats, add the files to the database + string crcquery = "INSERT OR IGNORE INTO crc (crc) VALUES"; + string md5query = "INSERT OR IGNORE INTO md5 (md5) VALUES"; + string sha1query = "INSERT OR IGNORE INTO sha1 (sha1, depot) VALUES"; + string crcsha1query = "INSERT OR IGNORE INTO crcsha1 (crc, sha1) VALUES"; + string md5sha1query = "INSERT OR IGNORE INTO md5sha1 (md5, sha1) VALUES"; + + foreach (string key in df.Keys) + { + List datItems = df[key]; + foreach (Rom rom in datItems) + { + // If we care about if the file exists, check the databse first + if (onlyNeeded && !noDb) + { + string query = "SELECT * FROM crcsha1 JOIN md5sha1 ON crcsha1.sha1=md5sha1.sha1" + + $" WHERE crcsha1.crc=\"{rom.CRC}\"" + + $" OR md5sha1.md5=\"{rom.MD5}\"" + + $" OR md5sha1.sha1=\"{rom.SHA1}\""; + SqliteCommand slc = new SqliteCommand(query, dbc); + SqliteDataReader sldr = slc.ExecuteReader(); + + if (sldr.HasRows) + { + // Add to the queries + if (!string.IsNullOrWhiteSpace(rom.CRC)) + crcquery += $" (\"{rom.CRC}\"),"; + + if (!string.IsNullOrWhiteSpace(rom.MD5)) + md5query += $" (\"{rom.MD5}\"),"; + + if (!string.IsNullOrWhiteSpace(rom.SHA1)) + { + sha1query += $" (\"{rom.SHA1}\", \"{_depots.Keys.ToList()[0]}\"),"; + + if (!string.IsNullOrWhiteSpace(rom.CRC)) + crcsha1query += $" (\"{rom.CRC}\", \"{rom.SHA1}\"),"; + + if (!string.IsNullOrWhiteSpace(rom.MD5)) + md5sha1query += $" (\"{rom.MD5}\", \"{rom.SHA1}\"),"; + } + + // Add to the Dat + need.Add(key, rom); + } + } + // Otherwise, just add the file to the list + else + { + // Add to the queries + if (!noDb) + { + if (!string.IsNullOrWhiteSpace(rom.CRC)) + crcquery += $" (\"{rom.CRC}\"),"; + + if (!string.IsNullOrWhiteSpace(rom.MD5)) + md5query += $" (\"{rom.MD5}\"),"; + + if (!string.IsNullOrWhiteSpace(rom.SHA1)) + { + sha1query += $" (\"{rom.SHA1}\", \"{_depots.Keys.ToList()[0]}\"),"; + + if (!string.IsNullOrWhiteSpace(rom.CRC)) + crcsha1query += $" (\"{rom.CRC}\", \"{rom.SHA1}\"),"; + + if (!string.IsNullOrWhiteSpace(rom.MD5)) + md5sha1query += $" (\"{rom.MD5}\", \"{rom.SHA1}\"),"; + } + } + + // Add to the Dat + need.Add(key, rom); + } + } + } + + // Now run the queries, if they're populated + if (crcquery != "INSERT OR IGNORE INTO crc (crc) VALUES") + { + SqliteCommand slc = new SqliteCommand(crcquery.TrimEnd(','), dbc); + slc.ExecuteNonQuery(); + slc.Dispose(); + } + + if (md5query != "INSERT OR IGNORE INTO md5 (md5) VALUES") + { + SqliteCommand slc = new SqliteCommand(md5query.TrimEnd(','), dbc); + slc.ExecuteNonQuery(); + slc.Dispose(); + } + + if (sha1query != "INSERT OR IGNORE INTO sha1 (sha1, depot) VALUES") + { + SqliteCommand slc = new SqliteCommand(sha1query.TrimEnd(','), dbc); + slc.ExecuteNonQuery(); + slc.Dispose(); + } + + if (crcsha1query != "INSERT OR IGNORE INTO crcsha1 (crc, sha1) VALUES") + { + SqliteCommand slc = new SqliteCommand(crcsha1query.TrimEnd(','), dbc); + slc.ExecuteNonQuery(); + slc.Dispose(); + } + + if (md5sha1query != "INSERT OR IGNORE INTO md5sha1 (md5, sha1) VALUES") + { + SqliteCommand slc = new SqliteCommand(md5sha1query.TrimEnd(','), dbc); + slc.ExecuteNonQuery(); + slc.Dispose(); + } + + // Create the sorting object to use and rebuild the needed files + need.RebuildGeneric(onlyDirs, _depots.Keys.ToList()[0], false /*quickScan*/, false /*date*/, + false /*delete*/, false /*inverse*/, OutputFormat.TorrentGzipRomba, asl, false /*updateDat*/, + null /*headerToCheckAgainst*/, true /* chdsAsFiles */); + } + } + + private class BuildFeature : RombaSharpFeature + { + public const string Value = "Build"; + + public BuildFeature() + { + this.Name = Value; + this.Flags = new List() { "build" }; + this.Description = "For each specified DAT file it creates the torrentzip files."; + this._featureType = FeatureType.Flag; + this.LongDescription = @"For each specified DAT file it creates the torrentzip files in the specified output dir. The files will be placed in the specified location using a folder -structure according to the original DAT master directory tree structure."); - build.AddFeature(outStringInput); - build.AddFeature(fixdatOnlyFlag); - build.AddFeature(copyFlag); - build.AddFeature(workersInt32Input); - build.AddFeature(subworkersInt32Input); +structure according to the original DAT master directory tree structure."; + this.Features = new Dictionary(); - #endregion + AddFeature(outStringInput); + AddFeature(fixdatOnlyFlag); + AddFeature(copyFlag); + AddFeature(workersInt32Input); + AddFeature(subworkersInt32Input); + } - #region Cancel + public override void ProcessFeatures(Dictionary features) + { + // Get feature flags + bool copy = GetBoolean(features, CopyValue); + string outdat = GetString(features, OutStringValue); - Feature cancel = new Feature( - "Cancel", - "cancel", - "Cancels current long-running job", - FeatureType.Flag, - longDescription: @"Cancels current long-running job."); + // Verify the filenames + Dictionary foundDats = GetValidDats(Inputs); - #endregion + // Ensure the output directory is set + if (string.IsNullOrWhiteSpace(outdat)) + outdat = "out"; - #region DatStats + // Now that we have the dictionary, we can loop through and output to a new folder for each + foreach (string key in foundDats.Keys) + { + // Get the DAT file associated with the key + DatFile datFile = new DatFile(); + datFile.Parse(Path.Combine(_dats, foundDats[key]), 0, 0); - Feature datstats = new Feature( - "DatStats", - "datstats", - "Prints dat stats.", - FeatureType.Flag, - longDescription: @"Print dat stats."); + // Create the new output directory if it doesn't exist + string outputFolder = Path.Combine(outdat, Path.GetFileNameWithoutExtension(foundDats[key])); + Utilities.EnsureOutputDirectory(outputFolder, create: true); - #endregion + // Get all online depots + List onlineDepots = _depots.Where(d => d.Value.Item2).Select(d => d.Key).ToList(); - #region DbStats + // Now scan all of those depots and rebuild + ArchiveScanLevel asl = Utilities.GetArchiveScanLevelFromNumbers(1, 1, 1, 1); + datFile.RebuildDepot(onlineDepots, outputFolder, false /*date*/, + false /*delete*/, false /*inverse*/, (copy ? OutputFormat.TorrentGzipRomba : OutputFormat.TorrentZip), + false /*updateDat*/, null /*headerToCheckAgainst*/); + } + } + } - Feature dbstats = new Feature( - "DbStats", - "dbstats", - "Prints db stats.", - FeatureType.Flag, - longDescription: @"Print db stats."); + private class CancelFeature : RombaSharpFeature + { + public const string Value = "Cancel"; - #endregion + public CancelFeature() + { + this.Name = Value; + this.Flags = new List() { "cancel" }; + this.Description = "Cancels current long-running job"; + this._featureType = FeatureType.Flag; + this.LongDescription = "Cancels current long-running job."; + this.Features = new Dictionary(); + } - #region Diffdat + public override void ProcessFeatures(Dictionary features) + { + Globals.Logger.User("This feature is not yet implemented: cancel"); + } + } - Feature diffdat = new Feature( - "Diffdat", - "diffdat", - "Creates a DAT file with those entries that are in -new DAT.", - FeatureType.Flag, - longDescription: @"Creates a DAT file with those entries that are in -new DAT file and not -in -old DAT file. Ignores those entries in -old that are not in -new."); - diffdat.AddFeature(outStringInput); - diffdat.AddFeature(oldStringInput); - diffdat.AddFeature(newStringInput); - diffdat.AddFeature(nameStringInput); - diffdat.AddFeature(descriptionStringInput); + private class DatStatsFeature : RombaSharpFeature + { + public const string Value = "DatStats"; - #endregion + public DatStatsFeature() + { + this.Name = Value; + this.Flags = new List() { "datstats" }; + this.Description = "Prints dat stats."; + this._featureType = FeatureType.Flag; + this.LongDescription = "Print dat stats."; + this.Features = new Dictionary(); + } - #region Dir2Dat + public override void ProcessFeatures(Dictionary features) + { + // If we have no inputs listed, we want to use datroot + if (Inputs == null || Inputs.Count == 0) + { + Inputs = new List { Path.GetFullPath(_dats) }; + } - Feature dir2dat = new Feature( - "Dir2Dat", - "dir2dat", - "Creates a DAT file for the specified input directory and saves it to the -out filename.", - FeatureType.Flag); - dir2dat.AddFeature(outStringInput); - dir2dat.AddFeature(sourceStringInput); - dir2dat.AddFeature(nameStringInput); // Defaults to "untitled" - dir2dat.AddFeature(descriptionStringInput); + // Now output the stats for all inputs + DatFile.OutputStats(Inputs, "rombasharp-datstats", null /* outDir */, true /* single */, true /* baddumpCol */, true /* nodumpCol */, StatReportFormat.Textfile); + } + } - #endregion + private class DbStatsFeature : RombaSharpFeature + { + public const string Value = "DbStats"; - #region EDiffdat + public DbStatsFeature() + { + this.Name = Value; + this.Flags = new List() { "dbstats" }; + this.Description = "Prints db stats."; + this._featureType = FeatureType.Flag; + this.LongDescription = "Print db stats."; + this.Features = new Dictionary(); + } - Feature ediffdat = new Feature( - "EDiffdat", - "ediffdat", - "Creates a DAT file with those entries that are in -new DAT.", - FeatureType.Flag, - longDescription: @"Creates a DAT file with those entries that are in -new DAT files and not -in -old DAT files. Ignores those entries in -old that are not in -new."); - ediffdat.AddFeature(outStringInput); - ediffdat.AddFeature(oldStringInput); - ediffdat.AddFeature(newStringInput); + public override void ProcessFeatures(Dictionary features) + { + SqliteConnection dbc = new SqliteConnection(_connectionString); + dbc.Open(); - #endregion + // Total number of CRCs + string query = "SELECT COUNT(*) FROM crc"; + SqliteCommand slc = new SqliteCommand(query, dbc); + Globals.Logger.User($"Total CRCs: {(long)slc.ExecuteScalar()}"); - #region Export + // Total number of MD5s + query = "SELECT COUNT(*) FROM md5"; + slc = new SqliteCommand(query, dbc); + Globals.Logger.User($"Total MD5s: {(long)slc.ExecuteScalar()}"); + + // Total number of SHA1s + query = "SELECT COUNT(*) FROM sha1"; + slc = new SqliteCommand(query, dbc); + Globals.Logger.User($"Total SHA1s: {(long)slc.ExecuteScalar()}"); + + // Total number of DATs + query = "SELECT COUNT(*) FROM dat"; + slc = new SqliteCommand(query, dbc); + Globals.Logger.User($"Total DATs: {(long)slc.ExecuteScalar()}"); + + slc.Dispose(); + dbc.Dispose(); + } + } + + private class DetailedHelpFeature : RombaSharpFeature + { + public const string Value = "Help (Detailed)"; + + public DetailedHelpFeature() + { + this.Name = Value; + this.Flags = new List() { "-??", "-hd", "--help-detailed" }; + this.Description = "Show this detailed help"; + this._featureType = FeatureType.Flag; + this.LongDescription = "Display a detailed help text to the screen."; + this.Features = new Dictionary(); + } + + public override bool ProcessArgs(string[] args, Help help) + { + // If we had something else after help + if (args.Length > 1) + { + help.OutputIndividualFeature(args[1], includeLongDescription: true); + return true; + } + + // Otherwise, show generic help + else + { + help.OutputAllHelp(); + return true; + } + } + } + + private class DiffdatFeature : RombaSharpFeature + { + public const string Value = "Diffdat"; + + public DiffdatFeature() + { + this.Name = Value; + this.Flags = new List() { "diffdat" }; + this.Description = "Creates a DAT file with those entries that are in -new DAT."; + this._featureType = FeatureType.Flag; + this.LongDescription = @"Creates a DAT file with those entries that are in -new DAT file and not +in -old DAT file. Ignores those entries in -old that are not in -new."; + this.Features = new Dictionary(); + + AddFeature(outStringInput); + AddFeature(oldStringInput); + AddFeature(newStringInput); + AddFeature(nameStringInput); + AddFeature(descriptionStringInput); + } + + public override void ProcessFeatures(Dictionary features) + { + // Get feature flags + string name = GetString(features, NameStringValue); + string description = GetString(features, DescriptionStringValue); + string newdat = GetString(features, NewStringValue); + string olddat = GetString(features, OldStringValue); + string outdat = GetString(features, OutStringValue); + + // Ensure the output directory + Utilities.EnsureOutputDirectory(outdat, create: true); + + // Check that all required files exist + if (!File.Exists(olddat)) + { + Globals.Logger.Error($"File '{olddat}' does not exist!"); + return; + } + + if (!File.Exists(newdat)) + { + Globals.Logger.Error($"File '{newdat}' does not exist!"); + return; + } + + // Create the encapsulating datfile + DatFile datfile = new DatFile() + { + Name = name, + Description = description, + }; + + // Create the inputs + List dats = new List { newdat }; + List basedats = new List { olddat }; + + // Now run the diff on the inputs + datfile.DetermineUpdateType(dats, basedats, outdat, UpdateMode.DiffAgainst, false /* inplace */, false /* skip */, + false /* clean */, false /* remUnicode */, false /* descAsName */, new Filter(), SplitType.None, + new List(), false /* onlySame */); + } + } + + private class Dir2DatFeature : RombaSharpFeature + { + public const string Value = "Dir2Dat"; + + public Dir2DatFeature() + { + this.Name = Value; + this.Flags = new List() { "dir2dat" }; + this.Description = "Creates a DAT file for the specified input directory and saves it to the -out filename."; + this._featureType = FeatureType.Flag; + this.LongDescription = "Creates a DAT file for the specified input directory and saves it to the -out filename."; + this.Features = new Dictionary(); + + AddFeature(outStringInput); + AddFeature(sourceStringInput); + AddFeature(nameStringInput); // Defaults to "untitled" + AddFeature(descriptionStringInput); + } + + public override void ProcessFeatures(Dictionary features) + { + // Get feature flags + string name = GetString(features, NameStringValue); + string description = GetString(features, DescriptionStringValue); + string source = GetString(features, SourceStringValue); + string outdat = GetString(features, OutStringValue); + + // Ensure the output directory + Utilities.EnsureOutputDirectory(outdat, create: true); + + // Check that all required directories exist + if (!Directory.Exists(source)) + { + Globals.Logger.Error($"File '{source}' does not exist!"); + return; + } + + // Create the encapsulating datfile + DatFile datfile = new DatFile() + { + Name = (string.IsNullOrWhiteSpace(name) ? "untitled" : name), + Description = description, + }; + + // Now run the D2D on the input and write out + // TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually + datfile.PopulateFromDir(source, Hash.DeepHashes, true /* bare */, false /* archivesAsFiles */, SkipFileType.None, false /* addBlanks */, + false /* addDate */, _tmpdir, false /* copyFiles */, null /* headerToCheckAgainst */, true /* chdsAsFiles */, null /* filter */); + datfile.Write(outDir: outdat); + } + } + + private class EDiffdatFeature : RombaSharpFeature + { + public const string Value = "EDiffdat"; + + public EDiffdatFeature() + { + this.Name = Value; + this.Flags = new List() { "ediffdat" }; + this.Description = "Creates a DAT file with those entries that are in -new DAT."; + this._featureType = FeatureType.Flag; + this.LongDescription = @"Creates a DAT file with those entries that are in -new DAT files and not +in -old DAT files. Ignores those entries in -old that are not in -new."; + this.Features = new Dictionary(); + + AddFeature(outStringInput); + AddFeature(oldStringInput); + AddFeature(newStringInput); + } + + public override void ProcessFeatures(Dictionary features) + { + // Get feature flags + string olddat = GetString(features, OldStringValue); + string outdat = GetString(features, OutStringValue); + string newdat = GetString(features, NewStringValue); + + // Ensure the output directory + Utilities.EnsureOutputDirectory(outdat, create: true); + + // Check that all required files exist + if (!File.Exists(olddat)) + { + Globals.Logger.Error($"File '{olddat}' does not exist!"); + return; + } + + if (!File.Exists(newdat)) + { + Globals.Logger.Error($"File '{newdat}' does not exist!"); + return; + } + + // Create the encapsulating datfile + DatFile datfile = new DatFile(); + + // Create the inputs + List dats = new List { newdat }; + List basedats = new List { olddat }; + + // Now run the diff on the inputs + datfile.DetermineUpdateType(dats, basedats, outdat, UpdateMode.DiffAgainst, false /* inplace */, false /* skip */, + false /* clean */, false /* remUnicode */, false /* descAsName */, new Filter(), SplitType.None, + new List(), false /* onlySame */); + } + } + + private class ExportFeature : RombaSharpFeature + { + public const string Value = "Export"; // Unique to RombaSharp - Feature export = new Feature( - "Export", - "export", - "Exports db to export.csv", - FeatureType.Flag, - longDescription: "Exports db to standardized export.csv"); + public ExportFeature() + { + this.Name = Value; + this.Flags = new List() { "export" }; + this.Description = "Exports db to export.csv"; + this._featureType = FeatureType.Flag; + this.LongDescription = "Exports db to standardized export.csv"; + this.Features = new Dictionary(); + } - #endregion + // TODO: Add ability to say which depot the files are found in + public override void ProcessFeatures(Dictionary features) + { + SqliteConnection dbc = new SqliteConnection(_connectionString); + dbc.Open(); + StreamWriter sw = new StreamWriter(Utilities.TryCreate("export.csv")); - #region Fixdat + // First take care of all file hashes + sw.WriteLine("CRC,MD5,SHA-1"); // ,Depot - Feature fixdat = new Feature( - "Fixdat", - "fixdat", - "For each specified DAT file it creates a fix DAT.", - FeatureType.Flag, - longDescription: @"For each specified DAT file it creates a fix DAT with the missing entries for + string query = "SELECT crcsha1.crc, md5sha1.md5, md5sha1.sha1 FROM crcsha1 JOIN md5sha1 ON crcsha1.sha1=md5sha1.sha1"; // md5sha1.sha1=sha1depot.sha1 + SqliteCommand slc = new SqliteCommand(query, dbc); + SqliteDataReader sldr = slc.ExecuteReader(); + + if (sldr.HasRows) + { + while (sldr.Read()) + { + string line = $"{sldr.GetString(0)},{sldr.GetString(1)},{sldr.GetString(2)}"; // + ",{sldr.GetString(3)}"; + sw.WriteLine(line); + } + } + + // Then take care of all DAT hashes + sw.WriteLine(); + sw.WriteLine("DAT Hash"); + + query = "SELECT hash FROM dat"; + slc = new SqliteCommand(query, dbc); + sldr = slc.ExecuteReader(); + + if (sldr.HasRows) + { + while (sldr.Read()) + { + sw.WriteLine(sldr.GetString(0)); + } + } + + sldr.Dispose(); + slc.Dispose(); + sw.Dispose(); + dbc.Dispose(); + } + } + + private class FixdatFeature : RombaSharpFeature + { + public const string Value = "Fixdat"; + + public FixdatFeature() + { + this.Name = Value; + this.Flags = new List() { "fixdat" }; + this.Description = "For each specified DAT file it creates a fix DAT."; + this._featureType = FeatureType.Flag; + this.LongDescription = @"For each specified DAT file it creates a fix DAT with the missing entries for that DAT. If nothing is missing it doesn't create a fix DAT for that -particular DAT."); - fixdat.AddFeature(outStringInput); - fixdat.AddFeature(fixdatOnlyFlag); // Enabled by default - fixdat.AddFeature(workersInt32Input); - fixdat.AddFeature(subworkersInt32Input); +particular DAT."; + this.Features = new Dictionary(); - #endregion + AddFeature(outStringInput); + AddFeature(fixdatOnlyFlag); // Enabled by default + AddFeature(workersInt32Input); + AddFeature(subworkersInt32Input); + } - #region Import + public override void ProcessFeatures(Dictionary features) + { + // Get feature flags + // Inputs + bool fixdatOnly = GetBoolean(features, FixdatOnlyValue); + int subworkers = GetInt32(features, SubworkersInt32Value); + int workers = GetInt32(features, WorkersInt32Value); + string outdat = GetString(features, OutStringValue); + + Globals.Logger.Error("This feature is not yet implemented: fixdat"); + } + } + + private class HelpFeature : RombaSharpFeature + { + public const string Value = "Help"; + + public HelpFeature() + { + this.Name = Value; + this.Flags = new List() { "-?", "-h", "--help" }; + this.Description = "Show this help"; + this._featureType = FeatureType.Flag; + this.LongDescription = "Built-in to most of the programs is a basic help text."; + this.Features = new Dictionary(); + } + + public override bool ProcessArgs(string[] args, Help help) + { + // If we had something else after help + if (args.Length > 1) + { + help.OutputIndividualFeature(args[1]); + return true; + } + + // Otherwise, show generic help + else + { + help.OutputGenericHelp(); + return true; + } + } + } + + private class ImportFeature : RombaSharpFeature + { + public const string Value = "Import"; // Unique to RombaSharp - Feature import = new Feature( - "Import", - "import", - "Import a database from a formatted CSV file", - FeatureType.Flag, - longDescription: @"Import a database from a formatted CSV file"); + public ImportFeature() + { + this.Name = Value; + this.Flags = new List() { "import" }; + this.Description = "Import a database from a formatted CSV file"; + this._featureType = FeatureType.Flag; + this.LongDescription = "Import a database from a formatted CSV file"; + this.Features = new Dictionary(); + } - #endregion + public override void ProcessFeatures(Dictionary features) + { + Globals.Logger.Error("This feature is not yet implemented: import"); - #region Lookup + // First ensure the inputs and database connection + Inputs = Utilities.GetOnlyFilesFromInputs(Inputs); + SqliteConnection dbc = new SqliteConnection(_connectionString); + SqliteCommand slc = new SqliteCommand(); + dbc.Open(); - Feature lookup = new Feature( - "Lookup", - "lookup", - "For each specified hash it looks up any available information.", - FeatureType.Flag, - longDescription: @"For each specified hash it looks up any available information (dat or rom)."); - lookup.AddFeature(sizeInt64Input); // Defaults to -1 - lookup.AddFeature(outStringInput); + // Now, for each of these files, attempt to add the data found inside + foreach (string input in Inputs) + { + StreamReader sr = new StreamReader(Utilities.TryOpenRead(input)); - #endregion + // The first line should be the hash header + string line = sr.ReadLine(); + if (line != "CRC,MD5,SHA-1") // ,Depot + { + Globals.Logger.Error("{0} is not a valid export file"); + continue; + } - #region Memstats + // Define the insert queries + string crcquery = "INSERT OR IGNORE INTO crc (crc) VALUES"; + string md5query = "INSERT OR IGNORE INTO md5 (md5) VALUES"; + string sha1query = "INSERT OR IGNORE INTO sha1 (sha1) VALUES"; + string crcsha1query = "INSERT OR IGNORE INTO crcsha1 (crc, sha1) VALUES"; + string md5sha1query = "INSERT OR IGNORE INTO md5sha1 (md5, sha1) VALUES"; - Feature memstats = new Feature( - "Memstats", - "memstats", - "Prints memory stats.", - FeatureType.Flag, - longDescription: @"Print memory stats."); + // For each line until we hit a blank line... + while (!sr.EndOfStream && line != string.Empty) + { + line = sr.ReadLine(); + string[] hashes = line.Split(','); - #endregion + // Loop through the parsed entries + if (!string.IsNullOrWhiteSpace(hashes[0])) + crcquery += $" (\"{hashes[0]}\"),"; - #region Merge + if (!string.IsNullOrWhiteSpace(hashes[1])) + md5query += $" (\"{hashes[1]}\"),"; - Feature merge = new Feature( - "Merge", - "merge", - "Merges depot", - FeatureType.Flag, - longDescription: @"Merges specified depot into current depot."); - merge.AddFeature(onlyNeededFlag); - merge.AddFeature(resumeStringInput); - merge.AddFeature(workersInt32Input); - merge.AddFeature(skipInitialScanFlag); + if (!string.IsNullOrWhiteSpace(hashes[2])) + { + sha1query += $" (\"{hashes[2]}\"),"; - #endregion + if (!string.IsNullOrWhiteSpace(hashes[0])) + crcsha1query += $" (\"{hashes[0]}\", \"{hashes[2]}\"),"; - #region Miss + if (!string.IsNullOrWhiteSpace(hashes[1])) + md5sha1query += $" (\"{hashes[1]}\", \"{hashes[2]}\"),"; + } + } + + // Now run the queries after fixing them + if (crcquery != "INSERT OR IGNORE INTO crc (crc) VALUES") + { + slc = new SqliteCommand(crcquery.TrimEnd(','), dbc); + slc.ExecuteNonQuery(); + } + + if (md5query != "INSERT OR IGNORE INTO md5 (md5) VALUES") + { + slc = new SqliteCommand(md5query.TrimEnd(','), dbc); + slc.ExecuteNonQuery(); + } + + if (sha1query != "INSERT OR IGNORE INTO sha1 (sha1) VALUES") + { + slc = new SqliteCommand(sha1query.TrimEnd(','), dbc); + slc.ExecuteNonQuery(); + } + + if (crcsha1query != "INSERT OR IGNORE INTO crcsha1 (crc, sha1) VALUES") + { + slc = new SqliteCommand(crcsha1query.TrimEnd(','), dbc); + slc.ExecuteNonQuery(); + } + + if (md5sha1query != "INSERT OR IGNORE INTO md5sha1 (md5, sha1) VALUES") + { + slc = new SqliteCommand(md5sha1query.TrimEnd(','), dbc); + slc.ExecuteNonQuery(); + } + + // Now add all of the DAT hashes + // TODO: Do we really need to save the DAT hashes? + + sr.Dispose(); + } + + slc.Dispose(); + dbc.Dispose(); + } + } + + private class LookupFeature : RombaSharpFeature + { + public const string Value = "Lookup"; + + public LookupFeature() + { + this.Name = Value; + this.Flags = new List() { "lookup" }; + this.Description = "For each specified hash it looks up any available information."; + this._featureType = FeatureType.Flag; + this.LongDescription = "For each specified hash it looks up any available information (dat or rom)."; + this.Features = new Dictionary(); + + AddFeature(sizeInt64Input); // Defaults to -1 + AddFeature(outStringInput); + } + + public override void ProcessFeatures(Dictionary features) + { + // Get feature flags + long size = GetInt64(features, SizeInt64Value); + string outdat = GetString(features, OutStringValue); + + // First, try to figure out what type of hash each is by length and clean it + List crc = new List(); + List md5 = new List(); + List sha1 = new List(); + foreach (string input in Inputs) + { + string temp = string.Empty; + if (input.Length == Constants.CRCLength) + { + temp = Utilities.CleanHashData(input, Constants.CRCLength); + if (!string.IsNullOrWhiteSpace(temp)) + { + crc.Add(temp); + } + } + else if (input.Length == Constants.MD5Length) + { + temp = Utilities.CleanHashData(input, Constants.MD5Length); + if (!string.IsNullOrWhiteSpace(temp)) + { + md5.Add(temp); + } + } + else if (input.Length == Constants.SHA1Length) + { + temp = Utilities.CleanHashData(input, Constants.SHA1Length); + if (!string.IsNullOrWhiteSpace(temp)) + { + sha1.Add(temp); + } + } + } + + SqliteConnection dbc = new SqliteConnection(_connectionString); + dbc.Open(); + + // Now, search for each of them and return true or false for each + foreach (string input in crc) + { + string query = $"SELECT * FROM crc WHERE crc=\"{input}\""; + SqliteCommand slc = new SqliteCommand(query, dbc); + SqliteDataReader sldr = slc.ExecuteReader(); + if (sldr.HasRows) + { + int count = 0; + while (sldr.Read()) + { + count++; + } + + Globals.Logger.User($"For hash '{input}' there were {count} matches in the database"); + } + else + { + Globals.Logger.User($"Hash '{input}' had no matches in the database"); + } + + sldr.Dispose(); + slc.Dispose(); + } + foreach (string input in md5) + { + string query = $"SELECT * FROM md5 WHERE md5=\"{input}\""; + SqliteCommand slc = new SqliteCommand(query, dbc); + SqliteDataReader sldr = slc.ExecuteReader(); + if (sldr.HasRows) + { + int count = 0; + while (sldr.Read()) + { + count++; + } + + Globals.Logger.User($"For hash '{input}' there were {count} matches in the database"); + } + else + { + Globals.Logger.User($"Hash '{input}' had no matches in the database"); + } + + sldr.Dispose(); + slc.Dispose(); + } + foreach (string input in sha1) + { + string query = $"SELECT * FROM sha1 WHERE sha1=\"{input}\""; + SqliteCommand slc = new SqliteCommand(query, dbc); + SqliteDataReader sldr = slc.ExecuteReader(); + if (sldr.HasRows) + { + int count = 0; + while (sldr.Read()) + { + count++; + } + + Globals.Logger.User($"For hash '{input}' there were {count} matches in the database"); + } + else + { + Globals.Logger.User($"Hash '{input}' had no matches in the database"); + } + + sldr.Dispose(); + slc.Dispose(); + } + + dbc.Dispose(); + } + } + + private class MemstatsFeature : RombaSharpFeature + { + public const string Value = "Memstats"; + + public MemstatsFeature() + { + this.Name = Value; + this.Flags = new List() { "memstats" }; + this.Description = "Prints memory stats."; + this._featureType = FeatureType.Flag; + this.LongDescription = "Print memory stats."; + this.Features = new Dictionary(); + } + + public override void ProcessFeatures(Dictionary features) + { + Globals.Logger.User("This feature is not yet implemented: memstats"); + } + } + + private class MergeFeature : RombaSharpFeature + { + public const string Value = "Merge"; + + public MergeFeature() + { + this.Name = Value; + this.Flags = new List() { "merge" }; + this.Description = "Merges depot"; + this._featureType = FeatureType.Flag; + this.LongDescription = "Merges specified depot into current depot."; + this.Features = new Dictionary(); + + AddFeature(onlyNeededFlag); + AddFeature(resumeStringInput); + AddFeature(workersInt32Input); + AddFeature(skipInitialScanFlag); + } + + // TODO: Add way of specifying "current depot" since that's what Romba relies on + public override void ProcessFeatures(Dictionary features) + { + // Get feature flags + bool onlyNeeded = GetBoolean(features, OnlyNeededValue); + bool skipInitialscan = GetBoolean(features, SkipInitialScanValue); + int workers = GetInt32(features, WorkersInt32Value); + string resume = GetString(features, ResumeStringValue); + + Globals.Logger.Error("This feature is not yet implemented: merge"); + + // Verify that the inputs are valid directories + Inputs = Utilities.GetOnlyDirectoriesFromInputs(Inputs); + + // Loop over all input directories + foreach (string input in Inputs) + { + List depotFiles = Directory.EnumerateFiles(input, "*.gz", SearchOption.AllDirectories).ToList(); + + // If we are copying all that is possible but we want to scan first + if (!onlyNeeded && !skipInitialscan) + { + + } + + // If we are copying all that is possible but we don't care to scan first + else if (!onlyNeeded && skipInitialscan) + { + + } + + // If we are copying only what is needed but we want to scan first + else if (onlyNeeded && !skipInitialscan) + { + + } + + // If we are copying only what is needed but we don't care to scan first + else if (onlyNeeded && skipInitialscan) + { + + } + } + } + } + + private class MissFeature : RombaSharpFeature + { + public const string Value = "Miss"; // Unique to RombaSharp - Feature miss = new Feature( - "Miss", - "miss", - "Create miss and have file", - FeatureType.Flag, - longDescription: @"For each specified DAT file, create miss and have file"); + public MissFeature() + { + this.Name = Value; + this.Flags = new List() { "miss" }; + this.Description = "Create miss and have file"; + this._featureType = FeatureType.Flag; + this.LongDescription = "For each specified DAT file, create miss and have file"; + this.Features = new Dictionary(); + } - #endregion + public override void ProcessFeatures(Dictionary features) + { + // Verify the filenames + Dictionary foundDats = GetValidDats(Inputs); - #region Progress + // Create the new output directory if it doesn't exist + Utilities.EnsureOutputDirectory(Path.Combine(Globals.ExeDir, "out"), create: true); - Feature progress = new Feature( - "Progress", - "progress", - "Shows progress of the currently running command.", - FeatureType.Flag, - longDescription: @"Shows progress of the currently running command."); + // Now that we have the dictionary, we can loop through and output to a new folder for each + foreach (string key in foundDats.Keys) + { + // Get the DAT file associated with the key + DatFile datFile = new DatFile(); + datFile.Parse(Path.Combine(_dats, foundDats[key]), 0, 0); - #endregion + // Now loop through and see if all of the hash combinations exist in the database + /* ended here */ + } - #region Purge Backup + Globals.Logger.Error("This feature is not yet implemented: miss"); + } + } - Feature purgeBackup = new Feature( - "Purge Backup", - "purge-backup", - "Moves DAT index entries for orphaned DATs.", - FeatureType.Flag, - longDescription: @"Deletes DAT index entries for orphaned DATs and moves ROM files that are no + private class ProgressFeature : RombaSharpFeature + { + public const string Value = "Progress"; + + public ProgressFeature() + { + this.Name = Value; + this.Flags = new List() { "progress" }; + this.Description = "Shows progress of the currently running command."; + this._featureType = FeatureType.Flag; + this.LongDescription = "Shows progress of the currently running command."; + this.Features = new Dictionary(); + } + + public override void ProcessFeatures(Dictionary features) + { + Globals.Logger.User("This feature is not yet implemented: progress"); + } + } + + private class PurgeBackupFeature : RombaSharpFeature + { + public const string Value = "Purge Backup"; + + public PurgeBackupFeature() + { + this.Name = Value; + this.Flags = new List() { "purge-backup" }; + this.Description = "Moves DAT index entries for orphaned DATs."; + this._featureType = FeatureType.Flag; + this.LongDescription = @"Deletes DAT index entries for orphaned DATs and moves ROM files that are no longer associated with any current DATs to the specified backup folder. The files will be placed in the backup location using a folder structure according to the original DAT master directory tree -structure. It also deletes the specified DATs from the DAT index."); - purgeBackup.AddFeature(backupStringInput); - purgeBackup.AddFeature(workersInt32Input); - purgeBackup.AddFeature(depotListStringInput); - purgeBackup.AddFeature(datsListStringInput); - purgeBackup.AddFeature(logOnlyFlag); +structure. It also deletes the specified DATs from the DAT index."; + this.Features = new Dictionary(); - #endregion + AddFeature(backupStringInput); + AddFeature(workersInt32Input); + AddFeature(depotListStringInput); + AddFeature(datsListStringInput); + AddFeature(logOnlyFlag); + } - #region Purge Delete + public override void ProcessFeatures(Dictionary features) + { + // Get feature flags + bool logOnly = GetBoolean(features, LogOnlyValue); + int workers = GetInt32(features, WorkersInt32Value); + string backup = GetString(features, BackupStringValue); + List dats = GetList(features, DatsListStringValue); + List depot = GetList(features, DepotListStringValue); + + Globals.Logger.Error("This feature is not yet implemented: purge-backup"); + } + } + + private class PurgeDeleteFeature : RombaSharpFeature + { + public const string Value = "Purge Delete"; // Unique to RombaSharp - Feature purgeDelete = new Feature( - "Purge Delete", - "purge-delete", - "Deletes DAT index entries for orphaned DATs", - FeatureType.Flag); - purgeDelete.AddFeature(workersInt32Input); - purgeDelete.AddFeature(depotListStringInput); - purgeDelete.AddFeature(datsListStringInput); - purgeDelete.AddFeature(logOnlyFlag); + public PurgeDeleteFeature() + { + this.Name = Value; + this.Flags = new List() { "purge-delete" }; + this.Description = "Deletes DAT index entries for orphaned DATs"; + this._featureType = FeatureType.Flag; + this.LongDescription = @"Deletes DAT index entries for orphaned DATs and moves ROM files that are no +longer associated with any current DATs to the specified backup folder. +The files will be placed in the backup location using +a folder structure according to the original DAT master directory tree +structure. It also deletes the specified DATs from the DAT index."; + this.Features = new Dictionary(); - #endregion + AddFeature(workersInt32Input); + AddFeature(depotListStringInput); + AddFeature(datsListStringInput); + AddFeature(logOnlyFlag); + } - #region Refresh DATs + public override void ProcessFeatures(Dictionary features) + { + // Get feature flags + bool logOnly = GetBoolean(features, LogOnlyValue); + int workers = GetInt32(features, WorkersInt32Value); + List dats = GetList(features, DatsListStringValue); + List depot = GetList(features, DepotListStringValue); - Feature refreshDats = new Feature( - "Refresh DATs", - "refresh-dats", - "Refreshes the DAT index from the files in the DAT master directory tree.", - FeatureType.Flag, - longDescription: @"Refreshes the DAT index from the files in the DAT master directory tree. + Globals.Logger.Error("This feature is not yet implemented: purge-delete"); + } + } + + private class RefreshDatsFeature : RombaSharpFeature + { + public const string Value = "Refresh DATs"; + + public RefreshDatsFeature() + { + this.Name = Value; + this.Flags = new List() { "refresh-dats" }; + this.Description = "Refreshes the DAT index from the files in the DAT master directory tree."; + this._featureType = FeatureType.Flag; + this.LongDescription = @"Refreshes the DAT index from the files in the DAT master directory tree. Detects any changes in the DAT master directory tree and updates the DAT index accordingly, marking deleted or overwritten dats as orphaned and updating -contents of any changed dats."); - refreshDats.AddFeature(workersInt32Input); - refreshDats.AddFeature(missingSha1sStringInput); +contents of any changed dats."; + this.Features = new Dictionary(); - #endregion + AddFeature(workersInt32Input); + AddFeature(missingSha1sStringInput); + } - #region Recan Depots + public override void ProcessFeatures(Dictionary features) + { + // Get feature flags + int workers = GetInt32(features, WorkersInt32Value); + string missingSha1s = GetString(features, MissingSha1sStringValue); + + // Make sure the db is set + if (string.IsNullOrWhiteSpace(_db)) + { + _db = "db.sqlite"; + _connectionString = $"Data Source={_db};Version = 3;"; + } + + // Make sure the file exists + if (!File.Exists(_db)) + DatabaseTools.EnsureDatabase(_dbSchema, _db, _connectionString); + + // Make sure the dats dir is set + if (string.IsNullOrWhiteSpace(_dats)) + _dats = "dats"; + + _dats = Path.Combine(Globals.ExeDir, _dats); + + // Make sure the folder exists + if (!Directory.Exists(_dats)) + Directory.CreateDirectory(_dats); + + // First get a list of SHA-1's from the input DATs + DatFile datroot = new DatFile { Type = "SuperDAT", }; + // TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually + datroot.PopulateFromDir(_dats, Hash.DeepHashes, false, false, SkipFileType.None, false, false, _tmpdir, false, null, true, null); + datroot.BucketBy(SortedBy.SHA1, DedupeType.None); + + // Create a List of dat hashes in the database (SHA-1) + List databaseDats = new List(); + List unneeded = new List(); + + SqliteConnection dbc = new SqliteConnection(_connectionString); + dbc.Open(); + + // Populate the List from the database + InternalStopwatch watch = new InternalStopwatch("Populating the list of existing DATs"); + + string query = "SELECT DISTINCT hash FROM dat"; + SqliteCommand slc = new SqliteCommand(query, dbc); + SqliteDataReader sldr = slc.ExecuteReader(); + if (sldr.HasRows) + { + sldr.Read(); + string hash = sldr.GetString(0); + if (datroot.Contains(hash)) + { + datroot.Remove(hash); + databaseDats.Add(hash); + } + else if (!databaseDats.Contains(hash)) + { + unneeded.Add(hash); + } + } + datroot.BucketBy(SortedBy.Game, DedupeType.None, norename: true); + + watch.Stop(); + + slc.Dispose(); + sldr.Dispose(); + + // Loop through the Dictionary and add all data + watch.Start("Adding new DAT information"); + foreach (string key in datroot.Keys) + { + foreach (Rom value in datroot[key]) + { + AddDatToDatabase(value, dbc); + } + } + + watch.Stop(); + + // Now loop through and remove all references to old Dats + if (unneeded.Count > 0) + { + watch.Start("Removing unmatched DAT information"); + + query = "DELETE FROM dat WHERE"; + foreach (string dathash in unneeded) + { + query += $" OR hash=\"{dathash}\""; + } + + query = query.Replace("WHERE OR", "WHERE"); + slc = new SqliteCommand(query, dbc); + slc.ExecuteNonQuery(); + slc.Dispose(); + + watch.Stop(); + } + + dbc.Dispose(); + } + } + + private class RescanDepotsFeature : RombaSharpFeature + { + public const string Value = "Rescan Depots"; // Unique to RombaSharp - Feature rescanDepots = new Feature( - "Rescan Depots", - "depot-rescan", - "Rescan a specific depot to get new information", - FeatureType.Flag); + public RescanDepotsFeature() + { + this.Name = Value; + this.Flags = new List() { "depot-rescan" }; + this.Description = "Rescan a specific depot to get new information"; + this._featureType = FeatureType.Flag; + this.LongDescription = "Rescan a specific depot to get new information"; + this.Features = new Dictionary(); + } - #endregion + public override void ProcessFeatures(Dictionary features) + { + Globals.Logger.Error("This feature is not yet implemented: rescan-depots"); - #region Shutdown + foreach (string depotname in Inputs) + { + // Check that it's a valid depot first + if (!_depots.ContainsKey(depotname)) + { + Globals.Logger.User($"'{depotname}' is not a recognized depot. Please add it to your configuration file and try again"); + return; + } - Feature shutdown = new Feature( - "Shutdown", - "shutdown", - "Gracefully shuts down server.", - FeatureType.Flag, - longDescription: @"Gracefully shuts down server saving all the cached data."); + // Then check that the depot is online + if (!Directory.Exists(depotname)) + { + Globals.Logger.User($"'{depotname}' does not appear to be online. Please check its status and try again"); + return; + } - #endregion + // Open the database connection + SqliteConnection dbc = new SqliteConnection(_connectionString); + dbc.Open(); - #region Version + // If we have it, then check for all hashes that are in that depot + List hashes = new List(); + string query = $"SELECT sha1 FROM sha1 WHERE depot=\"{depotname}\""; + SqliteCommand slc = new SqliteCommand(query, dbc); + SqliteDataReader sldr = slc.ExecuteReader(); + if (sldr.HasRows) + { + while (sldr.Read()) + { + hashes.Add(sldr.GetString(0)); + } + } - Feature version = new Feature( - "Version", - "version", - "Prints version", - FeatureType.Flag, - longDescription: @"Prints version."); + // Now rescan the depot itself + DatFile depot = new DatFile(); + // TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually + depot.PopulateFromDir(depotname, Hash.DeepHashes, false, false, SkipFileType.None, false, false, _tmpdir, false, null, true, null); + depot.BucketBy(SortedBy.SHA1, DedupeType.None); - #endregion + // Set the base queries to use + string crcquery = "INSERT OR IGNORE INTO crc (crc) VALUES"; + string md5query = "INSERT OR IGNORE INTO md5 (md5) VALUES"; + string sha1query = "INSERT OR IGNORE INTO sha1 (sha1, depot) VALUES"; + string crcsha1query = "INSERT OR IGNORE INTO crcsha1 (crc, sha1) VALUES"; + string md5sha1query = "INSERT OR IGNORE INTO md5sha1 (md5, sha1) VALUES"; - // Now, add all of the main features to the Help object - help.Add(helpFeature); - help.Add(script); - help.Add(archive); - help.Add(build); - help.Add(cancel); - help.Add(datstats); - help.Add(dbstats); - help.Add(diffdat); - help.Add(dir2dat); - help.Add(ediffdat); - help.Add(export); - help.Add(fixdat); - help.Add(import); - help.Add(lookup); - help.Add(memstats); - help.Add(merge); - help.Add(miss); - help.Add(purgeBackup); - help.Add(purgeDelete); - help.Add(refreshDats); - help.Add(rescanDepots); - help.Add(progress); - help.Add(shutdown); - help.Add(version); + // Once we have both, check for any new files + List dupehashes = new List(); + List keys = depot.Keys; + foreach (string key in keys) + { + List roms = depot[key]; + foreach (Rom rom in roms) + { + if (hashes.Contains(rom.SHA1)) + { + dupehashes.Add(rom.SHA1); + hashes.Remove(rom.SHA1); + } + else if (!dupehashes.Contains(rom.SHA1)) + { + if (!string.IsNullOrWhiteSpace(rom.CRC)) + crcquery += $" (\"{rom.CRC}\"),"; - return help; + if (!string.IsNullOrWhiteSpace(rom.MD5)) + md5query += $" (\"{rom.MD5}\"),"; + + if (!string.IsNullOrWhiteSpace(rom.SHA1)) + { + sha1query += $" (\"{rom.SHA1}\", \"{depotname}\"),"; + + if (!string.IsNullOrWhiteSpace(rom.CRC)) + crcsha1query += $" (\"{rom.CRC}\", \"{rom.SHA1}\"),"; + + if (!string.IsNullOrWhiteSpace(rom.MD5)) + md5sha1query += $" (\"{rom.MD5}\", \"{rom.SHA1}\"),"; + } + } + } + } + + // Now run the queries after fixing them + if (crcquery != "INSERT OR IGNORE INTO crc (crc) VALUES") + { + slc = new SqliteCommand(crcquery.TrimEnd(','), dbc); + slc.ExecuteNonQuery(); + } + + if (md5query != "INSERT OR IGNORE INTO md5 (md5) VALUES") + { + slc = new SqliteCommand(md5query.TrimEnd(','), dbc); + slc.ExecuteNonQuery(); + } + + if (sha1query != "INSERT OR IGNORE INTO sha1 (sha1, depot) VALUES") + { + slc = new SqliteCommand(sha1query.TrimEnd(','), dbc); + slc.ExecuteNonQuery(); + } + + if (crcsha1query != "INSERT OR IGNORE INTO crcsha1 (crc, sha1) VALUES") + { + slc = new SqliteCommand(crcsha1query.TrimEnd(','), dbc); + slc.ExecuteNonQuery(); + } + + if (md5sha1query != "INSERT OR IGNORE INTO md5sha1 (md5, sha1) VALUES") + { + slc = new SqliteCommand(md5sha1query.TrimEnd(','), dbc); + slc.ExecuteNonQuery(); + } + + // Now that we've added the information, we get to remove all of the hashes that we want to + query = @"DELETE FROM sha1 +JOIN crcsha1 + ON sha1.sha1=crcsha1.sha1 +JOIN md5sha1 + ON sha1.sha1=md5sha1.sha1 +JOIN crc + ON crcsha1.crc=crc.crc +JOIN md5 + ON md5sha1.md5=md5.md5 +WHERE sha1.sha1 IN "; + query += $"({string.Join("\",\"", hashes)}\")"; + slc = new SqliteCommand(query, dbc); + slc.ExecuteNonQuery(); + + // Dispose of the database connection + slc.Dispose(); + dbc.Dispose(); + } + } } + + private class ScriptFeature : RombaSharpFeature + { + public const string Value = "Script"; + + public ScriptFeature() + { + this.Name = Value; + this.Flags = new List() { "--script" }; + this.Description = "Enable script mode (no clear screen)"; + this._featureType = FeatureType.Flag; + this.LongDescription = "For times when RombaSharp is being used in a scripted environment, the user may not want the screen to be cleared every time that it is called. This flag allows the user to skip clearing the screen on run just like if the console was being redirected."; + this.Features = new Dictionary(); + } + } + + private class ShutdownFeature : RombaSharpFeature + { + public const string Value = "Shutdown"; + + public ShutdownFeature() + { + this.Name = Value; + this.Flags = new List() { "shutdown" }; + this.Description = "Gracefully shuts down server."; + this._featureType = FeatureType.Flag; + this.LongDescription = "Gracefully shuts down server saving all the cached data."; + this.Features = new Dictionary(); + } + + public override void ProcessFeatures(Dictionary features) + { + Globals.Logger.User("This feature is not yet implemented: shutdown"); + } + } + + private class VersionFeature : RombaSharpFeature + { + public const string Value = "Version"; + + public VersionFeature() + { + this.Name = Value; + this.Flags = new List() { "version" }; + this.Description = "Prints version"; + this._featureType = FeatureType.Flag; + this.LongDescription = "Prints version."; + this.Features = new Dictionary(); + } + + public override void ProcessFeatures(Dictionary features) + { + Globals.Logger.User($"RombaSharp version: {Constants.Version}"); + } + } + + #endregion } } diff --git a/RombaSharp/RombaSharp.Helpers.cs b/RombaSharp/RombaSharp.Helpers.cs index aa00cc9e..2772cbd7 100644 --- a/RombaSharp/RombaSharp.Helpers.cs +++ b/RombaSharp/RombaSharp.Helpers.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; +using System.IO; using System.Linq; using System.Xml; using Mono.Data.Sqlite; @@ -10,15 +10,6 @@ using SabreTools.Library.DatFiles; using SabreTools.Library.DatItems; using SabreTools.Library.Tools; -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using SearchOption = System.IO.SearchOption; -using StreamWriter = System.IO.StreamWriter; -#endif - namespace RombaSharp { public partial class RombaSharp @@ -46,7 +37,7 @@ namespace RombaSharp } else { - Globals.Logger.Warning("The file '{0}' could not be found in the DAT root", input); + Globals.Logger.Warning($"The file '{input}' could not be found in the DAT root"); } } @@ -69,7 +60,7 @@ namespace RombaSharp baddir = "bad", dats = "dats", db = "db", - connectionString = ""; + connectionString = string.Empty; Dictionary> depots = new Dictionary>(); // Get the XML text reader for the configuration file, if possible @@ -121,7 +112,7 @@ namespace RombaSharp XmlReader subreader = xtr.ReadSubtree(); if (subreader != null) { - string root = ""; + string root = string.Empty; long maxsize = -1; bool online = true; @@ -175,51 +166,39 @@ namespace RombaSharp // Now validate the values given if (workers < 1) - { workers = 1; - } if (workers > 8) - { workers = 8; - } + if (!Directory.Exists(logdir)) - { Directory.CreateDirectory(logdir); - } + if (!Directory.Exists(tmpdir)) - { Directory.CreateDirectory(tmpdir); - } + if (!Directory.Exists(webdir)) - { Directory.CreateDirectory(webdir); - } + if (!Directory.Exists(baddir)) - { Directory.CreateDirectory(baddir); - } + if (verbosity < 0) - { verbosity = 0; - } + if (verbosity > 3) - { verbosity = 3; - } + if (cores < 1) - { cores = 1; - } + if (cores > 16) - { cores = 16; - } + if (!Directory.Exists(dats)) - { Directory.CreateDirectory(dats); - } - db = Path.GetFileNameWithoutExtension(db) + ".sqlite"; - connectionString = "Data Source=" + db + ";Version = 3;"; + + db = $"{Path.GetFileNameWithoutExtension(db)}.sqlite"; + connectionString = $"Data Source={db};Version = 3;"; foreach (string key in depots.Keys) { if (!Directory.Exists(key)) @@ -231,23 +210,18 @@ namespace RombaSharp else { if (!File.Exists(Path.Combine(key, ".romba_size"))) - { File.CreateText(Path.Combine(key, ".romba_size")); - } + if (!File.Exists(Path.Combine(key, ".romba_size.backup"))) - { File.CreateText(Path.Combine(key, ".romba_size.backup")); - } } } + if (port < 0) - { port = 0; - } + if (port > 65535) - { port = 65535; - } // Finally set all of the fields Globals.MaxThreads = workers; @@ -272,10 +246,10 @@ namespace RombaSharp private static void AddDatToDatabase(Rom dat, SqliteConnection dbc) { // Get the dat full path - string fullpath = Path.Combine(_dats, (dat.MachineName == "dats" ? "" : dat.MachineName), dat.Name); + string fullpath = Path.Combine(_dats, (dat.MachineName == "dats" ? string.Empty : dat.MachineName), dat.Name); // Parse the Dat if possible - Globals.Logger.User("Adding from '" + dat.Name + "'"); + Globals.Logger.User($"Adding from '{dat.Name}'"); DatFile tempdat = new DatFile(); tempdat.Parse(fullpath, 0, 0); @@ -294,50 +268,42 @@ namespace RombaSharp { foreach (DatItem datItem in tempdat[romkey]) { - Globals.Logger.Verbose("Checking and adding file '{0}'", datItem.Name); + Globals.Logger.Verbose($"Checking and adding file '{datItem.Name}'"); if (datItem.ItemType == ItemType.Rom) { Rom rom = (Rom)datItem; - if (!String.IsNullOrWhiteSpace(rom.CRC)) - { - crcquery += " (\"" + rom.CRC + "\"),"; - } - if (!String.IsNullOrWhiteSpace(rom.MD5)) - { - md5query += " (\"" + rom.MD5 + "\"),"; - } - if (!String.IsNullOrWhiteSpace(rom.SHA1)) - { - sha1query += " (\"" + rom.SHA1 + "\"),"; + if (!string.IsNullOrWhiteSpace(rom.CRC)) + crcquery += $" (\"{rom.CRC}\"),"; + + if (!string.IsNullOrWhiteSpace(rom.MD5)) + md5query += $" (\"{rom.MD5}\"),"; - if (!String.IsNullOrWhiteSpace(rom.CRC)) - { - crcsha1query += " (\"" + rom.CRC + "\", \"" + rom.SHA1 + "\"),"; - } - if (!String.IsNullOrWhiteSpace(rom.MD5)) - { - md5sha1query += " (\"" + rom.MD5 + "\", \"" + rom.SHA1 + "\"),"; - } + if (!string.IsNullOrWhiteSpace(rom.SHA1)) + { + sha1query += $" (\"{rom.SHA1}\"),"; + + if (!string.IsNullOrWhiteSpace(rom.CRC)) + crcsha1query += $" (\"{rom.CRC}\", \"{rom.SHA1}\"),"; + + if (!string.IsNullOrWhiteSpace(rom.MD5)) + md5sha1query += $" (\"{rom.MD5}\", \"{rom.SHA1}\"),"; } } else if (datItem.ItemType == ItemType.Disk) { Disk disk = (Disk)datItem; - if (!String.IsNullOrWhiteSpace(disk.MD5)) - { - md5query += " (\"" + disk.MD5 + "\"),"; - } - if (!String.IsNullOrWhiteSpace(disk.SHA1)) - { - sha1query += " (\"" + disk.SHA1 + "\"),"; + if (!string.IsNullOrWhiteSpace(disk.MD5)) + md5query += $" (\"{disk.MD5}\"),"; - if (!String.IsNullOrWhiteSpace(disk.MD5)) - { - md5sha1query += " (\"" + disk.MD5 + "\", \"" + disk.SHA1 + "\"),"; - } + if (!string.IsNullOrWhiteSpace(disk.SHA1)) + { + sha1query += $" (\"{disk.SHA1}\"),"; + + if (!string.IsNullOrWhiteSpace(disk.MD5)) + md5sha1query += $" (\"{disk.MD5}\", \"{disk.SHA1}\"),"; } } } @@ -349,21 +315,25 @@ namespace RombaSharp slc = new SqliteCommand(crcquery.TrimEnd(','), dbc); slc.ExecuteNonQuery(); } + if (md5query != "INSERT OR IGNORE INTO md5 (md5) VALUES") { slc = new SqliteCommand(md5query.TrimEnd(','), dbc); slc.ExecuteNonQuery(); } + if (sha1query != "INSERT OR IGNORE INTO sha1 (sha1) VALUES") { slc = new SqliteCommand(sha1query.TrimEnd(','), dbc); slc.ExecuteNonQuery(); } + if (crcsha1query != "INSERT OR IGNORE INTO crcsha1 (crc, sha1) VALUES") { slc = new SqliteCommand(crcsha1query.TrimEnd(','), dbc); slc.ExecuteNonQuery(); } + if (md5sha1query != "INSERT OR IGNORE INTO md5sha1 (md5, sha1) VALUES") { slc = new SqliteCommand(md5sha1query.TrimEnd(','), dbc); @@ -371,7 +341,7 @@ namespace RombaSharp } } - string datquery = "INSERT OR IGNORE INTO dat (hash) VALUES (\"" + dat.SHA1 + "\")"; + string datquery = $"INSERT OR IGNORE INTO dat (hash) VALUES (\"{dat.SHA1}\")"; slc = new SqliteCommand(datquery, dbc); slc.ExecuteNonQuery(); slc.Dispose(); diff --git a/RombaSharp/RombaSharp.Inits.cs b/RombaSharp/RombaSharp.Inits.cs deleted file mode 100644 index 38c96fe9..00000000 --- a/RombaSharp/RombaSharp.Inits.cs +++ /dev/null @@ -1,1132 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Mono.Data.Sqlite; - -using SabreTools.Library.Data; -using SabreTools.Library.DatFiles; -using SabreTools.Library.DatItems; -using SabreTools.Library.Tools; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using SearchOption = System.IO.SearchOption; -using StreamReader = System.IO.StreamReader; -using StreamWriter = System.IO.StreamWriter; -#endif - -namespace RombaSharp -{ - public partial class RombaSharp - { - #region Init Methods - - /// - /// Wrap adding files to the depots - /// - /// List of input folders to use - /// True if only files in the database and don't exist are added, false otherwise - /// Resume a previously interrupted operation from the specified path - /// flag value == 0 means: add Zip files themselves into the depot in addition to their contents, flag value == 2 means add Zip files themselves but don't add content - /// How many workers to launch for the job, default from config - /// flag value == 0 means: add GZip files themselves into the depot in addition to their contents, flag value == 2 means add GZip files themselves but don't add content - /// flag value == 0 means: add 7Zip files themselves into the depot in addition to their contents, flag value == 2 means add 7Zip files themselves but don't add content - /// True to skip the initial scan of the files to determine amount of work, false otherwise - /// True to use go zip implementation instead of zlib, false otherwise - /// True to archive into depot but do not touch DB index and ignore only-needed flag, false otherwise - /// TODO: Add ability to update .romba files with proper size AND use the correct depot if it fills up - /// TODO: Add ability correctly to mark which depot the files are being rebuilt to in the DB - private static void InitArchive( - List inputs, - bool onlyNeeded, - string resume, - int includeZips, - int workers, - int includeGZips, - int include7Zips, - bool skipInitialScan, - bool useGolangZip, // Obsolete - bool noDb) - { - // First we want to get just all directories from the inputs - List onlyDirs = new List(); - foreach (string input in inputs) - { - if (Directory.Exists(input)) - { - onlyDirs.Add(Path.GetFullPath(input)); - } - } - - // Then process all of the input directories into an internal DAT - DatFile df = new DatFile(); - foreach (string dir in onlyDirs) - { - // TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually - df.PopulateFromDir(dir, Hash.DeepHashes, false, false, SkipFileType.None, false, false, _tmpdir, false, null, true, null); - df.PopulateFromDir(dir, Hash.DeepHashes, false, true, SkipFileType.None, false, false, _tmpdir, false, null, true, null); - } - - // Create an empty Dat for files that need to be rebuilt - DatFile need = new DatFile(); - - // Open the database connection - SqliteConnection dbc = new SqliteConnection(_connectionString); - dbc.Open(); - - // Now that we have the Dats, add the files to the database - string crcquery = "INSERT OR IGNORE INTO crc (crc) VALUES"; - string md5query = "INSERT OR IGNORE INTO md5 (md5) VALUES"; - string sha1query = "INSERT OR IGNORE INTO sha1 (sha1, depot) VALUES"; - string crcsha1query = "INSERT OR IGNORE INTO crcsha1 (crc, sha1) VALUES"; - string md5sha1query = "INSERT OR IGNORE INTO md5sha1 (md5, sha1) VALUES"; - - foreach (string key in df.Keys) - { - List datItems = df[key]; - foreach (Rom rom in datItems) - { - // If we care about if the file exists, check the databse first - if (onlyNeeded && !noDb) - { - string query = "SELECT * FROM crcsha1 JOIN md5sha1 ON crcsha1.sha1=md5sha1.sha1" - + " WHERE crcsha1.crc=\"" + rom.CRC + "\"" - + " OR md5sha1.md5=\"" + rom.MD5 + "\"" - + " OR md5sha1.sha1=\"" + rom.SHA1 + "\""; - SqliteCommand slc = new SqliteCommand(query, dbc); - SqliteDataReader sldr = slc.ExecuteReader(); - - if (sldr.HasRows) - { - // Add to the queries - if (!String.IsNullOrWhiteSpace(rom.CRC)) - { - crcquery += " (\"" + rom.CRC + "\"),"; - } - if (!String.IsNullOrWhiteSpace(rom.MD5)) - { - md5query += " (\"" + rom.MD5 + "\"),"; - } - if (!String.IsNullOrWhiteSpace(rom.SHA1)) - { - sha1query += " (\"" + rom.SHA1 + "\", \"" + _depots.Keys.ToList()[0] + "\"),"; - - if (!String.IsNullOrWhiteSpace(rom.CRC)) - { - crcsha1query += " (\"" + rom.CRC + "\", \"" + rom.SHA1 + "\"),"; - } - if (!String.IsNullOrWhiteSpace(rom.MD5)) - { - md5sha1query += " (\"" + rom.MD5 + "\", \"" + rom.SHA1 + "\"),"; - } - } - - // Add to the Dat - need.Add(key, rom); - } - } - // Otherwise, just add the file to the list - else - { - // Add to the queries - if (!noDb) - { - if (!String.IsNullOrWhiteSpace(rom.CRC)) - { - crcquery += " (\"" + rom.CRC + "\"),"; - } - if (!String.IsNullOrWhiteSpace(rom.MD5)) - { - md5query += " (\"" + rom.MD5 + "\"),"; - } - if (!String.IsNullOrWhiteSpace(rom.SHA1)) - { - sha1query += " (\"" + rom.SHA1 + "\", \"" + _depots.Keys.ToList()[0] + "\"),"; - - if (!String.IsNullOrWhiteSpace(rom.CRC)) - { - crcsha1query += " (\"" + rom.CRC + "\", \"" + rom.SHA1 + "\"),"; - } - if (!String.IsNullOrWhiteSpace(rom.MD5)) - { - md5sha1query += " (\"" + rom.MD5 + "\", \"" + rom.SHA1 + "\"),"; - } - } - } - - // Add to the Dat - need.Add(key, rom); - } - } - } - - // Now run the queries, if they're populated - if (crcquery != "INSERT OR IGNORE INTO crc (crc) VALUES") - { - SqliteCommand slc = new SqliteCommand(crcquery.TrimEnd(','), dbc); - slc.ExecuteNonQuery(); - slc.Dispose(); - } - if (md5query != "INSERT OR IGNORE INTO md5 (md5) VALUES") - { - SqliteCommand slc = new SqliteCommand(md5query.TrimEnd(','), dbc); - slc.ExecuteNonQuery(); - slc.Dispose(); - } - if (sha1query != "INSERT OR IGNORE INTO sha1 (sha1, depot) VALUES") - { - SqliteCommand slc = new SqliteCommand(sha1query.TrimEnd(','), dbc); - slc.ExecuteNonQuery(); - slc.Dispose(); - } - if (crcsha1query != "INSERT OR IGNORE INTO crcsha1 (crc, sha1) VALUES") - { - SqliteCommand slc = new SqliteCommand(crcsha1query.TrimEnd(','), dbc); - slc.ExecuteNonQuery(); - slc.Dispose(); - } - if (md5sha1query != "INSERT OR IGNORE INTO md5sha1 (md5, sha1) VALUES") - { - SqliteCommand slc = new SqliteCommand(md5sha1query.TrimEnd(','), dbc); - slc.ExecuteNonQuery(); - slc.Dispose(); - } - - // Create the sorting object to use and rebuild the needed files - ArchiveScanLevel asl = Utilities.GetArchiveScanLevelFromNumbers(include7Zips, includeGZips, 2, includeZips); - need.RebuildGeneric(onlyDirs, _depots.Keys.ToList()[0], false /*quickScan*/, false /*date*/, - false /*delete*/, false /*inverse*/, OutputFormat.TorrentGzip, true /*romba*/, asl, false /*updateDat*/, - null /*headerToCheckAgainst*/, true /* chdsAsFiles */); - } - - /// - /// Wrap building all files from a set of DATs - /// - /// List of input DATs to rebuild from - /// Output file - /// True to only fix dats and don't generate torrentzips, false otherwise - /// True if files should be copied to output, false for rebuild - /// How many workers to launch for the job, default from config - /// How many subworkers to launch for each worker, default from config - private static void InitBuild( - List inputs, - string outdat, - bool fixdatOnly, - bool copy, - int workers, - int subworkers) - { - // Verify the filenames - Dictionary foundDats = GetValidDats(inputs); - - // Ensure the output directory is set - if (String.IsNullOrWhiteSpace(outdat)) - { - outdat = "out"; - } - - // Now that we have the dictionary, we can loop through and output to a new folder for each - foreach (string key in foundDats.Keys) - { - // Get the DAT file associated with the key - DatFile datFile = new DatFile(); - datFile.Parse(Path.Combine(_dats, foundDats[key]), 0, 0); - - // Create the new output directory if it doesn't exist - string outputFolder = Path.Combine(outdat, Path.GetFileNameWithoutExtension(foundDats[key])); - Utilities.EnsureOutputDirectory(outputFolder, create: true); - - // Get all online depots - List onlineDepots = _depots.Where(d => d.Value.Item2).Select(d => d.Key).ToList(); - - // Now scan all of those depots and rebuild - ArchiveScanLevel asl = Utilities.GetArchiveScanLevelFromNumbers(1, 1, 1, 1); - datFile.RebuildDepot(onlineDepots, outputFolder, false /*date*/, - false /*delete*/, false /*inverse*/, (copy ? OutputFormat.TorrentGzip : OutputFormat.TorrentZip), copy, - false /*updateDat*/, null /*headerToCheckAgainst*/); - } - } - - /// - /// Wrap cancelling a long-running job - /// - /// TODO: Implement - private static void InitCancel() - { - Globals.Logger.User("This feature is not yet implemented: cancel"); - } - - /// - /// Wrap printing dat stats - /// - /// List of input DATs to get stats from - private static void InitDatStats(List inputs) - { - // If we have no inputs listed, we want to use datroot - if (inputs == null || inputs.Count == 0) - { - inputs = new List(); - inputs.Add(Path.GetFullPath(_dats)); - } - - // Now output the stats for all inputs - DatFile.OutputStats(inputs, "rombasharp-datstats", null /* outDir */, true /* single */, true /* baddumpCol */, true /* nodumpCol */, StatReportFormat.Textfile); - } - - /// - /// Wrap printing db stats - /// - private static void InitDbStats() - { - SqliteConnection dbc = new SqliteConnection(_connectionString); - dbc.Open(); - - // Total number of CRCs - string query = "SELECT COUNT(*) FROM crc"; - SqliteCommand slc = new SqliteCommand(query, dbc); - Globals.Logger.User("Total CRCs: {0}", (long)slc.ExecuteScalar()); - - // Total number of MD5s - query = "SELECT COUNT(*) FROM md5"; - slc = new SqliteCommand(query, dbc); - Globals.Logger.User("Total MD5s: {0}", (long)slc.ExecuteScalar()); - - // Total number of SHA1s - query = "SELECT COUNT(*) FROM sha1"; - slc = new SqliteCommand(query, dbc); - Globals.Logger.User("Total SHA1s: {0}", (long)slc.ExecuteScalar()); - - // Total number of DATs - query = "SELECT COUNT(*) FROM dat"; - slc = new SqliteCommand(query, dbc); - Globals.Logger.User("Total DATs: {0}", (long)slc.ExecuteScalar()); - - slc.Dispose(); - dbc.Dispose(); - } - - /// - /// Wrap creating a diffdat for a given old and new dat - /// - /// Output file - /// Old DAT file - /// New DAT file - /// Name value in DAT header - /// Description value in DAT header - private static void InitDiffDat( - string outdat, - string old, - string newdat, - string name, - string description) - { - // Ensure the output directory - Utilities.EnsureOutputDirectory(outdat, create: true); - - // Check that all required files exist - if (!File.Exists(old)) - { - Globals.Logger.Error("File '{0}' does not exist!", old); - return; - } - if (!File.Exists(newdat)) - { - Globals.Logger.Error("File '{0}' does not exist!", newdat); - return; - } - - // Create the encapsulating datfile - DatFile datfile = new DatFile() - { - Name = name, - Description = description, - }; - - // Create the inputs - List dats = new List(); - dats.Add(newdat); - List basedats = new List(); - basedats.Add(old); - - // Now run the diff on the inputs - datfile.DetermineUpdateType(dats, basedats, outdat, UpdateMode.DiffAgainst, false /* inplace */, false /* skip */, - false /* clean */, false /* remUnicode */, false /* descAsName */, new Filter(), SplitType.None, - new List(), false /* onlySame */); - } - - /// - /// Wrap creating a dir2dat from a given source - /// - /// Output file - /// Source directory - /// Name value in DAT header - /// Description value in DAT header - private static void InitDir2Dat( - string outdat, - string source, - string name, - string description) - { - // Ensure the output directory - Utilities.EnsureOutputDirectory(outdat, create: true); - - // Check that all required directories exist - if (!Directory.Exists(source)) - { - Globals.Logger.Error("File '{0}' does not exist!", source); - return; - } - - // Create the encapsulating datfile - DatFile datfile = new DatFile() - { - Name = (String.IsNullOrWhiteSpace(name) ? "untitled" : name), - Description = description, - }; - - // Now run the D2D on the input and write out - // TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually - datfile.PopulateFromDir(source, Hash.DeepHashes, true /* bare */, false /* archivesAsFiles */, SkipFileType.None, false /* addBlanks */, - false /* addDate */, _tmpdir, false /* copyFiles */, null /* headerToCheckAgainst */, true /* chdsAsFiles */, null /* filter */); - datfile.Write(outDir: outdat); - } - - /// - /// Wrap creating a diffdat for a given old and new dat - /// - /// Output file - /// Old DAT file - /// New DAT file - private static void InitEDiffDat( - string outdat, - string old, - string newdat) - { - // Ensure the output directory - Utilities.EnsureOutputDirectory(outdat, create: true); - - // Check that all required files exist - if (!File.Exists(old)) - { - Globals.Logger.Error("File '{0}' does not exist!", old); - return; - } - if (!File.Exists(newdat)) - { - Globals.Logger.Error("File '{0}' does not exist!", newdat); - return; - } - - // Create the encapsulating datfile - DatFile datfile = new DatFile(); - - // Create the inputs - List dats = new List(); - dats.Add(newdat); - List basedats = new List(); - basedats.Add(old); - - // Now run the diff on the inputs - datfile.DetermineUpdateType(dats, basedats, outdat, UpdateMode.DiffAgainst, false /* inplace */, false /* skip */, - false /* clean */, false /* remUnicode */, false /* descAsName */, new Filter(), SplitType.None, - new List(), false /* onlySame */); - } - - /// - /// Wrap exporting the database to CSV - /// - /// TODO: Add ability to say which depot the files are found in - private static void InitExport() - { - SqliteConnection dbc = new SqliteConnection(_connectionString); - dbc.Open(); - StreamWriter sw = new StreamWriter(Utilities.TryCreate("export.csv")); - - // First take care of all file hashes - sw.WriteLine("CRC,MD5,SHA-1"); // ,Depot - - string query = "SELECT crcsha1.crc, md5sha1.md5, md5sha1.sha1 FROM crcsha1 JOIN md5sha1 ON crcsha1.sha1=md5sha1.sha1"; // md5sha1.sha1=sha1depot.sha1 - SqliteCommand slc = new SqliteCommand(query, dbc); - SqliteDataReader sldr = slc.ExecuteReader(); - - if (sldr.HasRows) - { - while (sldr.Read()) - { - string line = sldr.GetString(0) + "," - + sldr.GetString(1) + "," - + sldr.GetString(2); // + "," - // + sldr.GetString(3); - sw.WriteLine(line); - } - } - - // Then take care of all DAT hashes - sw.WriteLine(); - sw.WriteLine("DAT Hash"); - - query = "SELECT hash FROM dat"; - slc = new SqliteCommand(query, dbc); - sldr = slc.ExecuteReader(); - - if (sldr.HasRows) - { - while (sldr.Read()) - { - sw.WriteLine(sldr.GetString(0)); - } - } - - sldr.Dispose(); - slc.Dispose(); - sw.Dispose(); - dbc.Dispose(); - } - - /// - /// Wrap creating a fixdat for each Dat - /// - /// List of input DATs to get fixdats for - /// Output directory - /// True to only fix dats and don't generate torrentzips, false otherwise - /// How many workers to launch for the job, default from config - /// How many subworkers to launch for each worker, default from config - /// TODO: Implement - private static void InitFixdat( - List inputs, - string outdat, - bool fixdatOnly, - int workers, - int subworkers) - { - Globals.Logger.Error("This feature is not yet implemented: fixdat"); - } - - /// - /// Wrap importing CSVs into the database - /// - /// List of input CSV files to import information from - private static void InitImport(List inputs) - { - Globals.Logger.Error("This feature is not yet implemented: import"); - - // First ensure the inputs and database connection - inputs = Utilities.GetOnlyFilesFromInputs(inputs); - SqliteConnection dbc = new SqliteConnection(_connectionString); - SqliteCommand slc = new SqliteCommand(); - dbc.Open(); - - // Now, for each of these files, attempt to add the data found inside - foreach (string input in inputs) - { - StreamReader sr = new StreamReader(Utilities.TryOpenRead(input)); - - // The first line should be the hash header - string line = sr.ReadLine(); - if (line != "CRC,MD5,SHA-1") // ,Depot - { - Globals.Logger.Error("{0} is not a valid export file"); - continue; - } - - // Define the insert queries - string crcquery = "INSERT OR IGNORE INTO crc (crc) VALUES"; - string md5query = "INSERT OR IGNORE INTO md5 (md5) VALUES"; - string sha1query = "INSERT OR IGNORE INTO sha1 (sha1) VALUES"; - string crcsha1query = "INSERT OR IGNORE INTO crcsha1 (crc, sha1) VALUES"; - string md5sha1query = "INSERT OR IGNORE INTO md5sha1 (md5, sha1) VALUES"; - - // For each line until we hit a blank line... - while (!sr.EndOfStream && line != "") - { - line = sr.ReadLine(); - string[] hashes = line.Split(','); - - // Loop through the parsed entries - if (!String.IsNullOrWhiteSpace(hashes[0])) - { - crcquery += " (\"" + hashes[0] + "\"),"; - } - if (!String.IsNullOrWhiteSpace(hashes[1])) - { - md5query += " (\"" + hashes[1] + "\"),"; - } - if (!String.IsNullOrWhiteSpace(hashes[2])) - { - sha1query += " (\"" + hashes[2] + "\"),"; - - if (!String.IsNullOrWhiteSpace(hashes[0])) - { - crcsha1query += " (\"" + hashes[0] + "\", \"" + hashes[2] + "\"),"; - } - if (!String.IsNullOrWhiteSpace(hashes[1])) - { - md5sha1query += " (\"" + hashes[1] + "\", \"" + hashes[2] + "\"),"; - } - } - } - - // Now run the queries after fixing them - if (crcquery != "INSERT OR IGNORE INTO crc (crc) VALUES") - { - slc = new SqliteCommand(crcquery.TrimEnd(','), dbc); - slc.ExecuteNonQuery(); - } - if (md5query != "INSERT OR IGNORE INTO md5 (md5) VALUES") - { - slc = new SqliteCommand(md5query.TrimEnd(','), dbc); - slc.ExecuteNonQuery(); - } - if (sha1query != "INSERT OR IGNORE INTO sha1 (sha1) VALUES") - { - slc = new SqliteCommand(sha1query.TrimEnd(','), dbc); - slc.ExecuteNonQuery(); - } - if (crcsha1query != "INSERT OR IGNORE INTO crcsha1 (crc, sha1) VALUES") - { - slc = new SqliteCommand(crcsha1query.TrimEnd(','), dbc); - slc.ExecuteNonQuery(); - } - if (md5sha1query != "INSERT OR IGNORE INTO md5sha1 (md5, sha1) VALUES") - { - slc = new SqliteCommand(md5sha1query.TrimEnd(','), dbc); - slc.ExecuteNonQuery(); - } - - // Now add all of the DAT hashes - // TODO: Do we really need to save the DAT hashes? - - sr.Dispose(); - } - - slc.Dispose(); - dbc.Dispose(); - } - - /// - /// Wrap looking up if hashes exist in the database - /// - /// List of input strings representing hashes to check for - /// Size to limit hash by, -1 otherwise - /// Output directory - private static void InitLookup( - List inputs, - long size, - string outdat) - { - // First, try to figure out what type of hash each is by length and clean it - List crc = new List(); - List md5 = new List(); - List sha1 = new List(); - foreach (string input in inputs) - { - string temp = ""; - if (input.Length == Constants.CRCLength) - { - temp = Utilities.CleanHashData(input, Constants.CRCLength); - if (!String.IsNullOrWhiteSpace(temp)) - { - crc.Add(temp); - } - } - else if (input.Length == Constants.MD5Length) - { - temp = Utilities.CleanHashData(input, Constants.MD5Length); - if (!String.IsNullOrWhiteSpace(temp)) - { - md5.Add(temp); - } - } - else if (input.Length == Constants.SHA1Length) - { - temp = Utilities.CleanHashData(input, Constants.SHA1Length); - if (!String.IsNullOrWhiteSpace(temp)) - { - sha1.Add(temp); - } - } - } - - SqliteConnection dbc = new SqliteConnection(_connectionString); - dbc.Open(); - - // Now, search for each of them and return true or false for each - foreach (string input in crc) - { - string query = "SELECT * FROM crc WHERE crc=\"" + input + "\""; - SqliteCommand slc = new SqliteCommand(query, dbc); - SqliteDataReader sldr = slc.ExecuteReader(); - if (sldr.HasRows) - { - int count = 0; - while (sldr.Read()) - { - count++; - } - - Globals.Logger.User("For hash '{0}' there were {1} matches in the database", input, count); - } - else - { - Globals.Logger.User("Hash '{0}' had no matches in the database", input); - } - - sldr.Dispose(); - slc.Dispose(); - } - foreach (string input in md5) - { - string query = "SELECT * FROM md5 WHERE md5=\"" + input + "\""; - SqliteCommand slc = new SqliteCommand(query, dbc); - SqliteDataReader sldr = slc.ExecuteReader(); - if (sldr.HasRows) - { - int count = 0; - while (sldr.Read()) - { - count++; - } - - Globals.Logger.User("For hash '{0}' there were {1} matches in the database", input, count); - } - else - { - Globals.Logger.User("Hash '{0}' had no matches in the database", input); - } - - sldr.Dispose(); - slc.Dispose(); - } - foreach (string input in sha1) - { - string query = "SELECT * FROM sha1 WHERE sha1=\"" + input + "\""; - SqliteCommand slc = new SqliteCommand(query, dbc); - SqliteDataReader sldr = slc.ExecuteReader(); - if (sldr.HasRows) - { - int count = 0; - while (sldr.Read()) - { - count++; - } - - Globals.Logger.User("For hash '{0}' there were {1} matches in the database", input, count); - } - else - { - Globals.Logger.User("Hash '{0}' had no matches in the database", input); - } - - sldr.Dispose(); - slc.Dispose(); - } - - dbc.Dispose(); - } - - /// - /// Wrap printing memory stats - /// - /// TODO: Implement - private static void InitMemstats() - { - Globals.Logger.User("This feature is not yet implemented: memstats"); - } - - /// - /// Wrap merging an external depot into an existing one - /// - /// List of input depots to merge in - /// True if only files in the database and don't exist are added, false otherwise - /// Resume a previously interrupted operation from the specified path - /// How many workers to launch for the job, default from config - /// True to skip the initial scan of the files to determine amount of work, false otherwise - /// TODO: Add way of specifying "current depot" since that's what Romba relies on - /// TODO: Implement - private static void InitMerge( - List inputs, - bool onlyNeeded, - string resume, - int workers, - bool skipInitialscan) - { - Globals.Logger.Error("This feature is not yet implemented: merge"); - - // Verify that the inputs are valid directories - inputs = Utilities.GetOnlyDirectoriesFromInputs(inputs); - - // Loop over all input directories - foreach (string input in inputs) - { - List depotFiles = Directory.EnumerateFiles(input, "*.gz", SearchOption.AllDirectories).ToList(); - - // If we are copying all that is possible but we want to scan first - if (!onlyNeeded && !skipInitialscan) - { - - } - // If we are copying all that is possible but we don't care to scan first - else if (!onlyNeeded && skipInitialscan) - { - - } - // If we are copying only what is needed but we want to scan first - else if (onlyNeeded && !skipInitialscan) - { - - } - // If we are copying only what is needed but we don't care to scan first - else if (onlyNeeded && skipInitialscan) - { - - } - } - } - - /// - /// Wrap creating a havefile and a missfile for each Dat - /// - /// List of DAT files to get a miss and have for, empty means all - /// TODO: Implement - private static void InitMiss(List inputs) - { - // Verify the filenames - Dictionary foundDats = GetValidDats(inputs); - - // Create the new output directory if it doesn't exist - Utilities.EnsureOutputDirectory(Path.Combine(Globals.ExeDir, "out"), 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) - { - // Get the DAT file associated with the key - DatFile datFile = new DatFile(); - datFile.Parse(Path.Combine(_dats, foundDats[key]), 0, 0); - - // Now loop through and see if all of the hash combinations exist in the database - /* ended here */ - } - - Globals.Logger.Error("This feature is not yet implemented: miss"); - } - - /// - /// Wrap showing progress of currently running command - /// - /// TODO: Implement - private static void InitProgress() - { - Globals.Logger.User("This feature is not yet implemented: progress"); - } - - /// - /// Wrap backing up of no longer needed files from the depots - /// - /// Backup directory where backup files are moved to - /// How many workers to launch for the job, default from config - /// List of depots to scan files in, empty means all - /// List of DATs to use as the basis of scanning, empty means all - /// True if only the output of the operation is shown, false to actually run - /// TODO: Implement - private static void InitPurgeBackup( - string backup, - int workers, - List depot, - List dats, - bool logOnly) - { - Globals.Logger.Error("This feature is not yet implemented: purge-backup"); - } - - /// - /// Wrap deleting of no longer needed files from the depots - /// - /// How many workers to launch for the job, default from config - /// List of depots to scan files in, empty means all - /// List of DATs to use as the basis of scanning, empty means all - /// True if only the output of the operation is shown, false to actually run - /// TODO: Implement - private static void InitPurgeDelete( - int workers, - List depot, - List dats, - bool logOnly) - { - Globals.Logger.Error("This feature is not yet implemented: purge-delete"); - } - - /// - /// Wrap refreshing the database with potentially new dats - /// - /// How many workers to launch for the job, default from config - /// Write paths of dats with missing sha1s into this file - private static void InitRefreshDats( - int workers, - string missingSha1s) - { - // Make sure the db is set - if (String.IsNullOrWhiteSpace(_db)) - { - _db = "db.sqlite"; - _connectionString = "Data Source=" + _db + ";Version = 3;"; - } - - // Make sure the file exists - if (!File.Exists(_db)) - { - DatabaseTools.EnsureDatabase(_dbSchema, _db, _connectionString); - } - - // Make sure the dats dir is set - if (String.IsNullOrWhiteSpace(_dats)) - { - _dats = "dats"; - } - - _dats = Path.Combine(Globals.ExeDir, _dats); - - // Make sure the folder exists - if (!Directory.Exists(_dats)) - { - Directory.CreateDirectory(_dats); - } - - // First get a list of SHA-1's from the input DATs - DatFile datroot = new DatFile { Type = "SuperDAT", }; - // TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually - datroot.PopulateFromDir(_dats, Hash.DeepHashes, false, false, SkipFileType.None, false, false, _tmpdir, false, null, true, null); - datroot.BucketBy(SortedBy.SHA1, DedupeType.None); - - // Create a List of dat hashes in the database (SHA-1) - List databaseDats = new List(); - List unneeded = new List(); - - SqliteConnection dbc = new SqliteConnection(_connectionString); - dbc.Open(); - - // Populate the List from the database - InternalStopwatch watch = new InternalStopwatch("Populating the list of existing DATs"); - - string query = "SELECT DISTINCT hash FROM dat"; - SqliteCommand slc = new SqliteCommand(query, dbc); - SqliteDataReader sldr = slc.ExecuteReader(); - if (sldr.HasRows) - { - sldr.Read(); - string hash = sldr.GetString(0); - if (datroot.Contains(hash)) - { - datroot.Remove(hash); - databaseDats.Add(hash); - } - else if (!databaseDats.Contains(hash)) - { - unneeded.Add(hash); - } - } - datroot.BucketBy(SortedBy.Game, DedupeType.None, norename: true); - - watch.Stop(); - - slc.Dispose(); - sldr.Dispose(); - - // Loop through the Dictionary and add all data - watch.Start("Adding new DAT information"); - foreach (string key in datroot.Keys) - { - foreach (Rom value in datroot[key]) - { - AddDatToDatabase(value, dbc); - } - } - - watch.Stop(); - - // Now loop through and remove all references to old Dats - if (unneeded.Count > 0) - { - watch.Start("Removing unmatched DAT information"); - - query = "DELETE FROM dat WHERE"; - foreach (string dathash in unneeded) - { - query += " OR hash=\"" + dathash + "\""; - } - - query = query.Replace("WHERE OR", "WHERE"); - slc = new SqliteCommand(query, dbc); - slc.ExecuteNonQuery(); - slc.Dispose(); - - watch.Stop(); - } - - dbc.Dispose(); - } - - /// - /// Wrap rescanning depots - /// - /// List of depots to rescan, empty means all - /// TODO: Verify implementation - private static void InitRescanDepots(List inputs) - { - Globals.Logger.Error("This feature is not yet implemented: rescan-depots"); - - foreach (string depotname in inputs) - { - // Check that it's a valid depot first - if (!_depots.ContainsKey(depotname)) - { - Globals.Logger.User("'{0}' is not a recognized depot. Please add it to your configuration file and try again", depotname); - return; - } - - // Then check that the depot is online - if (!Directory.Exists(depotname)) - { - Globals.Logger.User("'{0}' does not appear to be online. Please check its status and try again", depotname); - return; - } - - // Open the database connection - SqliteConnection dbc = new SqliteConnection(_connectionString); - dbc.Open(); - - // If we have it, then check for all hashes that are in that depot - List hashes = new List(); - string query = "SELECT sha1 FROM sha1 WHERE depot=\"" + depotname + "\""; - SqliteCommand slc = new SqliteCommand(query, dbc); - SqliteDataReader sldr = slc.ExecuteReader(); - if (sldr.HasRows) - { - while (sldr.Read()) - { - hashes.Add(sldr.GetString(0)); - } - } - - // Now rescan the depot itself - DatFile depot = new DatFile(); - // TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually - depot.PopulateFromDir(depotname, Hash.DeepHashes, false, false, SkipFileType.None, false, false, _tmpdir, false, null, true, null); - depot.BucketBy(SortedBy.SHA1, DedupeType.None); - - // Set the base queries to use - string crcquery = "INSERT OR IGNORE INTO crc (crc) VALUES"; - string md5query = "INSERT OR IGNORE INTO md5 (md5) VALUES"; - string sha1query = "INSERT OR IGNORE INTO sha1 (sha1, depot) VALUES"; - string crcsha1query = "INSERT OR IGNORE INTO crcsha1 (crc, sha1) VALUES"; - string md5sha1query = "INSERT OR IGNORE INTO md5sha1 (md5, sha1) VALUES"; - - // Once we have both, check for any new files - List dupehashes = new List(); - List keys = depot.Keys; - foreach (string key in keys) - { - List roms = depot[key]; - foreach (Rom rom in roms) - { - if (hashes.Contains(rom.SHA1)) - { - dupehashes.Add(rom.SHA1); - hashes.Remove(rom.SHA1); - } - else if (!dupehashes.Contains(rom.SHA1)) - { - if (!String.IsNullOrWhiteSpace(rom.CRC)) - { - crcquery += " (\"" + rom.CRC + "\"),"; - } - if (!String.IsNullOrWhiteSpace(rom.MD5)) - { - md5query += " (\"" + rom.MD5 + "\"),"; - } - if (!String.IsNullOrWhiteSpace(rom.SHA1)) - { - sha1query += " (\"" + rom.SHA1 + "\", \"" + depotname + "\"),"; - - if (!String.IsNullOrWhiteSpace(rom.CRC)) - { - crcsha1query += " (\"" + rom.CRC + "\", \"" + rom.SHA1 + "\"),"; - } - if (!String.IsNullOrWhiteSpace(rom.MD5)) - { - md5sha1query += " (\"" + rom.MD5 + "\", \"" + rom.SHA1 + "\"),"; - } - } - } - } - } - - // Now run the queries after fixing them - if (crcquery != "INSERT OR IGNORE INTO crc (crc) VALUES") - { - slc = new SqliteCommand(crcquery.TrimEnd(','), dbc); - slc.ExecuteNonQuery(); - } - if (md5query != "INSERT OR IGNORE INTO md5 (md5) VALUES") - { - slc = new SqliteCommand(md5query.TrimEnd(','), dbc); - slc.ExecuteNonQuery(); - } - if (sha1query != "INSERT OR IGNORE INTO sha1 (sha1, depot) VALUES") - { - slc = new SqliteCommand(sha1query.TrimEnd(','), dbc); - slc.ExecuteNonQuery(); - } - if (crcsha1query != "INSERT OR IGNORE INTO crcsha1 (crc, sha1) VALUES") - { - slc = new SqliteCommand(crcsha1query.TrimEnd(','), dbc); - slc.ExecuteNonQuery(); - } - if (md5sha1query != "INSERT OR IGNORE INTO md5sha1 (md5, sha1) VALUES") - { - slc = new SqliteCommand(md5sha1query.TrimEnd(','), dbc); - slc.ExecuteNonQuery(); - } - - // Now that we've added the information, we get to remove all of the hashes that we want to - query = @"DELETE FROM sha1 -JOIN crcsha1 - ON sha1.sha1=crcsha1.sha1 -JOIN md5sha1 - ON sha1.sha1=md5sha1.sha1 -JOIN crc - ON crcsha1.crc=crc.crc -JOIN md5 - ON md5sha1.md5=md5.md5 -WHERE sha1.sha1 IN (""" + String.Join("\",\"", hashes) + "\")"; - slc = new SqliteCommand(query, dbc); - slc.ExecuteNonQuery(); - - // Dispose of the database connection - slc.Dispose(); - dbc.Dispose(); - } - } - - /// - /// Wrap gracefully shutting down the server - /// - /// TODO: Implement - private static void InitShutdown() - { - Globals.Logger.User("This feature is not yet implemented: shutdown"); - } - - /// - /// Wrap printing the version - /// - private static void InitVersion() - { - Globals.Logger.User("RombaSharp version: {0}", Constants.Version); - } - - #endregion - } -} diff --git a/RombaSharp/RombaSharp.cs b/RombaSharp/RombaSharp.cs index 56daafb3..19485926 100644 --- a/RombaSharp/RombaSharp.cs +++ b/RombaSharp/RombaSharp.cs @@ -1,17 +1,10 @@ using System; using System.Collections.Generic; -using System.Linq; using SabreTools.Library.Data; using SabreTools.Library.Help; using SabreTools.Library.Tools; -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; -#endif - namespace RombaSharp { /// @@ -43,8 +36,8 @@ namespace RombaSharp private static int _port; // Web server port // Other private variables - private static string _config = "config.xml"; - private static string _dbSchema = "rombasharp"; + private const string _config = "config.xml"; + private const string _dbSchema = "rombasharp"; private static string _connectionString; private static Help _help; @@ -96,289 +89,80 @@ namespace RombaSharp return; } - // User flags - bool copy = false, - fixdatOnly = false, - logOnly = false, - noDb = false, - onlyNeeded = false, - skipInitialScan = false, - useGolangZip = false; - - // User inputs - string backup = "", - description = "", - missingSha1s = "", - name = "", - newdat = "", - old = "", - outdat = "", - resume = "", - source = ""; - int include7Zips = 1, - includeGZips = 1, - includeZips = 1, - subworkers = 0, - workers = 0; - long size = -1; - List dats = new List(); - List depot = new List(); - List inputs = new List(); - // Get the first argument as a feature flag - string feature = args[0]; + string featureName = args[0]; // Verify that the flag is valid - if (!_help.TopLevelFlag(feature)) + if (!_help.TopLevelFlag(featureName)) { - Globals.Logger.User("'{0}' is not valid feature flag", feature); - _help.OutputIndividualFeature(feature); + Globals.Logger.User($"'{featureName}' is not valid feature flag"); + _help.OutputIndividualFeature(featureName); Globals.Logger.Close(); return; } - // Now get the proper name for the feature - feature = _help.GetFeatureName(feature); + // Get the proper name for the feature + featureName = _help.GetFeatureName(featureName); + + // Get the associated feature + RombaSharpFeature feature = _help[featureName] as RombaSharpFeature; // If we had the help feature first - if (feature == "Help") + if (featureName == HelpFeature.Value || featureName == DetailedHelpFeature.Value) { - // If we had something else after help - if (args.Length > 1) - { - _help.OutputIndividualFeature(args[1]); - Globals.Logger.Close(); - return; - } - // Otherwise, show generic help - else - { - _help.OutputGenericHelp(); - Globals.Logger.Close(); - return; - } + feature.ProcessArgs(args, _help); + Globals.Logger.Close(); + return; } // Now verify that all other flags are valid - for (int i = 1; i < args.Length; i++) + if (!feature.ProcessArgs(args, _help)) { - // Verify that the current flag is proper for the feature - if (!_help[feature].ValidateInput(args[i])) - { - // Everything else is treated as a generic input - inputs.Add(args[i]); - } + Globals.Logger.Close(); + return; } - // Now loop through all inputs + // Now process the current feature Dictionary features = _help.GetEnabledFeatures(); - foreach (KeyValuePair feat in features) + switch (featureName) { - // Check all of the flag names and translate to arguments - switch (feat.Key) - { - #region User Flags - - case "copy": - copy = true; - break; - case "fixdatOnly": - fixdatOnly = true; - break; - case "log-only": - logOnly = true; - break; - case "no-db": - noDb = true; - break; - case "only-needed": - onlyNeeded = true; - break; - case "skip-initial-scan": - skipInitialScan = true; - break; - case "use-golang-zip": - useGolangZip = true; - break; - - #endregion - - #region User Int32 Inputs - - case "include-7zips": - include7Zips = (int)feat.Value.GetValue() == Int32.MinValue ? (int)feat.Value.GetValue() : 0; - break; - case "include-gzips": - includeGZips = (int)feat.Value.GetValue() == Int32.MinValue ? (int)feat.Value.GetValue() : 0; - break; - case "include-zips": - includeZips = (int)feat.Value.GetValue() == Int32.MinValue ? (int)feat.Value.GetValue() : 0; - break; - case "subworkers": - subworkers = (int)feat.Value.GetValue() == Int32.MinValue ? (int)feat.Value.GetValue() : _cores; - break; - case "workers": - workers = (int)feat.Value.GetValue() == Int32.MinValue ? (int)feat.Value.GetValue() : _cores; - break; - - #endregion - - #region User Int64 Inputs - - case "size": - size = (long)feat.Value.GetValue() == Int64.MinValue ? (long)feat.Value.GetValue() : 0; - break; - - #endregion - - #region User List Inputs - - case "dats": - dats.AddRange((List)feat.Value.GetValue()); - break; - case "depot": - depot.AddRange((List)feat.Value.GetValue()); - break; - - #endregion - - #region User String Inputs - - case "backup": - backup = (string)feat.Value.GetValue(); - break; - case "description": - description = (string)feat.Value.GetValue(); - break; - case "missingSha1s": - missingSha1s = (string)feat.Value.GetValue(); - break; - case "name": - name = (string)feat.Value.GetValue(); - break; - case "new": - newdat = (string)feat.Value.GetValue(); - break; - case "old": - old = (string)feat.Value.GetValue(); - break; - case "out": - outdat = (string)feat.Value.GetValue(); - break; - case "resume": - resume = (string)feat.Value.GetValue(); - break; - case "source": - source = (string)feat.Value.GetValue(); - break; - - #endregion - } - } - - // Now take care of each mode in succesion - switch(feature) - { - case "Help": + case DetailedHelpFeature.Value: + case HelpFeature.Value: + case ScriptFeature.Value: // No-op as this should be caught break; - // Adds ROM files from the specified directories to the ROM archive - case "Archive": - VerifyInputs(inputs, feature); - InitArchive(inputs, onlyNeeded, resume, includeZips, workers, includeGZips, include7Zips, skipInitialScan, useGolangZip, noDb); + + // Require input verification + case ArchiveFeature.Value: + case BuildFeature.Value: + case DatStatsFeature.Value: + case FixdatFeature.Value: + case ImportFeature.Value: + case LookupFeature.Value: + case MergeFeature.Value: + case MissFeature.Value: + case RescanDepotsFeature.Value: + VerifyInputs(feature.Inputs, featureName); + feature.ProcessFeatures(features); break; - // For each specified DAT file it creates the torrentzip files - case "Build": - VerifyInputs(inputs, feature); - InitBuild(inputs, outdat, fixdatOnly, copy, workers, subworkers); - break; - // Cancels current long-running job - case "Cancel": - InitCancel(); - break; - // Prints dat stats - case "DatStats": - VerifyInputs(inputs, feature); - InitDatStats(inputs); - break; - // Prints db stats - case "DbStats": - InitDbStats(); - break; - // Creates a DAT file with those entries that are in -new DAT - case "Diffdat": - InitDiffDat(outdat, old, newdat, name, description); - break; - // Creates a DAT file for the specified input directory and saves it to the -out filename - case "Dir2Dat": - InitDir2Dat(outdat, source, name, description); - break; - // Creates a DAT file with those entries that are in -new DAT - case "EDiffdat": - InitEDiffDat(outdat, old, newdat); - break; - // Exports db to export.csv - case "Export": - InitExport(); - break; - // For each specified DAT file it creates a fix DAT - case "Fixdat": - VerifyInputs(inputs, feature); - InitFixdat(inputs, outdat, fixdatOnly, workers, subworkers); - break; - // Import a database from a formatted CSV file - case "Import": - VerifyInputs(inputs, feature); - InitImport(inputs); - break; - // For each specified hash it looks up any available information - case "Lookup": - VerifyInputs(inputs, feature); - InitLookup(inputs, size, outdat); - break; - // Prints memory stats - case "Memstats": - InitMemstats(); - break; - // Merges depot - case "Merge": - VerifyInputs(inputs, feature); - InitMerge(inputs, onlyNeeded, resume, workers, skipInitialScan); - break; - // Create miss and have file - case "Miss": - VerifyInputs(inputs, feature); - InitMiss(inputs); - break; - // Shows progress of the currently running command - case "Progress": - InitProgress(); - break; - // Moves DAT index entries for orphaned DATs - case "Purge Backup": - InitPurgeBackup(backup, workers, depot, dats, logOnly); - break; - // Deletes DAT index entries for orphaned DATs - case "Purge Delete": - InitPurgeDelete(workers, depot, dats, logOnly); - break; - // Refreshes the DAT index from the files in the DAT master directory tree - case "Refresh DATs": - InitRefreshDats(workers, missingSha1s); - break; - // Rescan a specific depot - case "Rescan Depots": - VerifyInputs(inputs, feature); - InitRescanDepots(inputs); - break; - // Gracefully shuts down server - case "Shutdown": - InitShutdown(); - break; - // Prints version - case "Version": - InitVersion(); + + // Requires no input verification + case CancelFeature.Value: + case DbStatsFeature.Value: + case DiffdatFeature.Value: + case Dir2DatFeature.Value: + case EDiffdatFeature.Value: + case ExportFeature.Value: + case MemstatsFeature.Value: + case ProgressFeature.Value: + case PurgeBackupFeature.Value: + case PurgeDeleteFeature.Value: + case RefreshDatsFeature.Value: + case ShutdownFeature.Value: + case VersionFeature.Value: + feature.ProcessFeatures(features); break; + // If nothing is set, show the help default: _help.OutputGenericHelp(); diff --git a/RombaSharp/RombaSharp.csproj b/RombaSharp/RombaSharp.csproj index 3b3578ed..7562deed 100644 --- a/RombaSharp/RombaSharp.csproj +++ b/RombaSharp/RombaSharp.csproj @@ -2,53 +2,13 @@ Exe - net462;netcoreapp2.1 + net462;net472;net48;netcoreapp3.1 win10-x64;win7-x86 - Debug;Release;Mono + Debug;Release AnyCPU;x64 - - DEBUG;TRACE;MONO - false - - 3 - - - - DEBUG;TRACE;MONO - - false - - - - DEBUG;TRACE - false - - 3 - - - - DEBUG;TRACE - - false - - - - TRACE - true - - 3 - - - - TRACE - - true - - - diff --git a/SabreTools.Library/DatFiles/AttractMode.cs b/SabreTools.Library/DatFiles/AttractMode.cs index 258b361c..bfe2781b 100644 --- a/SabreTools.Library/DatFiles/AttractMode.cs +++ b/SabreTools.Library/DatFiles/AttractMode.cs @@ -1,19 +1,11 @@ using System; using System.Collections.Generic; +using System.IO; using System.Text; + using SabreTools.Library.Data; using SabreTools.Library.DatItems; using SabreTools.Library.Tools; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using FileStream = System.IO.FileStream; -using StreamReader = System.IO.StreamReader; -using StreamWriter = System.IO.StreamWriter; -#endif using NaturalSort; namespace SabreTools.Library.DatFiles @@ -119,13 +111,13 @@ namespace SabreTools.Library.DatFiles { try { - Globals.Logger.User("Opening file for writing: {0}", outfile); + Globals.Logger.User($"Opening file for writing: {outfile}"); FileStream fs = Utilities.TryCreate(outfile); // If we get back null for some reason, just log and return if (fs == null) { - Globals.Logger.Warning("File '{0}' could not be created for writing! Please check to see if the file is writable", outfile); + Globals.Logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable"); return false; } @@ -161,16 +153,14 @@ namespace SabreTools.Library.DatFiles // If we have a new game, output the beginning of the new item if (lastgame == null || lastgame.ToLowerInvariant() != item.MachineName.ToLowerInvariant()) - { WriteDatItem(sw, item, ignoreblanks); - } // If we have a "null" game (created by DATFromDir or something similar), log it to file if (item.ItemType == ItemType.Rom && ((Rom)item).Size == -1 && ((Rom)item).CRC == "null") { - Globals.Logger.Verbose("Empty folder found: {0}", item.MachineName); + Globals.Logger.Verbose($"Empty folder found: {item.MachineName}"); item.Name = (item.Name == "null" ? "-" : item.Name); ((Rom)item).Size = Constants.SizeZero; @@ -181,7 +171,7 @@ namespace SabreTools.Library.DatFiles } } - Globals.Logger.Verbose("File written!" + Environment.NewLine); + Globals.Logger.Verbose($"File written!{Environment.NewLine}"); sw.Dispose(); fs.Dispose(); } @@ -222,44 +212,42 @@ namespace SabreTools.Library.DatFiles /// Write out Game start using the supplied StreamWriter /// /// StreamWriter to output to - /// DatItem object to be output + /// DatItem object to be output /// True if blank roms should be skipped on output, false otherwise (default) /// True if the data was written, false on error - private bool WriteDatItem(StreamWriter sw, DatItem rom, bool ignoreblanks = false) + private bool WriteDatItem(StreamWriter sw, DatItem datItem, bool ignoreblanks = false) { // If we are in ignore blanks mode AND we have a blank (0-size) rom, skip - if (ignoreblanks - && (rom.ItemType == ItemType.Rom - && (((Rom)rom).Size == 0 || ((Rom)rom).Size == -1))) - { + if (ignoreblanks && (datItem.ItemType == ItemType.Rom && (((Rom)datItem).Size == 0 || ((Rom)datItem).Size == -1))) return true; - } try { // No game should start with a path separator - if (rom.MachineName.StartsWith(Path.DirectorySeparatorChar.ToString())) - { - rom.MachineName = rom.MachineName.Substring(1); - } + datItem.MachineName = datItem.MachineName.TrimStart(Path.DirectorySeparatorChar); - string state = (!ExcludeFields[(int)Field.MachineName] ? rom.MachineName : "") + ";" - + (!ExcludeFields[(int)Field.Description] ? rom.MachineDescription : "") + ";" - + FileName + ";" - + (!ExcludeFields[(int)Field.CloneOf] ? rom.CloneOf : "") + ";" - + (!ExcludeFields[(int)Field.Year] ? rom.Year : "") + ";" - + (!ExcludeFields[(int)Field.Manufacturer] ? rom.Manufacturer : "") + ";" - /* + rom.Category */ + ";" - /* + rom.Players */ + ";" - /* + rom.Rotation */ + ";" - /* + rom.Control */ + ";" - /* + rom.Status */ + ";" - /* + rom.DisplayCount */ + ";" - /* + rom.DisplayType */ + ";" - /* + rom.AltRomname */ + ";" - /* + rom.AltTitle */ + ";" - + (!ExcludeFields[(int)Field.Comment] ? rom.Comment : "") + ";" - /* + rom.Buttons */ + "\n"; + // Pre-process the item name + ProcessItemName(datItem, true); + + string state = string.Empty; + + state += $"{datItem.GetField(Field.MachineName, ExcludeFields)};"; + state += $"{datItem.GetField(Field.Description, ExcludeFields)};"; + state += $"{FileName};"; + state += $"{datItem.GetField(Field.CloneOf, ExcludeFields)};"; + state += $"{datItem.GetField(Field.Year, ExcludeFields)};"; + state += $"{datItem.GetField(Field.Manufacturer, ExcludeFields)};"; + state += ";"; // $"{datItem.GetField(Field.Category, ExcludeFields)};"; + state += ";"; // $"{datItem.GetField(Field.Players, ExcludeFields)};"; + state += ";"; // $"{datItem.GetField(Field.Rotation, ExcludeFields)};"; + state += ";"; // $"{datItem.GetField(Field.Control, ExcludeFields)};"; + state += ";"; // $"{datItem.GetField(Field.Status, ExcludeFields)};"; + state += ";"; // $"{datItem.GetField(Field.DisplayCount, ExcludeFields)};"; + state += ";"; // $"{datItem.GetField(Field.DisplayType, ExcludeFields)};"; + state += ";"; // $"{datItem.GetField(Field.AltRomname, ExcludeFields)};"; + state += ";"; // $"{datItem.GetField(Field.AltTitle, ExcludeFields)};"; + state += $"{datItem.GetField(Field.Comment, ExcludeFields)};"; + state += ";"; // $"{datItem.GetField(Field.Buttons, ExcludeFields)};"; sw.Write(state); sw.Flush(); diff --git a/SabreTools.Library/DatFiles/ClrMamePro.cs b/SabreTools.Library/DatFiles/ClrMamePro.cs index 3250f534..1147d134 100644 --- a/SabreTools.Library/DatFiles/ClrMamePro.cs +++ b/SabreTools.Library/DatFiles/ClrMamePro.cs @@ -1,20 +1,12 @@ using System; using System.Collections.Generic; +using System.IO; using System.Text; using System.Text.RegularExpressions; + using SabreTools.Library.Data; using SabreTools.Library.DatItems; using SabreTools.Library.Tools; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using FileStream = System.IO.FileStream; -using StreamReader = System.IO.StreamReader; -using StreamWriter = System.IO.StreamWriter; -#endif using NaturalSort; namespace SabreTools.Library.DatFiles @@ -64,9 +56,7 @@ namespace SabreTools.Library.DatFiles // Comments in CMP DATs start with a # if (line.Trim().StartsWith("#")) - { continue; - } // If the line is the header or a game if (Regex.IsMatch(line, Constants.HeaderPatternCMP)) @@ -86,11 +76,11 @@ namespace SabreTools.Library.DatFiles || normalizedValue == "game" // Used by most CMP DATs || normalizedValue == "machine") // Possibly used by MAME CMP DATs { - ReadSet(sr, false, filename, sysid, srcid, keep, clean, remUnicode); + ReadSet(sr, false, filename, sysid, srcid, clean, remUnicode); } else if (normalizedValue == "resource") // Used by some other DATs to denote a BIOS set { - ReadSet(sr, true, filename, sysid, srcid, keep, clean, remUnicode); + ReadSet(sr, true, filename, sysid, srcid, clean, remUnicode); } } } @@ -109,9 +99,7 @@ namespace SabreTools.Library.DatFiles // If there's no subtree to the header, skip it if (reader == null || reader.EndOfStream) - { return; - } // Otherwise, add what is possible string line = reader.ReadLine(); @@ -126,16 +114,15 @@ namespace SabreTools.Library.DatFiles // Get all header items (ONLY OVERWRITE IF THERE'S NO DATA) GroupCollection gc = Regex.Match(line, Constants.ItemPatternCMP).Groups; - string itemval = gc[2].Value.Replace("\"", ""); + string itemval = gc[2].Value.Replace("\"", string.Empty); if (line.Trim().StartsWith("Name:")) { - Name = (String.IsNullOrWhiteSpace(Name) ? line.Substring(6) : Name); + Name = (string.IsNullOrWhiteSpace(Name) ? line.Substring(6) : Name); superdat = superdat || itemval.Contains(" - SuperDAT"); + if (keep && superdat) - { - Type = (String.IsNullOrWhiteSpace(Type) ? "SuperDAT" : Type); - } + Type = (string.IsNullOrWhiteSpace(Type) ? "SuperDAT" : Type); line = reader.ReadLine(); continue; @@ -145,79 +132,76 @@ namespace SabreTools.Library.DatFiles { case "name": case "Name:": - Name = (String.IsNullOrWhiteSpace(Name) ? itemval : Name); + Name = (string.IsNullOrWhiteSpace(Name) ? itemval : Name); superdat = superdat || itemval.Contains(" - SuperDAT"); + if (keep && superdat) - { - Type = (String.IsNullOrWhiteSpace(Type) ? "SuperDAT" : Type); - } + Type = (string.IsNullOrWhiteSpace(Type) ? "SuperDAT" : Type); + break; case "description": case "Description:": - Description = (String.IsNullOrWhiteSpace(Description) ? itemval : Description); + Description = (string.IsNullOrWhiteSpace(Description) ? itemval : Description); break; case "rootdir": case "Rootdir:": - RootDir = (String.IsNullOrWhiteSpace(RootDir) ? itemval : RootDir); + RootDir = (string.IsNullOrWhiteSpace(RootDir) ? itemval : RootDir); break; case "category": case "Category:": - Category = (String.IsNullOrWhiteSpace(Category) ? itemval : Category); + Category = (string.IsNullOrWhiteSpace(Category) ? itemval : Category); break; case "version": case "Version:": - Version = (String.IsNullOrWhiteSpace(Version) ? itemval : Version); + Version = (string.IsNullOrWhiteSpace(Version) ? itemval : Version); break; case "date": case "Date:": - Date = (String.IsNullOrWhiteSpace(Date) ? itemval : Date); + Date = (string.IsNullOrWhiteSpace(Date) ? itemval : Date); break; case "author": case "Author:": - Author = (String.IsNullOrWhiteSpace(Author) ? itemval : Author); + Author = (string.IsNullOrWhiteSpace(Author) ? itemval : Author); break; case "email": case "Email:": - Email = (String.IsNullOrWhiteSpace(Email) ? itemval : Email); + Email = (string.IsNullOrWhiteSpace(Email) ? itemval : Email); break; case "homepage": case "Homepage:": - Homepage = (String.IsNullOrWhiteSpace(Homepage) ? itemval : Homepage); + Homepage = (string.IsNullOrWhiteSpace(Homepage) ? itemval : Homepage); break; case "url": case "Url:": - Url = (String.IsNullOrWhiteSpace(Url) ? itemval : Url); + Url = (string.IsNullOrWhiteSpace(Url) ? itemval : Url); break; case "comment": case "Comment:": - Comment = (String.IsNullOrWhiteSpace(Comment) ? itemval : Comment); + Comment = (string.IsNullOrWhiteSpace(Comment) ? itemval : Comment); break; case "header": case "Header:": - Header = (String.IsNullOrWhiteSpace(Header) ? itemval : Header); + Header = (string.IsNullOrWhiteSpace(Header) ? itemval : Header); break; case "type": case "Type:": - Type = (String.IsNullOrWhiteSpace(Type) ? itemval : Type); + Type = (string.IsNullOrWhiteSpace(Type) ? itemval : Type); superdat = superdat || itemval.Contains("SuperDAT"); break; case "forcemerging": if (ForceMerging == ForceMerging.None) - { ForceMerging = Utilities.GetForceMerging(itemval); - } + break; case "forcezipping": if (ForcePacking == ForcePacking.None) - { ForcePacking = Utilities.GetForcePacking(itemval); - } + break; case "forcepacking": if (ForcePacking == ForcePacking.None) - { ForcePacking = Utilities.GetForcePacking(itemval); - } + break; } @@ -233,7 +217,6 @@ namespace SabreTools.Library.DatFiles /// Name of the file to be parsed /// System ID for the DAT /// Source ID for the DAT - /// True if full pathnames are to be kept, false otherwise (default) /// True if game names are sanitized, false otherwise (default) /// True if we should remove non-ASCII characters from output, false otherwise (default) private void ReadSet( @@ -246,7 +229,6 @@ namespace SabreTools.Library.DatFiles int srcid, // Miscellaneous - bool keep, bool clean, bool remUnicode) { @@ -259,9 +241,7 @@ namespace SabreTools.Library.DatFiles // If there's no subtree to the header, skip it if (reader == null || reader.EndOfStream) - { return; - } // Otherwise, add what is possible string line = reader.ReadLine(); @@ -287,21 +267,13 @@ namespace SabreTools.Library.DatFiles containsItems = true; ItemType temptype = ItemType.Rom; if (line.Trim().StartsWith("rom (")) - { temptype = ItemType.Rom; - } else if (line.Trim().StartsWith("disk (")) - { temptype = ItemType.Disk; - } else if (line.Trim().StartsWith("file (")) - { temptype = ItemType.Rom; - } else if (line.Trim().StartsWith("sample")) - { temptype = ItemType.Sample; - } // Create the proper DatItem based on the type DatItem item = Utilities.GetDatItem(temptype); @@ -316,7 +288,7 @@ namespace SabreTools.Library.DatFiles // If we have a sample, treat it special if (temptype == ItemType.Sample) { - line = line.Trim().Remove(0, 6).Trim().Replace("\"", ""); // Remove "sample" from the input string + line = line.Trim().Remove(0, 6).Trim().Replace("\"", string.Empty); // Remove "sample" from the input string item.Name = line; // Now process and add the sample @@ -348,7 +320,7 @@ namespace SabreTools.Library.DatFiles && linegc[i] != "sha384" && linegc[i] != "sha512") { - item.Name += " " + linegc[i]; + item.Name += $" {linegc[i]}"; } // Perform correction @@ -361,58 +333,57 @@ namespace SabreTools.Library.DatFiles { long tempsize = -1; if (!Int64.TryParse(linegc[++i], out tempsize)) - { tempsize = 0; - } + ((Rom)item).Size = tempsize; } // Get the date from the next part else if (linegc[i] == "date") { - ((Rom)item).Date = linegc[++i].Replace("\"", "") + " " + linegc[++i].Replace("\"", ""); + ((Rom)item).Date = $"{linegc[++i].Replace("\"", string.Empty)} {linegc[++i].Replace("\"", string.Empty)}"; } // Get the CRC from the next part else if (linegc[i] == "crc") { - ((Rom)item).CRC = linegc[++i].Replace("\"", "").ToLowerInvariant(); + ((Rom)item).CRC = linegc[++i].Replace("\"", string.Empty).ToLowerInvariant(); } // Get the MD5 from the next part else if (linegc[i] == "md5") { - ((Rom)item).MD5 = linegc[++i].Replace("\"", "").ToLowerInvariant(); + ((Rom)item).MD5 = linegc[++i].Replace("\"", string.Empty).ToLowerInvariant(); } // Get the RIPEMD160 from the next part else if (linegc[i] == "ripemd160") { - ((Rom)item).RIPEMD160 = linegc[++i].Replace("\"", "").ToLowerInvariant(); + ((Rom)item).RIPEMD160 = linegc[++i].Replace("\"", string.Empty).ToLowerInvariant(); } // Get the SHA1 from the next part else if (linegc[i] == "sha1") { - ((Rom)item).SHA1 = linegc[++i].Replace("\"", "").ToLowerInvariant(); + ((Rom)item).SHA1 = linegc[++i].Replace("\"", string.Empty).ToLowerInvariant(); } // Get the SHA256 from the next part else if (linegc[i] == "sha256") { - ((Rom)item).SHA256 = linegc[++i].Replace("\"", "").ToLowerInvariant(); + ((Rom)item).SHA256 = linegc[++i].Replace("\"", string.Empty).ToLowerInvariant(); } // Get the SHA384 from the next part else if (linegc[i] == "sha384") { - ((Rom)item).SHA384 = linegc[++i].Replace("\"", "").ToLowerInvariant(); + ((Rom)item).SHA384 = linegc[++i].Replace("\"", string.Empty).ToLowerInvariant(); } // Get the SHA512 from the next part else if (linegc[i] == "sha512") { - ((Rom)item).SHA512 = linegc[++i].Replace("\"", "").ToLowerInvariant(); + ((Rom)item).SHA512 = linegc[++i].Replace("\"", string.Empty).ToLowerInvariant(); } } @@ -426,7 +397,7 @@ namespace SabreTools.Library.DatFiles for (int i = 0; i < linegc.Length; i++) { // Look at the current item and use it if possible - string quoteless = linegc[i].Replace("\"", ""); + string quoteless = linegc[i].Replace("\"", string.Empty); switch (quoteless) { //If the item is empty, we automatically skip it because it's a fluke @@ -440,189 +411,188 @@ namespace SabreTools.Library.DatFiles case "verified": ItemStatus tempStandaloneStatus = Utilities.GetItemStatus(quoteless); if (item.ItemType == ItemType.Rom) - { ((Rom)item).ItemStatus = tempStandaloneStatus; - } else if (item.ItemType == ItemType.Disk) - { ((Disk)item).ItemStatus = tempStandaloneStatus; - } + break; // Regular attributes case "name": - quoteless = linegc[++i].Replace("\"", ""); + quoteless = linegc[++i].Replace("\"", string.Empty); item.Name = quoteless; break; case "size": if (item.ItemType == ItemType.Rom) { - quoteless = linegc[++i].Replace("\"", ""); + quoteless = linegc[++i].Replace("\"", string.Empty); if (Int64.TryParse(quoteless, out long size)) - { ((Rom)item).Size = size; - } else - { ((Rom)item).Size = -1; - } } + break; case "crc": if (item.ItemType == ItemType.Rom) { - quoteless = linegc[++i].Replace("\"", ""); + quoteless = linegc[++i].Replace("\"", string.Empty); ((Rom)item).CRC = Utilities.CleanHashData(quoteless, Constants.CRCLength); } + break; case "md5": if (item.ItemType == ItemType.Rom) { - quoteless = linegc[++i].Replace("\"", ""); + quoteless = linegc[++i].Replace("\"", string.Empty); ((Rom)item).MD5 = Utilities.CleanHashData(quoteless, Constants.MD5Length); } else if (item.ItemType == ItemType.Disk) { i++; - quoteless = linegc[i].Replace("\"", ""); + quoteless = linegc[i].Replace("\"", string.Empty); ((Disk)item).MD5 = Utilities.CleanHashData(quoteless, Constants.MD5Length); } + break; case "ripemd160": if (item.ItemType == ItemType.Rom) { - quoteless = linegc[++i].Replace("\"", ""); + quoteless = linegc[++i].Replace("\"", string.Empty); ((Rom)item).RIPEMD160 = Utilities.CleanHashData(quoteless, Constants.RIPEMD160Length); } else if (item.ItemType == ItemType.Disk) { - quoteless = linegc[++i].Replace("\"", ""); + quoteless = linegc[++i].Replace("\"", string.Empty); ((Disk)item).RIPEMD160 = Utilities.CleanHashData(quoteless, Constants.RIPEMD160Length); } + break; case "sha1": if (item.ItemType == ItemType.Rom) { - quoteless = linegc[++i].Replace("\"", ""); + quoteless = linegc[++i].Replace("\"", string.Empty); ((Rom)item).SHA1 = Utilities.CleanHashData(quoteless, Constants.SHA1Length); } else if (item.ItemType == ItemType.Disk) { - quoteless = linegc[++i].Replace("\"", ""); + quoteless = linegc[++i].Replace("\"", string.Empty); ((Disk)item).SHA1 = Utilities.CleanHashData(quoteless, Constants.SHA1Length); } + break; case "sha256": if (item.ItemType == ItemType.Rom) { - quoteless = linegc[++i].Replace("\"", ""); + quoteless = linegc[++i].Replace("\"", string.Empty); ((Rom)item).SHA256 = Utilities.CleanHashData(quoteless, Constants.SHA256Length); } else if (item.ItemType == ItemType.Disk) { - quoteless = linegc[++i].Replace("\"", ""); + quoteless = linegc[++i].Replace("\"", string.Empty); ((Disk)item).SHA256 = Utilities.CleanHashData(quoteless, Constants.SHA256Length); } + break; case "sha384": if (item.ItemType == ItemType.Rom) { - quoteless = linegc[++i].Replace("\"", ""); + quoteless = linegc[++i].Replace("\"", string.Empty); ((Rom)item).SHA384 = Utilities.CleanHashData(quoteless, Constants.SHA384Length); } else if (item.ItemType == ItemType.Disk) { - quoteless = linegc[++i].Replace("\"", ""); + quoteless = linegc[++i].Replace("\"", string.Empty); ((Disk)item).SHA384 = Utilities.CleanHashData(quoteless, Constants.SHA384Length); } + break; case "sha512": if (item.ItemType == ItemType.Rom) { - quoteless = linegc[++i].Replace("\"", ""); + quoteless = linegc[++i].Replace("\"", string.Empty); ((Rom)item).SHA512 = Utilities.CleanHashData(quoteless, Constants.SHA512Length); } else if (item.ItemType == ItemType.Disk) { - quoteless = linegc[++i].Replace("\"", ""); + quoteless = linegc[++i].Replace("\"", string.Empty); ((Disk)item).SHA512 = Utilities.CleanHashData(quoteless, Constants.SHA512Length); } + break; case "status": case "flags": - quoteless = linegc[++i].Replace("\"", ""); + quoteless = linegc[++i].Replace("\"", string.Empty); ItemStatus tempFlagStatus = Utilities.GetItemStatus(quoteless); if (item.ItemType == ItemType.Rom) - { ((Rom)item).ItemStatus = tempFlagStatus; - } else if (item.ItemType == ItemType.Disk) - { ((Disk)item).ItemStatus = tempFlagStatus; - } + break; case "date": if (item.ItemType == ItemType.Rom) { // If we have quotes in the next item, assume only one item if (linegc[i + 1].Contains("\"")) - { - quoteless = linegc[++i].Replace("\"", ""); - } + quoteless = linegc[++i].Replace("\"", string.Empty); + // Otherwise, we assume we need to read the next two items else - { - quoteless = linegc[++i].Replace("\"", "") + " " + linegc[++i].Replace("\"", ""); - } + quoteless = $"{linegc[++i].Replace("\"", string.Empty)} {linegc[++i].Replace("\"", string.Empty)}"; + ((Rom)item).Date = quoteless; } else if (item.ItemType == ItemType.Release) { // If we have quotes in the next item, assume only one item if (linegc[i + 1].Contains("\"")) - { - quoteless = linegc[++i].Replace("\"", ""); - } + quoteless = linegc[++i].Replace("\"", string.Empty); + // Otherwise, we assume we need to read the next two items else - { - quoteless = linegc[++i].Replace("\"", "") + " " + linegc[++i].Replace("\"", ""); - } + quoteless = $"{linegc[++i].Replace("\"", string.Empty)} {linegc[++i].Replace("\"", string.Empty)}"; + ((Release)item).Date = quoteless; } + break; case "default": if (item.ItemType == ItemType.BiosSet) { - quoteless = linegc[++i].Replace("\"", ""); + quoteless = linegc[++i].Replace("\"", string.Empty); ((BiosSet)item).Default = Utilities.GetYesNo(quoteless.ToLowerInvariant()); } else if (item.ItemType == ItemType.Release) { - quoteless = linegc[++i].Replace("\"", ""); + quoteless = linegc[++i].Replace("\"", string.Empty); ((Release)item).Default = Utilities.GetYesNo(quoteless.ToLowerInvariant()); } + break; case "description": if (item.ItemType == ItemType.BiosSet) { - quoteless = linegc[++i].Replace("\"", ""); + quoteless = linegc[++i].Replace("\"", string.Empty); ((BiosSet)item).Description = quoteless.ToLowerInvariant(); } + break; case "region": if (item.ItemType == ItemType.Release) { - quoteless = linegc[++i].Replace("\"", ""); + quoteless = linegc[++i].Replace("\"", string.Empty); ((Release)item).Region = quoteless.ToLowerInvariant(); } + break; case "language": if (item.ItemType == ItemType.Release) { - quoteless = linegc[++i].Replace("\"", ""); + quoteless = linegc[++i].Replace("\"", string.Empty); ((Release)item).Language = quoteless.ToLowerInvariant(); } + break; } } @@ -636,7 +606,7 @@ namespace SabreTools.Library.DatFiles // Set-specific lines have a known pattern GroupCollection setgc = Regex.Match(line, Constants.ItemPatternCMP).Groups; - string itemval = setgc[2].Value.Replace("\"", ""); + string itemval = setgc[2].Value.Replace("\"", string.Empty); switch (setgc[1].Value) { @@ -676,6 +646,7 @@ namespace SabreTools.Library.DatFiles System = filename, SourceID = srcid, }; + blank.CopyMachineInformation(machine); // Now process and add the rom @@ -693,13 +664,13 @@ namespace SabreTools.Library.DatFiles { try { - Globals.Logger.User("Opening file for writing: {0}", outfile); + Globals.Logger.User($"Opening file for writing: {outfile}"); FileStream fs = Utilities.TryCreate(outfile); // If we get back null for some reason, just log and return if (fs == null) { - Globals.Logger.Warning("File '{0}' could not be created for writing! Please check to see if the file is writable", outfile); + Globals.Logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable"); return false; } @@ -735,22 +706,18 @@ namespace SabreTools.Library.DatFiles // If we have a different game and we're not at the start of the list, output the end of last item if (lastgame != null && lastgame.ToLowerInvariant() != rom.MachineName.ToLowerInvariant()) - { WriteEndGame(sw, rom); - } // If we have a new game, output the beginning of the new item if (lastgame == null || lastgame.ToLowerInvariant() != rom.MachineName.ToLowerInvariant()) - { WriteStartGame(sw, rom); - } // If we have a "null" game (created by DATFromDir or something similar), log it to file if (rom.ItemType == ItemType.Rom && ((Rom)rom).Size == -1 && ((Rom)rom).CRC == "null") { - Globals.Logger.Verbose("Empty folder found: {0}", rom.MachineName); + Globals.Logger.Verbose($"Empty folder found: {rom.MachineName}"); // If we're in a mode that doesn't allow for actual empty folders, add the blank info rom.Name = (rom.Name == "null" ? "-" : rom.Name); @@ -775,7 +742,7 @@ namespace SabreTools.Library.DatFiles // Write the file footer out WriteFooter(sw); - Globals.Logger.Verbose("File written!" + Environment.NewLine); + Globals.Logger.Verbose($"File written!{Environment.NewLine}"); sw.Dispose(); fs.Dispose(); } @@ -797,24 +764,48 @@ namespace SabreTools.Library.DatFiles { try { - string header = "clrmamepro (\n" + - "\tname \"" + Name + "\"\n" + - "\tdescription \"" + Description + "\"\n" + - (!String.IsNullOrWhiteSpace(Category) ? "\tcategory \"" + Category + "\"\n" : "") + - "\tversion \"" + Version + "\"\n" + - (!String.IsNullOrWhiteSpace(Date) ? "\tdate \"" + Date + "\"\n" : "") + - "\tauthor \"" + Author + "\"\n" + - (!String.IsNullOrWhiteSpace(Email) ? "\temail \"" + Email + "\"\n" : "") + - (!String.IsNullOrWhiteSpace(Homepage) ? "\thomepage \"" + Homepage + "\"\n" : "") + - (!String.IsNullOrWhiteSpace(Url) ? "\turl \"" + Url + "\"\n" : "") + - (!String.IsNullOrWhiteSpace(Comment) ? "\tcomment \"" + Comment + "\"\n" : "") + - (ForcePacking == ForcePacking.Unzip ? "\tforcezipping no\n" : "") + - (ForcePacking == ForcePacking.Zip ? "\tforcezipping yes\n" : "") + - (ForceMerging == ForceMerging.Full ? "\tforcemerging full\n" : "") + - (ForceMerging == ForceMerging.Split ? "\tforcemerging split\n" : "") + - (ForceMerging == ForceMerging.Merged ? "\tforcemerging merged\n" : "") + - (ForceMerging == ForceMerging.NonMerged ? "\tforcemerging nonmerged\n" : "") + - ")\n"; + string header = "clrmamepro (\n"; + header += $"\tname \"{Name}\"\n"; + header += $"\tdescription \"{Description}\"\n"; + if (!string.IsNullOrWhiteSpace(Category)) + header += $"\tcategory \"{Category}\"\n"; + header += $"\tversion \"{Version}\"\n"; + if (!string.IsNullOrWhiteSpace(Date)) + header += $"\tdate \"{Date}\"\n"; + header += $"\tauthor \"{Author}\"\n"; + if (!string.IsNullOrWhiteSpace(Email)) + header += $"\temail \"{Email}\"\n"; + if (!string.IsNullOrWhiteSpace(Homepage)) + header += $"\thomepage \"{Homepage}\"\n"; + if (!string.IsNullOrWhiteSpace(Url)) + header += $"\turl \"{Url}\"\n"; + if (!string.IsNullOrWhiteSpace(Comment)) + header += $"\tcomment \"{Comment}\"\n"; + switch (ForcePacking) + { + case ForcePacking.Unzip: + header += "\tforcezipping no\n"; + break; + case ForcePacking.Zip: + header += "\tforcezipping yes\n"; + break; + } + switch (ForceMerging) + { + case ForceMerging.Full: + header += "\tforcemerging full\n"; + break; + case ForceMerging.Split: + header += "\tforcemerging split\n"; + break; + case ForceMerging.Merged: + header += "\tforcemerging merged\n"; + break; + case ForceMerging.NonMerged: + header += "\tforcemerging nonmerged\n"; + break; + } + header += ")\n"; // Write the header out sw.Write(header); @@ -833,25 +824,31 @@ namespace SabreTools.Library.DatFiles /// Write out Game start using the supplied StreamWriter /// /// StreamWriter to output to - /// DatItem object to be output + /// DatItem object to be output /// True if the data was written, false on error - private bool WriteStartGame(StreamWriter sw, DatItem rom) + private bool WriteStartGame(StreamWriter sw, DatItem datItem) { try { // No game should start with a path separator - if (rom.MachineName.StartsWith(Path.DirectorySeparatorChar.ToString())) - { - rom.MachineName = rom.MachineName.Substring(1); - } + datItem.MachineName = datItem.MachineName.TrimStart(Path.DirectorySeparatorChar); - string state = (rom.MachineType == MachineType.Bios ? "resource" : "game") + " (\n\tname \"" + (!ExcludeFields[(int)Field.MachineName] ? rom.MachineName : "") + "\"\n" + - (!ExcludeFields[(int)Field.RomOf] && String.IsNullOrWhiteSpace(rom.RomOf) ? "" : "\tromof \"" + rom.RomOf + "\"\n") + - (!ExcludeFields[(int)Field.CloneOf] && String.IsNullOrWhiteSpace(rom.CloneOf) ? "" : "\tcloneof \"" + rom.CloneOf + "\"\n") + - (!ExcludeFields[(int)Field.SampleOf] && String.IsNullOrWhiteSpace(rom.SampleOf) ? "" : "\tsampleof \"" + rom.SampleOf + "\"\n") + - (!ExcludeFields[(int)Field.Description] ? "\tdescription \"" + (String.IsNullOrWhiteSpace(rom.MachineDescription) ? rom.MachineName : rom.MachineDescription) + "\"\n" : "") + - (!ExcludeFields[(int)Field.Year] && String.IsNullOrWhiteSpace(rom.Year) ? "" : "\tyear " + rom.Year + "\n") + - (!ExcludeFields[(int)Field.Manufacturer] && String.IsNullOrWhiteSpace(rom.Manufacturer) ? "" : "\tmanufacturer \"" + rom.Manufacturer + "\"\n"); + // Build the state based on excluded fields + string state = (datItem.MachineType == MachineType.Bios ? "resource" : "game"); + state += $" (\n\tname \"{datItem.MachineName}\"\n"; + state += $"\tromof \"{datItem.RomOf}\"\n"; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CloneOf, ExcludeFields))) + state += $"\tcloneof \"{datItem.CloneOf}\"\n"; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SampleOf, ExcludeFields))) + state += $"\tsampleof \"{datItem.SampleOf}\"\n"; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Description, ExcludeFields))) + state += $"\tdescription \"{datItem.MachineDescription}\"\n"; + else if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Description, ExcludeFields))) + state += $"\tdescription \"{datItem.MachineName}\"\n"; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Year, ExcludeFields))) + state += $"\tyear \"{datItem.Year}\"\n"; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Manufacturer, ExcludeFields))) + state += $"\tmanufacturer \"{datItem.Manufacturer}\"\n"; sw.Write(state); sw.Flush(); @@ -869,13 +866,19 @@ namespace SabreTools.Library.DatFiles /// Write out Game end using the supplied StreamWriter /// /// StreamWriter to output to - /// DatItem object to be output + /// DatItem object to be output /// True if the data was written, false on error - private bool WriteEndGame(StreamWriter sw, DatItem rom) + private bool WriteEndGame(StreamWriter sw, DatItem datItem) { try { - string state = (!ExcludeFields[(int)Field.SampleOf] && String.IsNullOrWhiteSpace(rom.SampleOf) ? "" : "\tsampleof \"" + rom.SampleOf + "\"\n") + ")\n"; + string state = string.Empty; + + // Build the state based on excluded fields + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SampleOf, ExcludeFields))) + state += $"\tsampleof \"{datItem.SampleOf}\"\n"; + + state += ")\n"; sw.Write(state); sw.Flush(); @@ -894,78 +897,103 @@ namespace SabreTools.Library.DatFiles /// /// DatFile to write out from /// StreamWriter to output to - /// DatItem object to be output + /// DatItem object to be output /// True if blank roms should be skipped on output, false otherwise (default) /// True if the data was written, false on error - private bool WriteDatItem(StreamWriter sw, DatItem rom, bool ignoreblanks = false) + private bool WriteDatItem(StreamWriter sw, DatItem datItem, bool ignoreblanks = false) { // If we are in ignore blanks mode AND we have a blank (0-size) rom, skip - if (ignoreblanks - && (rom.ItemType == ItemType.Rom - && (((Rom)rom).Size == 0 || ((Rom)rom).Size == -1))) - { + if (ignoreblanks && (datItem.ItemType == ItemType.Rom && ((datItem as Rom).Size == 0 || (datItem as Rom).Size == -1))) return true; - } try { - string state = ""; + string state = string.Empty; // Pre-process the item name - ProcessItemName(rom, true); + ProcessItemName(datItem, true); - switch (rom.ItemType) + // Build the state based on excluded fields + switch (datItem.ItemType) { case ItemType.Archive: - state += "\tarchive ( name\"" + (!ExcludeFields[(int)Field.Name] ? rom.Name : "") + "\"" - + " )\n"; + state += $"\tarchive ( name\"{datItem.GetField(Field.Name, ExcludeFields)}\""; + state += " )\n"; break; + case ItemType.BiosSet: - state += "\tbiosset ( name\"" + (!ExcludeFields[(int)Field.Name] ? rom.Name : "") + "\"" - + (!ExcludeFields[(int)Field.BiosDescription] && !String.IsNullOrWhiteSpace(((BiosSet)rom).Description) ? " description \"" + ((BiosSet)rom).Description + "\"" : "") - + (!ExcludeFields[(int)Field.Default] && ((BiosSet)rom).Default != null - ? "default " + ((BiosSet)rom).Default.ToString().ToLowerInvariant() - : "") - + " )\n"; + var biosSet = datItem as BiosSet; + state += $"\tbiosset ( name\"{datItem.GetField(Field.Name, ExcludeFields)}\""; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.BiosDescription, ExcludeFields))) + state += $" description \"{biosSet.Description}\""; + if (!ExcludeFields[(int)Field.Default] && biosSet.Default != null) + state += $" default \"{biosSet.Default.ToString().ToLowerInvariant()}\""; + state += " )\n"; break; + case ItemType.Disk: - state += "\tdisk ( name \"" + (!ExcludeFields[(int)Field.Name] ? rom.Name : "") + "\"" - + (!ExcludeFields[(int)Field.MD5] && !String.IsNullOrWhiteSpace(((Disk)rom).MD5) ? " md5 " + ((Disk)rom).MD5.ToLowerInvariant() : "") - + (!ExcludeFields[(int)Field.RIPEMD160] && !String.IsNullOrWhiteSpace(((Disk)rom).RIPEMD160) ? " ripemd160 " + ((Disk)rom).RIPEMD160.ToLowerInvariant() : "") - + (!ExcludeFields[(int)Field.SHA1] && !String.IsNullOrWhiteSpace(((Disk)rom).SHA1) ? " sha1 " + ((Disk)rom).SHA1.ToLowerInvariant() : "") - + (!ExcludeFields[(int)Field.SHA256] && !String.IsNullOrWhiteSpace(((Disk)rom).SHA256) ? " sha256 " + ((Disk)rom).SHA256.ToLowerInvariant() : "") - + (!ExcludeFields[(int)Field.SHA384] && !String.IsNullOrWhiteSpace(((Disk)rom).SHA384) ? " sha384 " + ((Disk)rom).SHA384.ToLowerInvariant() : "") - + (!ExcludeFields[(int)Field.SHA512] && !String.IsNullOrWhiteSpace(((Disk)rom).SHA512) ? " sha512 " + ((Disk)rom).SHA512.ToLowerInvariant() : "") - + (!ExcludeFields[(int)Field.Status] && ((Disk)rom).ItemStatus != ItemStatus.None ? " flags " + ((Disk)rom).ItemStatus.ToString().ToLowerInvariant() : "") - + " )\n"; + var disk = datItem as Disk; + state += $"\tdisk ( name \"{datItem.GetField(Field.Name, ExcludeFields)}\""; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields))) + state += $" md5 \"{disk.MD5.ToLowerInvariant()}\""; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, ExcludeFields))) + state += $" ripemd160 \"{disk.RIPEMD160.ToLowerInvariant()}\""; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields))) + state += $" sha1 \"{disk.SHA1.ToLowerInvariant()}\""; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, ExcludeFields))) + state += $" sha256 \"{disk.SHA256.ToLowerInvariant()}\""; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, ExcludeFields))) + state += $" sha384 \"{disk.SHA384.ToLowerInvariant()}\""; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, ExcludeFields))) + state += $" sha512 \"{disk.SHA512.ToLowerInvariant()}\""; + if (!ExcludeFields[(int)Field.Status] && disk.ItemStatus != ItemStatus.None) + state += $" flags \"{disk.ItemStatus.ToString().ToLowerInvariant()}\""; + state += " )\n"; break; + case ItemType.Release: - state += "\trelease ( name\"" + (!ExcludeFields[(int)Field.Name] ? rom.Name : "") + "\"" - + (!ExcludeFields[(int)Field.Region] && !String.IsNullOrWhiteSpace(((Release)rom).Region) ? " region \"" + ((Release)rom).Region + "\"" : "") - + (!ExcludeFields[(int)Field.Language] && !String.IsNullOrWhiteSpace(((Release)rom).Language) ? " language \"" + ((Release)rom).Language + "\"" : "") - + (!ExcludeFields[(int)Field.Date] && !String.IsNullOrWhiteSpace(((Release)rom).Date) ? " date \"" + ((Release)rom).Date + "\"" : "") - + (!ExcludeFields[(int)Field.Default] && ((Release)rom).Default != null - ? "default " + ((Release)rom).Default.ToString().ToLowerInvariant() - : "") - + " )\n"; + var release = datItem as Release; + state += $"\trelease ( name\"{datItem.GetField(Field.Name, ExcludeFields)}\""; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Region, ExcludeFields))) + state += $" region \"{release.Region}\""; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Language, ExcludeFields))) + state += $" language \"{release.Language}\""; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Date, ExcludeFields))) + state += $" date \"{release.Date}\""; + if (!ExcludeFields[(int)Field.Default] && release.Default != null) + state += $" default \"{release.Default.ToString().ToLowerInvariant()}\""; + state += " )\n"; break; + case ItemType.Rom: - state += "\trom ( name \"" + (!ExcludeFields[(int)Field.Name] ? rom.Name : "") + "\"" - + (!ExcludeFields[(int)Field.Size] && ((Rom)rom).Size != -1 ? " size " + ((Rom)rom).Size : "") - + (!ExcludeFields[(int)Field.CRC] && !String.IsNullOrWhiteSpace(((Rom)rom).CRC) ? " crc " + ((Rom)rom).CRC.ToLowerInvariant() : "") - + (!ExcludeFields[(int)Field.MD5] && !String.IsNullOrWhiteSpace(((Rom)rom).MD5) ? " md5 " + ((Rom)rom).MD5.ToLowerInvariant() : "") - + (!ExcludeFields[(int)Field.RIPEMD160] && !String.IsNullOrWhiteSpace(((Rom)rom).RIPEMD160) ? " ripemd160 " + ((Rom)rom).RIPEMD160.ToLowerInvariant() : "") - + (!ExcludeFields[(int)Field.SHA1] && !String.IsNullOrWhiteSpace(((Rom)rom).SHA1) ? " sha1 " + ((Rom)rom).SHA1.ToLowerInvariant() : "") - + (!ExcludeFields[(int)Field.SHA256] && !String.IsNullOrWhiteSpace(((Rom)rom).SHA256) ? " sha256 " + ((Rom)rom).SHA256.ToLowerInvariant() : "") - + (!ExcludeFields[(int)Field.SHA384] && !String.IsNullOrWhiteSpace(((Rom)rom).SHA384) ? " sha384 " + ((Rom)rom).SHA384.ToLowerInvariant() : "") - + (!ExcludeFields[(int)Field.SHA512] && !String.IsNullOrWhiteSpace(((Rom)rom).SHA512) ? " sha512 " + ((Rom)rom).SHA512.ToLowerInvariant() : "") - + (!ExcludeFields[(int)Field.Date] && !String.IsNullOrWhiteSpace(((Rom)rom).Date) ? " date \"" + ((Rom)rom).Date + "\"" : "") - + (!ExcludeFields[(int)Field.Status] && ((Rom)rom).ItemStatus != ItemStatus.None ? " flags " + ((Rom)rom).ItemStatus.ToString().ToLowerInvariant() : "") - + " )\n"; + var rom = datItem as Rom; + state += $"\trom ( name \"{datItem.GetField(Field.Name, ExcludeFields)}\""; + if (!ExcludeFields[(int)Field.Size] && rom.Size != -1) + state += $" size \"{rom.Size}\""; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CRC, ExcludeFields))) + state += $" crc \"{rom.CRC.ToLowerInvariant()}\""; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields))) + state += $" md5 \"{rom.MD5.ToLowerInvariant()}\""; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, ExcludeFields))) + state += $" ripemd160 \"{rom.RIPEMD160.ToLowerInvariant()}\""; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields))) + state += $" sha1 \"{rom.SHA1.ToLowerInvariant()}\""; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, ExcludeFields))) + state += $" sha256 \"{rom.SHA256.ToLowerInvariant()}\""; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, ExcludeFields))) + state += $" sha384 \"{rom.SHA384.ToLowerInvariant()}\""; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, ExcludeFields))) + state += $" sha512 \"{rom.SHA512.ToLowerInvariant()}\""; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Date, ExcludeFields))) + state += $" date \"{rom.Date}\""; + if (!ExcludeFields[(int)Field.Status] && rom.ItemStatus != ItemStatus.None) + state += $" flags \"{rom.ItemStatus.ToString().ToLowerInvariant()}\""; + state += " )\n"; break; + case ItemType.Sample: - state += "\tsample ( name\"" + (!ExcludeFields[(int)Field.Name] ? rom.Name : "") + "\"" - + " )\n"; + state += $"\tsample ( name\"{datItem.GetField(Field.Name, ExcludeFields)}\""; + state += " )\n"; break; } diff --git a/SabreTools.Library/DatFiles/DatFile.cs b/SabreTools.Library/DatFiles/DatFile.cs index 9daf6d9d..93564921 100644 --- a/SabreTools.Library/DatFiles/DatFile.cs +++ b/SabreTools.Library/DatFiles/DatFile.cs @@ -1,29 +1,18 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Net; using System.Text.RegularExpressions; using System.Threading.Tasks; + using SabreTools.Library.Data; using SabreTools.Library.FileTypes; using SabreTools.Library.DatItems; using SabreTools.Library.Reports; using SabreTools.Library.Skippers; using SabreTools.Library.Tools; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using FileStream = System.IO.FileStream; -using IOException = System.IO.IOException; -using MemoryStream = System.IO.MemoryStream; -using SearchOption = System.IO.SearchOption; -using SeekOrigin = System.IO.SeekOrigin; -using Stream = System.IO.Stream; -#endif using NaturalSort; namespace SabreTools.Library.DatFiles @@ -555,6 +544,7 @@ namespace SabreTools.Library.DatFiles /// /// Romba output mode /// + /// TODO: Remove use of this in lieu of depot parameter public bool Romba { get @@ -1105,16 +1095,12 @@ namespace SabreTools.Library.DatFiles // If the key is null, we return false since keys can't be null if (key == null) - { return contains; - } lock (_items) { if (_items.ContainsKey(key)) - { contains = _items[key].Contains(value); - } } return contains; @@ -1246,9 +1232,7 @@ namespace SabreTools.Library.DatFiles { // If the key is missing from the dictionary, add it if (!_items.ContainsKey(key)) - { _items.Add(key, new List()); - } } #endregion @@ -1266,14 +1250,12 @@ namespace SabreTools.Library.DatFiles { // If we have a situation where there's no dictionary or no keys at all, we skip if (_items == null || _items.Count == 0) - { return; - } // If the sorted type isn't the same, we want to sort the dictionary accordingly if (this.SortedBy != bucketBy) { - Globals.Logger.User("Organizing roms by {0}", bucketBy); + Globals.Logger.User($"Organizing roms by {bucketBy}"); // Set the sorted type this.SortedBy = bucketBy; @@ -1306,19 +1288,13 @@ namespace SabreTools.Library.DatFiles i--; // This make sure that the pointer stays on the correct since one was removed } } - - // If the key is now empty, remove it - if (this[key].Count == 0) - { - Remove(key); - } } } // If the merge type isn't the same, we want to merge the dictionary accordingly if (this.MergedBy != deduperoms) { - Globals.Logger.User("Deduping roms by {0}", deduperoms); + Globals.Logger.User($"Deduping roms by {deduperoms}"); // Set the sorted type this.MergedBy = deduperoms; @@ -1334,9 +1310,7 @@ namespace SabreTools.Library.DatFiles // If we're merging the roms, do so if (deduperoms == DedupeType.Full || (deduperoms == DedupeType.Game && bucketBy == SortedBy.Game)) - { sortedlist = DatItem.Merge(sortedlist); - } // Add the list back to the dictionary Remove(key); @@ -1371,45 +1345,31 @@ namespace SabreTools.Library.DatFiles { // If all items are supposed to have a SHA-512, we sort by that if (RomCount + DiskCount - NodumpCount == SHA512Count) - { BucketBy(SortedBy.SHA512, deduperoms, lower, norename); - } // If all items are supposed to have a SHA-384, we sort by that else if (RomCount + DiskCount - NodumpCount == SHA384Count) - { BucketBy(SortedBy.SHA384, deduperoms, lower, norename); - } // If all items are supposed to have a SHA-256, we sort by that else if (RomCount + DiskCount - NodumpCount == SHA256Count) - { BucketBy(SortedBy.SHA256, deduperoms, lower, norename); - } // If all items are supposed to have a SHA-1, we sort by that else if (RomCount + DiskCount - NodumpCount == SHA1Count) - { BucketBy(SortedBy.SHA1, deduperoms, lower, norename); - } - // If all items are supposed to have a SHA-1, we sort by that + // If all items are supposed to have a RIPEMD160, we sort by that else if (RomCount + DiskCount - NodumpCount == RIPEMD160Count) - { BucketBy(SortedBy.RIPEMD160, deduperoms, lower, norename); - } // If all items are supposed to have a MD5, we sort by that else if (RomCount + DiskCount - NodumpCount == MD5Count) - { BucketBy(SortedBy.MD5, deduperoms, lower, norename); - } // Otherwise, we sort by CRC else - { BucketBy(SortedBy.CRC, deduperoms, lower, norename); - } } /// @@ -1421,9 +1381,7 @@ namespace SabreTools.Library.DatFiles foreach(string key in keys) { if (this[key].Count == 0) - { Remove(key); - } } } @@ -1489,8 +1447,20 @@ namespace SabreTools.Library.DatFiles /// Type of the split that should be performed (split, merged, fully merged) /// List of Fields representing what should be updated [only for base replacement] /// True if descriptions should only be replaced if the game name is the same, false otherwise [only for base replacement] - public void DetermineUpdateType(List inputPaths, List basePaths, string outDir, UpdateMode updateMode, bool inplace, bool skip, - bool clean, bool remUnicode, bool descAsName, Filter filter, SplitType splitType, List updateFields, bool onlySame) + public void DetermineUpdateType( + List inputPaths, + List basePaths, + string outDir, + UpdateMode updateMode, + bool inplace, + bool skip, + bool clean, + bool remUnicode, + bool descAsName, + Filter filter, + SplitType splitType, + List updateFields, + bool onlySame) { // Ensure we only have files in the inputs List inputFileNames = Utilities.GetOnlyFilesFromInputs(inputPaths, appendparent: true); @@ -1505,52 +1475,52 @@ namespace SabreTools.Library.DatFiles // Reverse inputs if we're in a required mode if ((updateMode & UpdateMode.DiffReverseCascade) != 0) - { inputFileNames.Reverse(); - } if ((updateMode & UpdateMode.ReverseBaseReplace) != 0) - { baseFileNames.Reverse(); - } // If we're in merging mode if ((updateMode & UpdateMode.Merge) != 0) { // Populate the combined data and get the headers - List datHeaders = PopulateUserData(inputFileNames, inplace, clean, remUnicode, descAsName, outDir, filter, splitType); - MergeNoDiff(inputFileNames, datHeaders, outDir); + PopulateUserData(inputFileNames, clean, remUnicode, descAsName, filter, splitType); + MergeNoDiff(inputFileNames, outDir); } + // If we have one of the standard diffing modes else if ((updateMode & UpdateMode.DiffDupesOnly) != 0 || (updateMode & UpdateMode.DiffNoDupesOnly) != 0 || (updateMode & UpdateMode.DiffIndividualsOnly) != 0) { // Populate the combined data - PopulateUserData(inputFileNames, inplace, clean, remUnicode, descAsName, outDir, filter, splitType); - DiffNoCascade(inputFileNames, outDir, filter, updateMode); + PopulateUserData(inputFileNames, clean, remUnicode, descAsName, filter, splitType); + DiffNoCascade(inputFileNames, outDir, updateMode); } + // If we have one of the cascaded diffing modes else if ((updateMode & UpdateMode.DiffCascade) != 0 || (updateMode & UpdateMode.DiffReverseCascade) != 0) { // Populate the combined data and get the headers - List datHeaders = PopulateUserData(inputFileNames, inplace, clean, remUnicode, descAsName, outDir, filter, splitType); + List datHeaders = PopulateUserData(inputFileNames, clean, remUnicode, descAsName, filter, splitType); DiffCascade(inputFileNames, datHeaders, outDir, inplace, skip); } + // If we have diff against mode else if ((updateMode & UpdateMode.DiffAgainst) != 0) { // Populate the combined data - PopulateUserData(baseFileNames, inplace, clean, remUnicode, descAsName, outDir, filter, splitType); - DiffAgainst(inputFileNames, outDir, inplace, clean, remUnicode, descAsName, filter, splitType); + PopulateUserData(baseFileNames, clean, remUnicode, descAsName, filter, splitType); + DiffAgainst(inputFileNames, outDir, inplace, clean, remUnicode, descAsName); } + // If we have one of the base replacement modes else if ((updateMode & UpdateMode.BaseReplace) != 0 || (updateMode & UpdateMode.ReverseBaseReplace) != 0) { // Populate the combined data - PopulateUserData(baseFileNames, inplace, clean, remUnicode, descAsName, outDir, filter, splitType); - BaseReplace(inputFileNames, outDir, inplace, clean, remUnicode, descAsName, filter, splitType, updateFields, onlySame); + PopulateUserData(baseFileNames, clean, remUnicode, descAsName, filter, splitType); + BaseReplace(inputFileNames, outDir, inplace, clean, remUnicode, descAsName, filter, updateFields, onlySame); } return; @@ -1560,16 +1530,19 @@ namespace SabreTools.Library.DatFiles /// Populate the user DatData object from the input files /// /// Paths to DATs to parse - /// True if the output files should overwrite their inputs, false otherwise /// True to clean the game names to WoD standard, false otherwise (default) /// True if we should remove non-ASCII characters from output, false otherwise (default) /// True to use game descriptions as the names, false otherwise (default) - /// Optional param for output directory /// Filter object to be passed to the DatItem level /// Type of the split that should be performed (split, merged, fully merged) /// List of DatData objects representing headers - private List PopulateUserData(List inputs, bool inplace, bool clean, bool remUnicode, bool descAsName, - string outDir, Filter filter, SplitType splitType) + private List PopulateUserData( + List inputs, + bool clean, + bool remUnicode, + bool descAsName, + Filter filter, + SplitType splitType) { DatFile[] datHeaders = new DatFile[inputs.Count]; InternalStopwatch watch = new InternalStopwatch("Processing individual DATs"); @@ -1578,7 +1551,7 @@ namespace SabreTools.Library.DatFiles Parallel.For(0, inputs.Count, Globals.ParallelOptions, i => { string input = inputs[i]; - Globals.Logger.User("Adding DAT: {0}", input.Split('¬')[0]); + Globals.Logger.User($"Adding DAT: {input.Split('¬')[0]}"); datHeaders[i] = new DatFile() { DatFormat = (this.DatFormat != 0 ? this.DatFormat : 0), @@ -1594,7 +1567,6 @@ namespace SabreTools.Library.DatFiles AddExtension = this.AddExtension, ReplaceExtension = this.ReplaceExtension, RemoveExtension = this.RemoveExtension, - Romba = this.Romba, GameName = this.GameName, Quotes = this.Quotes, UseRomName = this.UseRomName, @@ -1641,11 +1613,18 @@ namespace SabreTools.Library.DatFiles /// True if we should remove non-ASCII characters from output, false otherwise (default) /// True to allow SL DATs to have game names used instead of descriptions, false otherwise (default) /// Filter object to be passed to the DatItem level - /// Type of the split that should be performed (split, merged, fully merged) /// List of Fields representing what should be updated [only for base replacement] /// True if descriptions should only be replaced if the game name is the same, false otherwise - public void BaseReplace(List inputFileNames, string outDir, bool inplace, bool clean, bool remUnicode, - bool descAsName, Filter filter, SplitType splitType, List updateFields, bool onlySame) + public void BaseReplace( + List inputFileNames, + string outDir, + bool inplace, + bool clean, + bool remUnicode, + bool descAsName, + Filter filter, + List updateFields, + bool onlySame) { // Fields unique to a DatItem List datItemFields = new List() @@ -1705,7 +1684,7 @@ namespace SabreTools.Library.DatFiles // We want to try to replace each item in each input DAT from the base foreach (string path in inputFileNames) { - Globals.Logger.User("Replacing items in '{0}' from the base DAT", path.Split('¬')[0]); + Globals.Logger.User($"Replacing items in '{path.Split('¬')[0]}' from the base DAT"); // First we parse in the DAT internally DatFile intDat = new DatFile() @@ -1723,7 +1702,6 @@ namespace SabreTools.Library.DatFiles AddExtension = this.AddExtension, ReplaceExtension = this.ReplaceExtension, RemoveExtension = this.RemoveExtension, - Romba = this.Romba, GameName = this.GameName, Quotes = this.Quotes, UseRomName = this.UseRomName, @@ -2152,10 +2130,13 @@ namespace SabreTools.Library.DatFiles /// True to clean the game names to WoD standard, false otherwise (default) /// True if we should remove non-ASCII characters from output, false otherwise (default) /// True to use game descriptions as the names, false otherwise (default) - /// Filter object to be passed to the DatItem level - /// Type of the split that should be performed (split, merged, fully merged) - public void DiffAgainst(List inputFileNames, string outDir, bool inplace, bool clean, bool remUnicode, - bool descAsName, Filter filter, SplitType splitType) + public void DiffAgainst( + List inputFileNames, + string outDir, + bool inplace, + bool clean, + bool remUnicode, + bool descAsName) { // For comparison's sake, we want to use CRC as the base ordering BucketBy(SortedBy.CRC, DedupeType.Full); @@ -2163,7 +2144,7 @@ namespace SabreTools.Library.DatFiles // Now we want to compare each input DAT against the base foreach (string path in inputFileNames) { - Globals.Logger.User("Comparing '{0}'' to base DAT", path.Split('¬')[0]); + Globals.Logger.User($"Comparing '{path.Split('¬')[0]}' to base DAT"); // First we parse in the DAT internally DatFile intDat = new DatFile(); @@ -2219,7 +2200,7 @@ namespace SabreTools.Library.DatFiles DatFile[] outDatsArray = new DatFile[inputs.Count]; Parallel.For(0, inputs.Count, Globals.ParallelOptions, j => { - string innerpost = " (" + j + " - " + Utilities.GetFilenameFromFileAndParent(inputs[j], true) + " Only)"; + string innerpost = $" ({j} - {Utilities.GetFilenameFromFileAndParent(inputs[j], true)} Only)"; DatFile diffData; // If we're in inplace mode or the output directory is set, take the appropriate DatData object already stored @@ -2255,16 +2236,14 @@ namespace SabreTools.Library.DatFiles // If the rom list is empty or null, just skip it if (items == null || items.Count == 0) - { return; - } foreach (DatItem item in items) { // There's odd cases where there are items with System ID < 0. Skip them for now if (item.SystemID < 0) { - Globals.Logger.Warning("Item found with a <0 SystemID: {0}", item.Name); + Globals.Logger.Warning($"Item found with a <0 SystemID: {item.Name}"); continue; } @@ -2293,31 +2272,25 @@ namespace SabreTools.Library.DatFiles /// /// List of inputs to write out from /// Output directory to write the DATs to - /// Filter object to be passed to the DatItem level /// Non-zero flag for diffing mode, zero otherwise - public void DiffNoCascade(List inputs, string outDir, Filter filter, UpdateMode diff) + public void DiffNoCascade(List inputs, string outDir, UpdateMode diff) { InternalStopwatch watch = new InternalStopwatch("Initializing all output DATs"); // Default vars for use - string post = ""; + string post = string.Empty; DatFile outerDiffData = new DatFile(); DatFile dupeData = new DatFile(); // Fill in any information not in the base DAT - if (String.IsNullOrWhiteSpace(FileName)) - { + if (string.IsNullOrWhiteSpace(FileName)) FileName = "All DATs"; - } - if (String.IsNullOrWhiteSpace(Name)) - { + if (string.IsNullOrWhiteSpace(Name)) Name = "All DATs"; - } - if (String.IsNullOrWhiteSpace(Description)) - { + + if (string.IsNullOrWhiteSpace(Description)) Description = "All DATs"; - } // Don't have External dupes if ((diff & UpdateMode.DiffNoDupesOnly) != 0) @@ -2351,7 +2324,7 @@ namespace SabreTools.Library.DatFiles Parallel.For(0, inputs.Count, Globals.ParallelOptions, j => { - string innerpost = " (" + j + " - " + Utilities.GetFilenameFromFileAndParent(inputs[j], true) + " Only)"; + string innerpost = $" ({j} - {Utilities.GetFilenameFromFileAndParent(inputs[j], true)} Only)"; DatFile diffData = new DatFile(this); diffData.FileName += innerpost; diffData.Name += innerpost; @@ -2375,9 +2348,7 @@ namespace SabreTools.Library.DatFiles // If the rom list is empty or null, just skip it if (items == null || items.Count == 0) - { return; - } // Loop through and add the items correctly foreach (DatItem item in items) @@ -2389,15 +2360,13 @@ namespace SabreTools.Library.DatFiles { // Individual DATs that are output if ((diff & UpdateMode.DiffIndividualsOnly) != 0) - { outDats[item.SystemID].Add(key, item); - } // Merged no-duplicates DAT if ((diff & UpdateMode.DiffNoDupesOnly) != 0) { DatItem newrom = item.Clone() as DatItem; - newrom.MachineName += " (" + Path.GetFileNameWithoutExtension(inputs[item.SystemID].Split('¬')[0]) + ")"; + newrom.MachineName += $" ({Path.GetFileNameWithoutExtension(inputs[item.SystemID].Split('¬')[0])})"; outerDiffData.Add(key, newrom); } @@ -2410,7 +2379,7 @@ namespace SabreTools.Library.DatFiles if ((item.DupeType & DupeType.External) != 0) { DatItem newrom = item.Clone() as DatItem; - newrom.MachineName += " (" + Path.GetFileNameWithoutExtension(inputs[item.SystemID].Split('¬')[0]) + ")"; + newrom.MachineName += $" ({Path.GetFileNameWithoutExtension(inputs[item.SystemID].Split('¬')[0])})"; dupeData.Add(key, newrom); } @@ -2425,15 +2394,11 @@ namespace SabreTools.Library.DatFiles // Output the difflist (a-b)+(b-a) diff if ((diff & UpdateMode.DiffNoDupesOnly) != 0) - { outerDiffData.Write(outDir, overwrite: false); - } // Output the (ab) diff if ((diff & UpdateMode.DiffDupesOnly) != 0) - { dupeData.Write(outDir, overwrite: false); - } // Output the individual (a-b) DATs if ((diff & UpdateMode.DiffIndividualsOnly) != 0) @@ -2454,9 +2419,8 @@ namespace SabreTools.Library.DatFiles /// Output user defined merge /// /// List of inputs to write out from - /// Dat headers used optionally /// Output directory to write the DATs to - public void MergeNoDiff(List inputs, List datHeaders, string outDir) + public void MergeNoDiff(List inputs, string outDir) { // If we're in SuperDAT mode, prefix all games with their respective DATs if (Type == "SuperDAT") @@ -2472,7 +2436,7 @@ namespace SabreTools.Library.DatFiles string filename = inputs[newItem.SystemID].Split('¬')[0]; string rootpath = inputs[newItem.SystemID].Split('¬')[1]; - rootpath += (String.IsNullOrWhiteSpace(rootpath) ? "" : Path.DirectorySeparatorChar.ToString()); + rootpath += (string.IsNullOrWhiteSpace(rootpath) ? string.Empty : Path.DirectorySeparatorChar.ToString()); filename = filename.Remove(0, rootpath.Length); newItem.MachineName = Path.GetDirectoryName(filename) + Path.DirectorySeparatorChar + Path.GetFileNameWithoutExtension(filename) + Path.DirectorySeparatorChar @@ -2501,14 +2465,21 @@ namespace SabreTools.Library.DatFiles /// True to use game descriptions as the names, false otherwise (default) /// Filter object to be passed to the DatItem level /// Type of the split that should be performed (split, merged, fully merged) - public void Update(List inputFileNames, string outDir, bool inplace, bool clean, bool remUnicode, bool descAsName, - Filter filter, SplitType splitType) + public void Update( + List inputFileNames, + string outDir, + bool inplace, + bool clean, + bool remUnicode, + bool descAsName, + Filter filter, + SplitType splitType) { // Iterate over the files foreach (string file in inputFileNames) { DatFile innerDatdata = new DatFile(this); - Globals.Logger.User("Processing '{0}'", Path.GetFileName(file.Split('¬')[0])); + Globals.Logger.User($"Processing '{Path.GetFileName(file.Split('¬')[0])}'"); innerDatdata.Parse(file, 0, 0, splitType, keep: true, clean: clean, remUnicode: remUnicode, descAsName: descAsName, keepext: ((innerDatdata.DatFormat & DatFormat.TSV) != 0 || (innerDatdata.DatFormat & DatFormat.CSV) != 0 @@ -2542,17 +2513,11 @@ namespace SabreTools.Library.DatFiles { // Clone each list of DATs in the dictionary List olditems = this[key]; - List newitems = new List(); - foreach (DatItem item in olditems) - { - newitems.Add((DatItem)item.Clone()); - } + List newitems = olditems.Select(i => (DatItem)i.Clone()).ToList(); // If the key is missing from the new dictionary, add it if (!sorted.ContainsKey(key)) - { sorted.Add(key, new List()); - } // Now add the list of items sorted[key].AddRange(newitems); @@ -2608,9 +2573,7 @@ namespace SabreTools.Library.DatFiles { // If the key mapping doesn't exist, add it if (!mapping.ContainsKey(item.MachineName)) - { mapping.TryAdd(item.MachineName, item.MachineDescription.Replace('/', '_').Replace("\"", "''").Replace(":", " -")); - } } }); @@ -2623,28 +2586,20 @@ namespace SabreTools.Library.DatFiles foreach (DatItem item in items) { // Update machine name - if (!String.IsNullOrWhiteSpace(item.MachineName) && mapping.ContainsKey(item.MachineName)) - { + if (!string.IsNullOrWhiteSpace(item.MachineName) && mapping.ContainsKey(item.MachineName)) item.MachineName = mapping[item.MachineName]; - } // Update cloneof - if (!String.IsNullOrWhiteSpace(item.CloneOf) && mapping.ContainsKey(item.CloneOf)) - { + if (!string.IsNullOrWhiteSpace(item.CloneOf) && mapping.ContainsKey(item.CloneOf)) item.CloneOf = mapping[item.CloneOf]; - } // Update romof - if (!String.IsNullOrWhiteSpace(item.RomOf) && mapping.ContainsKey(item.RomOf)) - { + if (!string.IsNullOrWhiteSpace(item.RomOf) && mapping.ContainsKey(item.RomOf)) item.RomOf = mapping[item.RomOf]; - } // Update sampleof - if (!String.IsNullOrWhiteSpace(item.SampleOf) && mapping.ContainsKey(item.SampleOf)) - { + if (!string.IsNullOrWhiteSpace(item.SampleOf) && mapping.ContainsKey(item.SampleOf)) item.SampleOf = mapping[item.SampleOf]; - } // Add the new item to the output list newItems.Add(item); @@ -2673,7 +2628,7 @@ namespace SabreTools.Library.DatFiles for (int i = 0; i < items.Count; i++) { string[] splitname = items[i].Name.Split('.'); - items[i].MachineName += "/" + string.Join(".", splitname.Take(splitname.Length > 1 ? splitname.Length - 1 : 1)); + items[i].MachineName += $"/{string.Join(".", splitname.Take(splitname.Length > 1 ? splitname.Length - 1 : 1))}"; } }); } @@ -2687,14 +2642,7 @@ namespace SabreTools.Library.DatFiles foreach (string key in keys) { List items = this[key]; - List newItems = new List(); - foreach (DatItem item in items) - { - if (!item.Remove) - { - newItems.Add(item); - } - } + List newItems = items.Where(i => !i.Remove).ToList(); Remove(key); AddRange(key, newItems); @@ -2721,13 +2669,10 @@ namespace SabreTools.Library.DatFiles { DatItem item = items[j]; if (Regex.IsMatch(item.MachineName, pattern)) - { item.MachineName = Regex.Replace(item.MachineName, pattern, "$2"); - } + if (Regex.IsMatch(item.MachineDescription, pattern)) - { item.MachineDescription = Regex.Replace(item.MachineDescription, pattern, "$2"); - } items[j] = item; } @@ -2859,28 +2804,20 @@ namespace SabreTools.Library.DatFiles { // If the game has no items in it, we want to continue if (this[game].Count == 0) - { continue; - } // Determine if the game has a parent or not string parent = null; - if (!String.IsNullOrWhiteSpace(this[game][0].RomOf)) - { + if (!string.IsNullOrWhiteSpace(this[game][0].RomOf)) parent = this[game][0].RomOf; - } // If the parent doesnt exist, we want to continue - if (String.IsNullOrWhiteSpace(parent)) - { + if (string.IsNullOrWhiteSpace(parent)) continue; - } // If the parent doesn't have any items, we want to continue if (this[parent].Count == 0) - { continue; - } // If the parent exists and has items, we copy the items from the parent to the current game DatItem copyFrom = this[game][0]; @@ -2890,9 +2827,7 @@ namespace SabreTools.Library.DatFiles DatItem datItem = (DatItem)item.Clone(); datItem.CopyMachineInformation(copyFrom); if (this[game].Where(i => i.Name == datItem.Name).Count() == 0 && !this[game].Contains(datItem)) - { Add(game, datItem); - } } } } @@ -2910,15 +2845,11 @@ namespace SabreTools.Library.DatFiles { // If the game doesn't have items, we continue if (this[game] == null || this[game].Count == 0) - { continue; - } // If the game (is/is not) a bios, we want to continue if (dev ^ (this[game][0].MachineType & MachineType.Device) != 0) - { continue; - } // If the game has no devices, we continue if (this[game][0].Devices == null @@ -2936,9 +2867,7 @@ namespace SabreTools.Library.DatFiles { // If the device doesn't exist then we continue if (this[device].Count == 0) - { continue; - } // Otherwise, copy the items from the device to the current game DatItem copyFrom = this[game][0]; @@ -2960,9 +2889,7 @@ namespace SabreTools.Library.DatFiles foreach (string device in newdevs) { if (!this[game][0].Devices.Contains(device)) - { this[game][0].Devices.Add(device); - } } // If we're checking slotoptions too @@ -2975,9 +2902,7 @@ namespace SabreTools.Library.DatFiles { // If the slotoption doesn't exist then we continue if (this[slotopt].Count == 0) - { continue; - } // Otherwise, copy the items from the slotoption to the current game DatItem copyFrom = this[game][0]; @@ -2999,9 +2924,7 @@ namespace SabreTools.Library.DatFiles foreach (string slotopt in newslotopts) { if (!this[game][0].SlotOptions.Contains(slotopt)) - { this[game][0].SlotOptions.Add(slotopt); - } } } } @@ -3019,28 +2942,20 @@ namespace SabreTools.Library.DatFiles { // If the game has no items in it, we want to continue if (this[game].Count == 0) - { continue; - } // Determine if the game has a parent or not string parent = null; - if (!String.IsNullOrWhiteSpace(this[game][0].CloneOf)) - { + if (!string.IsNullOrWhiteSpace(this[game][0].CloneOf)) parent = this[game][0].CloneOf; - } // If the parent doesnt exist, we want to continue - if (String.IsNullOrWhiteSpace(parent)) - { + if (string.IsNullOrWhiteSpace(parent)) continue; - } // If the parent doesn't have any items, we want to continue if (this[parent].Count == 0) - { continue; - } // If the parent exists and has items, we copy the items from the parent to the current game DatItem copyFrom = this[game][0]; @@ -3076,22 +2991,16 @@ namespace SabreTools.Library.DatFiles { // If the game has no items in it, we want to continue if (this[game].Count == 0) - { continue; - } // Determine if the game has a parent or not string parent = null; - if (!String.IsNullOrWhiteSpace(this[game][0].CloneOf)) - { + if (!string.IsNullOrWhiteSpace(this[game][0].CloneOf)) parent = this[game][0].CloneOf; - } // If there is no parent, then we continue - if (String.IsNullOrWhiteSpace(parent)) - { + if (string.IsNullOrWhiteSpace(parent)) continue; - } // Otherwise, move the items from the current game to a subfolder of the parent game DatItem copyFrom = this[parent].Count == 0 ? new Rom { MachineName = parent, MachineDescription = parent } : this[parent][0]; @@ -3109,7 +3018,7 @@ namespace SabreTools.Library.DatFiles else if (item.ItemType != ItemType.Disk && !this[parent].Contains(item)) { // Rename the child so it's in a subfolder - item.Name = item.MachineName + "\\" + item.Name; + item.Name = $"{item.MachineName}\\{item.Name}"; // Update the machine to be the new parent item.CopyMachineInformation(copyFrom); @@ -3153,34 +3062,24 @@ namespace SabreTools.Library.DatFiles { // If the game has no items in it, we want to continue if (this[game].Count == 0) - { continue; - } // If the game (is/is not) a bios, we want to continue if (bios ^ (this[game][0].MachineType & MachineType.Bios) != 0) - { continue; - } // Determine if the game has a parent or not string parent = null; - if (!String.IsNullOrWhiteSpace(this[game][0].RomOf)) - { + if (!string.IsNullOrWhiteSpace(this[game][0].RomOf)) parent = this[game][0].RomOf; - } // If the parent doesnt exist, we want to continue - if (String.IsNullOrWhiteSpace(parent)) - { + if (string.IsNullOrWhiteSpace(parent)) continue; - } // If the parent doesn't have any items, we want to continue if (this[parent].Count == 0) - { continue; - } // If the parent exists and has items, we remove the items that are in the parent from the current game List parentItems = this[parent]; @@ -3205,28 +3104,20 @@ namespace SabreTools.Library.DatFiles { // If the game has no items in it, we want to continue if (this[game].Count == 0) - { continue; - } // Determine if the game has a parent or not string parent = null; - if (!String.IsNullOrWhiteSpace(this[game][0].CloneOf)) - { + if (!string.IsNullOrWhiteSpace(this[game][0].CloneOf)) parent = this[game][0].CloneOf; - } // If the parent doesnt exist, we want to continue - if (String.IsNullOrWhiteSpace(parent)) - { + if (string.IsNullOrWhiteSpace(parent)) continue; - } // If the parent doesn't have any items, we want to continue if (this[parent].Count == 0) - { continue; - } // If the parent exists and has items, we remove the parent items from the current game List parentItems = this[parent]; @@ -3322,18 +3213,14 @@ namespace SabreTools.Library.DatFiles { // Check if we have a split path and get the filename accordingly if (filename.Contains("¬")) - { filename = filename.Split('¬')[0]; - } // Check the file extension first as a safeguard if (!Utilities.HasValidDatExtension(filename)) - { return; - } // If the output filename isn't set already, get the internal filename - FileName = (String.IsNullOrWhiteSpace(FileName) ? (keepext ? Path.GetFileName(filename) : Path.GetFileNameWithoutExtension(filename)) : FileName); + FileName = (string.IsNullOrWhiteSpace(FileName) ? (keepext ? Path.GetFileName(filename) : Path.GetFileNameWithoutExtension(filename)) : FileName); // If the output type isn't set already, get the internal output type DatFormat = (DatFormat == 0 ? Utilities.GetDatFormatFromFile(filename) : DatFormat); @@ -3346,20 +3233,16 @@ namespace SabreTools.Library.DatFiles } catch (Exception ex) { - Globals.Logger.Error("Error with file '{0}': {1}", filename, ex); + Globals.Logger.Error($"Error with file '{filename}': {ex}"); } // If we want to use descriptions as names, update everything if (descAsName) - { MachineDescriptionToName(); - } // If we are using tags from the DAT, set the proper input for split type unless overridden if (useTags && splitType == SplitType.None) - { splitType = Utilities.GetSplitType(ForceMerging); - } // Now we pre-process the DAT with the splitting/merging mode switch (splitType) @@ -3390,14 +3273,7 @@ namespace SabreTools.Library.DatFiles foreach (string key in Keys) { List items = this[key]; - List newitems = new List(); - foreach (DatItem item in items) - { - if (item.ItemType != ItemType.Blank) - { - newitems.Add(item); - } - } + List newitems = items.Where(i => i.ItemType != ItemType.Blank).ToList(); this.Remove(key); this.AddRange(key, newitems); @@ -3414,12 +3290,12 @@ namespace SabreTools.Library.DatFiles /// The key for the item public string ParseAddHelper(DatItem item, bool clean, bool remUnicode) { - string key = ""; + string key = string.Empty; // If there's no name in the rom, we log and skip it if (item.Name == null) { - Globals.Logger.Warning("{0}: Rom with no name found! Skipping...", FileName); + Globals.Logger.Warning($"{FileName}: Rom with no name found! Skipping..."); return key; } @@ -3450,21 +3326,21 @@ namespace SabreTools.Library.DatFiles // If we have the case where there is SHA-1 and nothing else, we don't fill in any other part of the data if (itemRom.Size == -1 - && String.IsNullOrWhiteSpace(itemRom.CRC) - && String.IsNullOrWhiteSpace(itemRom.MD5) - && String.IsNullOrWhiteSpace(itemRom.RIPEMD160) - && !String.IsNullOrWhiteSpace(itemRom.SHA1) - && String.IsNullOrWhiteSpace(itemRom.SHA256) - && String.IsNullOrWhiteSpace(itemRom.SHA384) - && String.IsNullOrWhiteSpace(itemRom.SHA512)) + && string.IsNullOrWhiteSpace(itemRom.CRC) + && string.IsNullOrWhiteSpace(itemRom.MD5) + && string.IsNullOrWhiteSpace(itemRom.RIPEMD160) + && !string.IsNullOrWhiteSpace(itemRom.SHA1) + && string.IsNullOrWhiteSpace(itemRom.SHA256) + && string.IsNullOrWhiteSpace(itemRom.SHA384) + && string.IsNullOrWhiteSpace(itemRom.SHA512)) { // No-op, just catch it so it doesn't go further - Globals.Logger.Verbose("{0}: Entry with only SHA-1 found - '{1}'", FileName, itemRom.Name); + Globals.Logger.Verbose($"{FileName}: Entry with only SHA-1 found - '{itemRom.Name}'"); } // If we have a rom and it's missing size AND the hashes match a 0-byte file, fill in the rest of the info else if ((itemRom.Size == 0 || itemRom.Size == -1) - && ((itemRom.CRC == Constants.CRCZero || String.IsNullOrWhiteSpace(itemRom.CRC)) + && ((itemRom.CRC == Constants.CRCZero || string.IsNullOrWhiteSpace(itemRom.CRC)) || itemRom.MD5 == Constants.MD5Zero || itemRom.RIPEMD160 == Constants.RIPEMD160Zero || itemRom.SHA1 == Constants.SHA1Zero @@ -3489,21 +3365,21 @@ namespace SabreTools.Library.DatFiles // If the file has no size and it's not the above case, skip and log else if (itemRom.ItemStatus != ItemStatus.Nodump && (itemRom.Size == 0 || itemRom.Size == -1)) { - Globals.Logger.Verbose("{0}: Incomplete entry for '{1}' will be output as nodump", FileName, itemRom.Name); + Globals.Logger.Verbose($"{FileName}: Incomplete entry for '{itemRom.Name}' will be output as nodump"); itemRom.ItemStatus = ItemStatus.Nodump; } // If the file has a size but aboslutely no hashes, skip and log else if (itemRom.ItemStatus != ItemStatus.Nodump && itemRom.Size > 0 - && String.IsNullOrWhiteSpace(itemRom.CRC) - && String.IsNullOrWhiteSpace(itemRom.MD5) - && String.IsNullOrWhiteSpace(itemRom.RIPEMD160) - && String.IsNullOrWhiteSpace(itemRom.SHA1) - && String.IsNullOrWhiteSpace(itemRom.SHA256) - && String.IsNullOrWhiteSpace(itemRom.SHA384) - && String.IsNullOrWhiteSpace(itemRom.SHA512)) + && string.IsNullOrWhiteSpace(itemRom.CRC) + && string.IsNullOrWhiteSpace(itemRom.MD5) + && string.IsNullOrWhiteSpace(itemRom.RIPEMD160) + && string.IsNullOrWhiteSpace(itemRom.SHA1) + && string.IsNullOrWhiteSpace(itemRom.SHA256) + && string.IsNullOrWhiteSpace(itemRom.SHA384) + && string.IsNullOrWhiteSpace(itemRom.SHA512)) { - Globals.Logger.Verbose("{0}: Incomplete entry for '{1}' will be output as nodump", FileName, itemRom.Name); + Globals.Logger.Verbose($"{FileName}: Incomplete entry for '{itemRom.Name}' will be output as nodump"); itemRom.ItemStatus = ItemStatus.Nodump; } @@ -3523,14 +3399,14 @@ namespace SabreTools.Library.DatFiles // If the file has aboslutely no hashes, skip and log if (itemDisk.ItemStatus != ItemStatus.Nodump - && String.IsNullOrWhiteSpace(itemDisk.MD5) - && String.IsNullOrWhiteSpace(itemDisk.RIPEMD160) - && String.IsNullOrWhiteSpace(itemDisk.SHA1) - && String.IsNullOrWhiteSpace(itemDisk.SHA256) - && String.IsNullOrWhiteSpace(itemDisk.SHA384) - && String.IsNullOrWhiteSpace(itemDisk.SHA512)) + && string.IsNullOrWhiteSpace(itemDisk.MD5) + && string.IsNullOrWhiteSpace(itemDisk.RIPEMD160) + && string.IsNullOrWhiteSpace(itemDisk.SHA1) + && string.IsNullOrWhiteSpace(itemDisk.SHA256) + && string.IsNullOrWhiteSpace(itemDisk.SHA384) + && string.IsNullOrWhiteSpace(itemDisk.SHA512)) { - Globals.Logger.Verbose("Incomplete entry for '{0}' will be output as nodump", itemDisk.Name); + Globals.Logger.Verbose($"Incomplete entry for '{itemDisk.Name}' will be output as nodump"); itemDisk.ItemStatus = ItemStatus.Nodump; } @@ -3603,23 +3479,23 @@ namespace SabreTools.Library.DatFiles bool addBlanks, bool addDate, string tempDir, bool copyFiles, string headerToCheckAgainst, bool chdsAsFiles, Filter filter) { // If the description is defined but not the name, set the name from the description - if (String.IsNullOrWhiteSpace(Name) && !String.IsNullOrWhiteSpace(Description)) + if (string.IsNullOrWhiteSpace(Name) && !string.IsNullOrWhiteSpace(Description)) { Name = Description; } // If the name is defined but not the description, set the description from the name - else if (!String.IsNullOrWhiteSpace(Name) && String.IsNullOrWhiteSpace(Description)) + else if (!string.IsNullOrWhiteSpace(Name) && string.IsNullOrWhiteSpace(Description)) { - Description = Name + (bare ? "" : " (" + Date + ")"); + Description = Name + (bare ? string.Empty : $" ({Date})"); } // If neither the name or description are defined, set them from the automatic values - else if (String.IsNullOrWhiteSpace(Name) && String.IsNullOrWhiteSpace(Description)) + else if (string.IsNullOrWhiteSpace(Name) && string.IsNullOrWhiteSpace(Description)) { - string[] splitpath = basePath.Split(Path.DirectorySeparatorChar); - Name = String.IsNullOrWhiteSpace(splitpath.Last()) ? splitpath[splitpath.Length - 2] : splitpath.Last(); - Description = Name + (bare ? "" : " (" + Date + ")"); + string[] splitpath = basePath.TrimEnd(Path.DirectorySeparatorChar).Split(Path.DirectorySeparatorChar); + Name = splitpath.Last(); + Description = Name + (bare ? string.Empty : $" ({Date})"); } // Clean the temp directory path @@ -3628,13 +3504,13 @@ namespace SabreTools.Library.DatFiles // Process the input if (Directory.Exists(basePath)) { - Globals.Logger.Verbose("Folder found: {0}", basePath); + Globals.Logger.Verbose($"Folder found: {basePath}"); // Process the files in the main folder or any subfolder List files = Directory.EnumerateFiles(basePath, "*", SearchOption.AllDirectories).ToList(); Parallel.ForEach(files, Globals.ParallelOptions, item => { - CheckFileForHashes(item, basePath, omitFromScan, bare, archivesAsFiles, skipFileType, + CheckFileForHashes(item, basePath, omitFromScan, archivesAsFiles, skipFileType, addBlanks, addDate, tempDir, copyFiles, headerToCheckAgainst, chdsAsFiles); }); @@ -3648,8 +3524,8 @@ namespace SabreTools.Library.DatFiles string fulldir = Path.GetFullPath(dir); // Set the temporary variables - string gamename = ""; - string romname = ""; + string gamename = string.Empty; + string romname = string.Empty; // If we have a SuperDAT, we want anything that's not the base path as the game, and the file as the rom if (Type == "SuperDAT") @@ -3666,46 +3542,28 @@ namespace SabreTools.Library.DatFiles } // Sanitize the names - if (gamename.StartsWith(Path.DirectorySeparatorChar.ToString())) - { - gamename = gamename.Substring(1); - } - if (gamename.EndsWith(Path.DirectorySeparatorChar.ToString())) - { - gamename = gamename.Substring(0, gamename.Length - 1); - } - if (romname.StartsWith(Path.DirectorySeparatorChar.ToString())) - { - romname = romname.Substring(1); - } - if (romname.EndsWith(Path.DirectorySeparatorChar.ToString())) - { - romname = romname.Substring(0, romname.Length - 1); - } + gamename = gamename.Trim(Path.DirectorySeparatorChar); + romname = romname.Trim(Path.DirectorySeparatorChar); - Globals.Logger.Verbose("Adding blank empty folder: {0}", gamename); + Globals.Logger.Verbose($"Adding blank empty folder: {gamename}"); this["null"].Add(new Rom(romname, gamename, omitFromScan)); }); } } else if (File.Exists(basePath)) { - CheckFileForHashes(basePath, Path.GetDirectoryName(Path.GetDirectoryName(basePath)), omitFromScan, bare, archivesAsFiles, + CheckFileForHashes(basePath, Path.GetDirectoryName(Path.GetDirectoryName(basePath)), omitFromScan, archivesAsFiles, skipFileType, addBlanks, addDate, tempDir, copyFiles, headerToCheckAgainst, chdsAsFiles); } // Now that we're done, delete the temp folder (if it's not the default) Globals.Logger.User("Cleaning temp folder"); if (tempDir != Path.GetTempPath()) - { Utilities.TryDeleteDirectory(tempDir); - } // If we have a valid filter, perform the filtering now if (filter != null && filter != default(Filter)) - { filter.FilterDatFile(this); - } return true; } @@ -3716,7 +3574,6 @@ namespace SabreTools.Library.DatFiles /// Filename of the item to be checked /// Base folder to be used in creating the DAT /// Hash flag saying what hashes should not be calculated - /// True if the date should be omitted from the DAT, false otherwise /// True if archives should be treated as files, false otherwise /// Type of files that should be skipped /// True if blank items should be created for empty folders, false otherwise @@ -3725,7 +3582,7 @@ namespace SabreTools.Library.DatFiles /// True if files should be copied to the temp directory before hashing, false otherwise /// Populated string representing the name of the skipper to use, a blank string to use the first available checker, null otherwise /// True if CHDs should be treated like regular files, false otherwise - private void CheckFileForHashes(string item, string basePath, Hash omitFromScan, bool bare, bool archivesAsFiles, + private void CheckFileForHashes(string item, string basePath, Hash omitFromScan, bool archivesAsFiles, SkipFileType skipFileType, bool addBlanks, bool addDate, string tempDir, bool copyFiles, string headerToCheckAgainst, bool chdsAsFiles) { // Special case for if we are in Romba mode (all names are supposed to be SHA-1 hashes) @@ -3740,11 +3597,11 @@ namespace SabreTools.Library.DatFiles // Add the list if it doesn't exist already Rom rom = new Rom(baseFile); Add(Utilities.GetKeyFromDatItem(rom, SortedBy.CRC), rom); - Globals.Logger.User("File added: {0}", Path.GetFileNameWithoutExtension(item) + Environment.NewLine); + Globals.Logger.User($"File added: {Path.GetFileNameWithoutExtension(item)}{Environment.NewLine}"); } else { - Globals.Logger.User("File not added: {0}", Path.GetFileNameWithoutExtension(item) + Environment.NewLine); + Globals.Logger.User($"File not added: {Path.GetFileNameWithoutExtension(item)}{Environment.NewLine}"); return; } @@ -3768,9 +3625,7 @@ namespace SabreTools.Library.DatFiles // If we have an archive and we're supposed to scan it if (archive != null && !archivesAsFiles) - { extracted = archive.GetChildren(omitFromScan: omitFromScan, date: addDate); - } // If the file should be skipped based on type, do so now if ((extracted != null && skipFileType == SkipFileType.Archive) @@ -3782,7 +3637,7 @@ namespace SabreTools.Library.DatFiles // If the extracted list is null, just scan the item itself if (extracted == null) { - ProcessFile(newItem, "", newBasePath, omitFromScan, addDate, headerToCheckAgainst, chdsAsFiles); + ProcessFile(newItem, string.Empty, newBasePath, omitFromScan, addDate, headerToCheckAgainst, chdsAsFiles); } // Otherwise, add all of the found items else @@ -3804,9 +3659,7 @@ namespace SabreTools.Library.DatFiles // Now get all blank folders from the archive if (archive != null) - { empties = archive.GetEmptyFolders(); - } // Add add all of the found empties to the DAT Parallel.ForEach(empties, Globals.ParallelOptions, empty => @@ -3822,9 +3675,7 @@ namespace SabreTools.Library.DatFiles // Cue to delete the file if it's a copy if (copyFiles && item != newItem) - { Utilities.TryDeleteDirectory(newBasePath); - } } /// @@ -3840,7 +3691,7 @@ namespace SabreTools.Library.DatFiles private void ProcessFile(string item, string parent, string basePath, Hash omitFromScan, bool addDate, string headerToCheckAgainst, bool chdsAsFiles) { - Globals.Logger.Verbose("'{0}' treated like a file", Path.GetFileName(item)); + Globals.Logger.Verbose($"'{Path.GetFileName(item)}' treated like a file"); BaseFile baseFile = Utilities.GetFileInfo(item, omitFromScan: omitFromScan, date: addDate, header: headerToCheckAgainst, chdsAsFiles: chdsAsFiles); ProcessFileHelper(item, Utilities.GetDatItem(baseFile), basePath, parent); } @@ -3856,17 +3707,13 @@ namespace SabreTools.Library.DatFiles { // If we somehow got something other than a Rom or Disk, cancel out if (datItem.ItemType != ItemType.Rom && datItem.ItemType != ItemType.Disk) - { return; - } try { - // If the basepath ends with a directory separator, remove it + // If the basepath doesn't end with a directory separator, add it if (!basepath.EndsWith(Path.DirectorySeparatorChar.ToString())) - { basepath += Path.DirectorySeparatorChar.ToString(); - } // Make sure we have the full item path item = Path.GetFullPath(item); @@ -3878,7 +3725,7 @@ namespace SabreTools.Library.DatFiles string key = Utilities.GetKeyFromDatItem(datItem, SortedBy.CRC); Add(key, datItem); - Globals.Logger.User("File added: {0}", datItem.Name + Environment.NewLine); + Globals.Logger.User($"File added: {datItem.Name}{Environment.NewLine}"); } catch (IOException ex) { @@ -3897,11 +3744,11 @@ namespace SabreTools.Library.DatFiles private void SetDatItemInfo(DatItem datItem, string item, string parent, string basepath) { // Get the data to be added as game and item names - string gamename = ""; - string romname = ""; + string gamename = string.Empty; + string romname = string.Empty; // If the parent is blank, then we have a non-archive file - if (String.IsNullOrWhiteSpace(parent)) + if (string.IsNullOrWhiteSpace(parent)) { // If we have a SuperDAT, we want anything that's not the base path as the game, and the file as the rom if (Type == "SuperDAT") @@ -3937,27 +3784,10 @@ namespace SabreTools.Library.DatFiles } // Sanitize the names - if (romname == null) - { - romname = ""; - } - if (gamename.StartsWith(Path.DirectorySeparatorChar.ToString())) - { - gamename = gamename.Substring(1); - } - if (gamename.EndsWith(Path.DirectorySeparatorChar.ToString())) - { - gamename = gamename.Substring(0, gamename.Length - 1); - } - if (romname.StartsWith(Path.DirectorySeparatorChar.ToString())) - { - romname = romname.Substring(1); - } - if (romname.EndsWith(Path.DirectorySeparatorChar.ToString())) - { - romname = romname.Substring(0, romname.Length - 1); - } - if (!String.IsNullOrWhiteSpace(gamename) && String.IsNullOrWhiteSpace(romname)) + gamename = gamename.Trim(Path.DirectorySeparatorChar); + romname = romname?.Trim(Path.DirectorySeparatorChar) ?? string.Empty; + + if (!string.IsNullOrWhiteSpace(gamename) && string.IsNullOrWhiteSpace(romname)) { romname = gamename; gamename = "Default"; @@ -3970,9 +3800,7 @@ namespace SabreTools.Library.DatFiles // If we have a Disk, then the ".chd" extension needs to be removed if (datItem.ItemType == ItemType.Disk) - { - datItem.Name = datItem.Name.Replace(".chd", ""); - } + datItem.Name = datItem.Name.Replace(".chd", string.Empty); } #endregion @@ -3988,12 +3816,18 @@ namespace SabreTools.Library.DatFiles /// True if input files should be deleted, false otherwise /// True if the DAT should be used as a filter instead of a template, false otherwise /// Output format that files should be written to - /// True if files should be output in Romba depot folders, false otherwise /// True if the updated DAT should be output, false otherwise /// Populated string representing the name of the skipper to use, a blank string to use the first available checker, null otherwise /// True if rebuilding was a success, false otherwise - public bool RebuildDepot(List inputs, string outDir, bool date, bool delete, - bool inverse, OutputFormat outputFormat, bool romba, bool updateDat, string headerToCheckAgainst) + public bool RebuildDepot( + List inputs, + string outDir, + bool date, + bool delete, + bool inverse, + OutputFormat outputFormat, + bool updateDat, + string headerToCheckAgainst) { #region Perform setup @@ -4030,7 +3864,7 @@ namespace SabreTools.Library.DatFiles #region Rebuild from depots in order - string format = ""; + string format = string.Empty; switch (outputFormat) { case OutputFormat.Folder: @@ -4043,6 +3877,7 @@ namespace SabreTools.Library.DatFiles format = "Torrent7Z"; break; case OutputFormat.TorrentGzip: + case OutputFormat.TorrentGzipRomba: format = "TorrentGZ"; break; case OutputFormat.TorrentLRZip: @@ -4059,7 +3894,7 @@ namespace SabreTools.Library.DatFiles break; } - InternalStopwatch watch = new InternalStopwatch("Rebuilding all files to {0}", format); + InternalStopwatch watch = new InternalStopwatch($"Rebuilding all files to {format}"); // Now loop through and get only directories from the input paths List directories = new List(); @@ -4068,7 +3903,7 @@ namespace SabreTools.Library.DatFiles // Add to the list if the input is a directory if (Directory.Exists(input)) { - Globals.Logger.Verbose("Adding depot: {0}", input); + Globals.Logger.Verbose($"Adding depot: {input}"); lock (directories) { directories.Add(input); @@ -4078,9 +3913,7 @@ namespace SabreTools.Library.DatFiles // If we don't have any directories, we want to exit if (directories.Count == 0) - { return success; - } // Now that we have a list of depots, we want to sort the input DAT by SHA-1 BucketBy(SortedBy.SHA1, DedupeType.None); @@ -4091,11 +3924,9 @@ namespace SabreTools.Library.DatFiles { // Pre-empt any issues that could arise from string length if (hash.Length != Constants.SHA1Length) - { continue; - } - Globals.Logger.User("Checking hash '{0}'", hash); + Globals.Logger.User($"Checking hash '{hash}'"); // Get the extension path for the hash string subpath = Utilities.GetRombaPath(hash); @@ -4113,9 +3944,7 @@ namespace SabreTools.Library.DatFiles // If we didn't find a path, then we continue if (foundpath == null) - { continue; - } // If we have a path, we want to try to get the rom information GZipArchive archive = new GZipArchive(foundpath); @@ -4123,21 +3952,13 @@ namespace SabreTools.Library.DatFiles // If the file information is null, then we continue if (fileinfo == null) - { continue; - } // Otherwise, we rebuild that file to all locations that we need to if (this[hash][0].ItemType == ItemType.Disk) - { - RebuildIndividualFile(new Disk(fileinfo), foundpath, outDir, date, inverse, outputFormat, romba, - updateDat, false /* isZip */, headerToCheckAgainst); - } + RebuildIndividualFile(new Disk(fileinfo), foundpath, outDir, date, inverse, outputFormat, updateDat, false /* isZip */, headerToCheckAgainst); else - { - RebuildIndividualFile(new Rom(fileinfo), foundpath, outDir, date, inverse, outputFormat, romba, - updateDat, false /* isZip */, headerToCheckAgainst); - } + RebuildIndividualFile(new Rom(fileinfo), foundpath, outDir, date, inverse, outputFormat, updateDat, false /* isZip */, headerToCheckAgainst); } watch.Stop(); @@ -4147,9 +3968,9 @@ namespace SabreTools.Library.DatFiles // If we're updating the DAT, output to the rebuild directory if (updateDat) { - FileName = "fixDAT_" + FileName; - Name = "fixDAT_" + Name; - Description = "fixDAT_" + Description; + FileName = $"fixDAT_{FileName}"; + Name = $"fixDAT_{Name}"; + Description = $"fixDAT_{Description}"; RemoveMarkedItems(); Write(outDir); } @@ -4167,15 +3988,23 @@ namespace SabreTools.Library.DatFiles /// True if input files should be deleted, false otherwise /// True if the DAT should be used as a filter instead of a template, false otherwise /// Output format that files should be written to - /// True if files should be output in Romba depot folders, false otherwise /// ArchiveScanLevel representing the archive handling levels /// True if the updated DAT should be output, false otherwise /// Populated string representing the name of the skipper to use, a blank string to use the first available checker, null otherwise /// True if CHDs should be treated like regular files, false otherwise /// True if rebuilding was a success, false otherwise - public bool RebuildGeneric(List inputs, string outDir, bool quickScan, bool date, - bool delete, bool inverse, OutputFormat outputFormat, bool romba, ArchiveScanLevel archiveScanLevel, bool updateDat, - string headerToCheckAgainst, bool chdsAsFiles) + public bool RebuildGeneric( + List inputs, + string outDir, + bool quickScan, + bool date, + bool delete, + bool inverse, + OutputFormat outputFormat, + ArchiveScanLevel archiveScanLevel, + bool updateDat, + string headerToCheckAgainst, + bool chdsAsFiles) { #region Perform setup @@ -4216,7 +4045,7 @@ namespace SabreTools.Library.DatFiles #region Rebuild from sources in order - string format = ""; + string format = string.Empty; switch (outputFormat) { case OutputFormat.Folder: @@ -4229,6 +4058,7 @@ namespace SabreTools.Library.DatFiles format = "Torrent7Z"; break; case OutputFormat.TorrentGzip: + case OutputFormat.TorrentGzipRomba: format = "TorrentGZ"; break; case OutputFormat.TorrentLRZip: @@ -4245,7 +4075,7 @@ namespace SabreTools.Library.DatFiles break; } - InternalStopwatch watch = new InternalStopwatch("Rebuilding all files to {0}", format); + InternalStopwatch watch = new InternalStopwatch($"Rebuilding all files to {format}"); // Now loop through all of the files in all of the inputs foreach (string input in inputs) @@ -4253,20 +4083,18 @@ namespace SabreTools.Library.DatFiles // If the input is a file if (File.Exists(input)) { - Globals.Logger.User("Checking file: {0}", input); - RebuildGenericHelper(input, outDir, quickScan, date, delete, inverse, - outputFormat, romba, archiveScanLevel, updateDat, headerToCheckAgainst, chdsAsFiles); + Globals.Logger.User($"Checking file: {input}"); + RebuildGenericHelper(input, outDir, quickScan, date, delete, inverse, outputFormat, archiveScanLevel, updateDat, headerToCheckAgainst, chdsAsFiles); } // If the input is a directory else if (Directory.Exists(input)) { - Globals.Logger.Verbose("Checking directory: {0}", input); + Globals.Logger.Verbose($"Checking directory: {input}"); foreach (string file in Directory.EnumerateFiles(input, "*", SearchOption.AllDirectories)) { - Globals.Logger.User("Checking file: {0}", file); - RebuildGenericHelper(file, outDir, quickScan, date, delete, inverse, - outputFormat, romba, archiveScanLevel, updateDat, headerToCheckAgainst, chdsAsFiles); + Globals.Logger.User($"Checking file: {file}"); + RebuildGenericHelper(file, outDir, quickScan, date, delete, inverse, outputFormat, archiveScanLevel, updateDat, headerToCheckAgainst, chdsAsFiles); } } } @@ -4278,9 +4106,9 @@ namespace SabreTools.Library.DatFiles // If we're updating the DAT, output to the rebuild directory if (updateDat) { - FileName = "fixDAT_" + FileName; - Name = "fixDAT_" + Name; - Description = "fixDAT_" + Description; + FileName = $"fixDAT_{FileName}"; + Name = $"fixDAT_{Name}"; + Description = $"fixDAT_{Description}"; RemoveMarkedItems(); Write(outDir); } @@ -4298,20 +4126,26 @@ namespace SabreTools.Library.DatFiles /// True if input files should be deleted, false otherwise /// True if the DAT should be used as a filter instead of a template, false otherwise /// Output format that files should be written to - /// True if files should be output in Romba depot folders, false otherwise /// ArchiveScanLevel representing the archive handling levels /// True if the updated DAT should be output, false otherwise /// Populated string representing the name of the skipper to use, a blank string to use the first available checker, null otherwise /// True if CHDs should be treated like regular files, false otherwise - private void RebuildGenericHelper(string file, string outDir, bool quickScan, bool date, - bool delete, bool inverse, OutputFormat outputFormat, bool romba, ArchiveScanLevel archiveScanLevel, bool updateDat, - string headerToCheckAgainst, bool chdsAsFiles) + private void RebuildGenericHelper( + string file, + string outDir, + bool quickScan, + bool date, + bool delete, + bool inverse, + OutputFormat outputFormat, + ArchiveScanLevel archiveScanLevel, + bool updateDat, + string headerToCheckAgainst, + bool chdsAsFiles) { // If we somehow have a null filename, return if (file == null) - { return; - } // Set the deletion variables bool usedExternally = false; @@ -4326,18 +4160,15 @@ namespace SabreTools.Library.DatFiles // TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually BaseFile fileinfo = Utilities.GetFileInfo(file, omitFromScan: (quickScan ? Hash.SecureHashes : Hash.DeepHashes), header: headerToCheckAgainst, chdsAsFiles: chdsAsFiles); + DatItem datItem = null; if (fileinfo.Type == FileType.CHD) - { datItem = new Disk(fileinfo); - } else if (fileinfo.Type == FileType.None) - { datItem = new Rom(fileinfo); - } usedExternally = RebuildIndividualFile(datItem, file, outDir, date, inverse, outputFormat, - romba, updateDat, null /* isZip */, headerToCheckAgainst); + updateDat, null /* isZip */, headerToCheckAgainst); } // If we're supposed to scan the file internally @@ -4366,18 +4197,14 @@ namespace SabreTools.Library.DatFiles { // TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually BaseFile fileinfo = Utilities.GetFileInfo(file, omitFromScan: (quickScan ? Hash.SecureHashes : Hash.DeepHashes), chdsAsFiles: chdsAsFiles); + DatItem datItem = null; if (fileinfo.Type == FileType.CHD) - { datItem = new Disk(fileinfo); - } else if (fileinfo.Type == FileType.None) - { datItem = new Rom(fileinfo); - } - usedExternally = RebuildIndividualFile(datItem, file, outDir, date, inverse, outputFormat, - romba, updateDat, null /* isZip */, headerToCheckAgainst); + usedExternally = RebuildIndividualFile(datItem, file, outDir, date, inverse, outputFormat, updateDat, null /* isZip */, headerToCheckAgainst); } // Otherwise, loop through the entries and try to match else @@ -4385,17 +4212,14 @@ namespace SabreTools.Library.DatFiles foreach (BaseFile entry in entries) { DatItem datItem = Utilities.GetDatItem(entry); - usedInternally |= RebuildIndividualFile(datItem, file, outDir, date, inverse, outputFormat, - romba, updateDat, !isTorrentGzip /* isZip */, headerToCheckAgainst); + usedInternally |= RebuildIndividualFile(datItem, file, outDir, date, inverse, outputFormat, updateDat, !isTorrentGzip /* isZip */, headerToCheckAgainst); } } } // If we are supposed to delete the file, do so if (delete && (usedExternally || usedInternally)) - { Utilities.TryDeleteFile(file); - } } /// @@ -4407,28 +4231,31 @@ namespace SabreTools.Library.DatFiles /// True if the date from the DAT should be used if available, false otherwise /// True if the DAT should be used as a filter instead of a template, false otherwise /// Output format that files should be written to - /// True if files should be output in Romba depot folders, false otherwise /// True if the updated DAT should be output, false otherwise /// True if the input file is an archive, false if the file is TGZ, null otherwise /// Populated string representing the name of the skipper to use, a blank string to use the first available checker, null otherwise /// True if the file was able to be rebuilt, false otherwise - private bool RebuildIndividualFile(DatItem datItem, string file, string outDir, bool date, - bool inverse, OutputFormat outputFormat, bool romba, bool updateDat, bool? isZip, string headerToCheckAgainst) + private bool RebuildIndividualFile( + DatItem datItem, + string file, + string outDir, + bool date, + bool inverse, + OutputFormat outputFormat, + bool updateDat, + bool? isZip, + string headerToCheckAgainst) { // Set the initial output value bool rebuilt = false; // If the DatItem is a Disk, force rebuilding to a folder except if TGZ - if (datItem.ItemType == ItemType.Disk && outputFormat != OutputFormat.TorrentGzip) - { + if (datItem.ItemType == ItemType.Disk && !(outputFormat == OutputFormat.TorrentGzip || outputFormat == OutputFormat.TorrentGzipRomba)) outputFormat = OutputFormat.Folder; - } // If we have a disk, change it into a Rom for later use if (datItem.ItemType == ItemType.Disk) - { datItem = ((Disk)datItem).ConvertToRom(); - } // Prepopluate a few key strings string crc = ((Rom)datItem).CRC ?? string.Empty; @@ -4445,26 +4272,20 @@ namespace SabreTools.Library.DatFiles // If we don't have any duplicates, continue if (dupes.Count == 0) - { return false; - } // If we have a very specific TGZ->TGZ case, just copy it accordingly GZipArchive tgz = new GZipArchive(file); BaseFile rom = tgz.GetTorrentGZFileInfo(); - if (isZip == false && rom != null && outputFormat == OutputFormat.TorrentGzip) + if (isZip == false && rom != null && (outputFormat == OutputFormat.TorrentGzip || outputFormat == OutputFormat.TorrentGzipRomba)) { - Globals.Logger.User("Matches found for '{0}', rebuilding accordingly...", Path.GetFileName(datItem.Name)); + Globals.Logger.User($"Matches found for '{Path.GetFileName(datItem.Name)}', rebuilding accordingly..."); // Get the proper output path - if (romba) - { + if (outputFormat == OutputFormat.TorrentGzipRomba) outDir = Path.Combine(outDir, Utilities.GetRombaPath(sha1)); - } else - { outDir = Path.Combine(outDir, sha1 + ".gz"); - } // Make sure the output folder is created Directory.CreateDirectory(Path.GetDirectoryName(outDir)); @@ -4490,9 +4311,7 @@ namespace SabreTools.Library.DatFiles string realName = null; BaseArchive archive = Utilities.GetArchive(file); if (archive != null) - { (fileStream, realName) = archive.CopyToStream(datItem.Name); - } } // Otherwise, just open the filestream else @@ -4502,15 +4321,13 @@ namespace SabreTools.Library.DatFiles // If the stream is null, then continue if (fileStream == null) - { return false; - } // Seek to the beginning of the stream if (fileStream.CanSeek) fileStream.Seek(0, SeekOrigin.Begin); - Globals.Logger.User("Matches found for '{0}', rebuilding accordingly...", Path.GetFileName(datItem.Name)); + Globals.Logger.User($"Matches found for '{Path.GetFileName(datItem.Name)}', rebuilding accordingly..."); rebuilt = true; // Now loop through the list and rebuild accordingly @@ -4520,7 +4337,7 @@ namespace SabreTools.Library.DatFiles Folder outputArchive = Utilities.GetArchive(outputFormat); // Now rebuild to the output file - outputArchive.Write(fileStream, outDir, (Rom)item, date: date, romba: romba); + outputArchive.Write(fileStream, outDir, (Rom)item, date: date, romba: outputFormat == OutputFormat.TorrentGzipRomba); } // Close the input stream @@ -4537,17 +4354,13 @@ namespace SabreTools.Library.DatFiles BaseFile rom = tgz.GetTorrentGZFileInfo(); if (isZip == false && rom != null && outputFormat == OutputFormat.TorrentGzip) { - Globals.Logger.User("Matches found for '{0}', rebuilding accordingly...", Path.GetFileName(datItem.Name)); + Globals.Logger.User($"Matches found for '{Path.GetFileName(datItem.Name)}', rebuilding accordingly..."); // Get the proper output path - if (romba) - { + if (outputFormat == OutputFormat.TorrentGzipRomba) outDir = Path.Combine(outDir, Utilities.GetRombaPath(sha1)); - } else - { outDir = Path.Combine(outDir, sha1 + ".gz"); - } // Make sure the output folder is created Directory.CreateDirectory(Path.GetDirectoryName(outDir)); @@ -4573,9 +4386,7 @@ namespace SabreTools.Library.DatFiles string realName = null; BaseArchive archive = Utilities.GetArchive(file); if (archive != null) - { (fileStream, realName) = archive.CopyToStream(datItem.Name); - } } // Otherwise, just open the filestream else @@ -4585,9 +4396,7 @@ namespace SabreTools.Library.DatFiles // If the stream is null, then continue if (fileStream == null) - { return false; - } // Get the item from the current file Rom item = new Rom(Utilities.GetStreamInfo(fileStream, fileStream.Length, keepReadOpen: true)); @@ -4601,7 +4410,7 @@ namespace SabreTools.Library.DatFiles item.MachineDescription = machinename; } - Globals.Logger.User("No matches found for '{0}', rebuilding accordingly from inverse flag...", Path.GetFileName(datItem.Name)); + Globals.Logger.User($"No matches found for '{Path.GetFileName(datItem.Name)}', rebuilding accordingly from inverse flag..."); // Get the output archive, if possible Folder outputArchive = Utilities.GetArchive(outputFormat); @@ -4628,12 +4437,11 @@ namespace SabreTools.Library.DatFiles writeStream.Write(ibuffer, 0, ilen); writeStream.Flush(); } + writeStream.Dispose(); - if (date && !String.IsNullOrWhiteSpace(item.Date)) - { + if (date && !string.IsNullOrWhiteSpace(item.Date)) File.SetCreationTime(outfile, DateTime.Parse(item.Date)); - } rebuilt &= true; } @@ -4644,7 +4452,7 @@ namespace SabreTools.Library.DatFiles } else { - rebuilt &= outputArchive.Write(fileStream, outDir, item, date: date, romba: romba); + rebuilt &= outputArchive.Write(fileStream, outDir, item, date: date, romba: outputFormat == OutputFormat.TorrentGzipRomba); } // Close the input stream @@ -4663,9 +4471,7 @@ namespace SabreTools.Library.DatFiles string realName = null; BaseArchive archive = Utilities.GetArchive(file); if (archive != null) - { (fileStream, realName) = archive.CopyToStream(datItem.Name); - } } // Otherwise, just open the filestream else @@ -4675,9 +4481,7 @@ namespace SabreTools.Library.DatFiles // If the stream is null, then continue if (fileStream == null) - { return false; - } // Check to see if we have a matching header first SkipperRule rule = Skipper.GetMatchingRule(fileStream, Path.GetFileNameWithoutExtension(headerToCheckAgainst)); @@ -4703,11 +4507,9 @@ namespace SabreTools.Library.DatFiles // If we don't have any duplicates, continue if (dupes.Count == 0) - { return false; - } - Globals.Logger.User("Headerless matches found for '{0}', rebuilding accordingly...", Path.GetFileName(datItem.Name)); + Globals.Logger.User($"Headerless matches found for '{Path.GetFileName(datItem.Name)}', rebuilding accordingly..."); rebuilt = true; // Now loop through the list and rebuild accordingly @@ -4715,7 +4517,7 @@ namespace SabreTools.Library.DatFiles { // Create a headered item to use as well datItem.CopyMachineInformation(item); - datItem.Name += "_" + crc; + datItem.Name += $"_{crc}"; // If either copy succeeds, then we want to set rebuilt to true bool eitherSuccess = false; @@ -4724,8 +4526,8 @@ namespace SabreTools.Library.DatFiles Folder outputArchive = Utilities.GetArchive(outputFormat); // Now rebuild to the output file - eitherSuccess |= outputArchive.Write(transformStream, outDir, (Rom)item, date: date, romba: romba); - eitherSuccess |= outputArchive.Write(fileStream, outDir, (Rom)datItem, date: date, romba: romba); + eitherSuccess |= outputArchive.Write(transformStream, outDir, (Rom)item, date: date, romba: outputFormat == OutputFormat.TorrentGzipRomba); + eitherSuccess |= outputArchive.Write(fileStream, outDir, (Rom)datItem, date: date, romba: outputFormat == OutputFormat.TorrentGzipRomba); // Now add the success of either rebuild rebuilt &= eitherSuccess; @@ -4763,16 +4565,14 @@ namespace SabreTools.Library.DatFiles // Add to the list if the input is a directory if (Directory.Exists(input)) { - Globals.Logger.Verbose("Adding depot: {0}", input); + Globals.Logger.Verbose($"Adding depot: {input}"); directories.Add(input); } } // If we don't have any directories, we want to exit if (directories.Count == 0) - { return success; - } // Now that we have a list of depots, we want to sort the input DAT by SHA-1 BucketBy(SortedBy.SHA1, DedupeType.None); @@ -4783,11 +4583,9 @@ namespace SabreTools.Library.DatFiles { // Pre-empt any issues that could arise from string length if (hash.Length != Constants.SHA1Length) - { continue; - } - Globals.Logger.User("Checking hash '{0}'", hash); + Globals.Logger.User($"Checking hash '{hash}'"); // Get the extension path for the hash string subpath = Utilities.GetRombaPath(hash); @@ -4805,9 +4603,7 @@ namespace SabreTools.Library.DatFiles // If we didn't find a path, then we continue if (foundpath == null) - { continue; - } // If we have a path, we want to try to get the rom information GZipArchive tgz = new GZipArchive(foundpath); @@ -4815,9 +4611,7 @@ namespace SabreTools.Library.DatFiles // If the file information is null, then we continue if (fileinfo == null) - { continue; - } // Now we want to remove all duplicates from the DAT new Rom(fileinfo).GetDuplicates(this, remove: true) @@ -4827,9 +4621,9 @@ namespace SabreTools.Library.DatFiles watch.Stop(); // If there are any entries in the DAT, output to the rebuild directory - FileName = "fixDAT_" + FileName; - Name = "fixDAT_" + Name; - Description = "fixDAT_" + Description; + FileName = $"fixDAT_{FileName}"; + Name = $"fixDAT_{Name}"; + Description = $"fixDAT_{Description}"; RemoveMarkedItems(); Write(); @@ -4857,15 +4651,15 @@ namespace SabreTools.Library.DatFiles { // TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually PopulateFromDir(input, (quickScan ? Hash.SecureHashes : Hash.DeepHashes) /* omitFromScan */, true /* bare */, false /* archivesAsFiles */, - SkipFileType.None, false /* addBlanks */, false /* addDate */, "" /* tempDir */, false /* copyFiles */, headerToCheckAgainst, chdsAsFiles, filter); + SkipFileType.None, false /* addBlanks */, false /* addDate */, string.Empty /* tempDir */, false /* copyFiles */, headerToCheckAgainst, chdsAsFiles, filter); } // Setup the fixdat DatFile matched = new DatFile(this); matched.ResetDictionary(); - matched.FileName = "fixDat_" + matched.FileName; - matched.Name = "fixDat_" + matched.Name; - matched.Description = "fixDat_" + matched.Description; + matched.FileName = $"fixDat_{matched.FileName}"; + matched.Name = $"fixDat_{matched.Name}"; + matched.Description = $"fixDat_{matched.Description}"; matched.DatFormat = DatFormat.Logiqx; // If we are checking hashes only, essentially diff the inputs @@ -4883,9 +4677,7 @@ namespace SabreTools.Library.DatFiles if (rom.SourceID == 99) { if (rom.ItemType == ItemType.Disk || rom.ItemType == ItemType.Rom) - { matched.Add(((Disk)rom).SHA1, rom); - } } } } @@ -4900,9 +4692,7 @@ namespace SabreTools.Library.DatFiles foreach (Rom rom in newroms) { if (rom.SourceID == 99) - { - matched.Add(rom.Size + "-" + rom.CRC, rom); - } + matched.Add($"{rom.Size}-{rom.CRC}", rom); } } } @@ -4935,9 +4725,7 @@ namespace SabreTools.Library.DatFiles { // If we somehow have the "none" split type, return if (splittingMode == SplittingMode.None) - { return; - } // Get only files from the inputs List files = Utilities.GetOnlyFilesFromInputs(inputs, appendparent: true); @@ -4953,25 +4741,19 @@ namespace SabreTools.Library.DatFiles // Split and write the DAT if ((splittingMode & SplittingMode.Extension) != 0) - { SplitByExtension(outDir, exta, extb); - } + if ((splittingMode & SplittingMode.Hash) != 0) - { SplitByHash(outDir); - } + if ((splittingMode & SplittingMode.Level) != 0) - { SplitByLevel(outDir, shortname, basedat); - } + if ((splittingMode & SplittingMode.Size) != 0) - { SplitBySize(outDir, radix); - } + if ((splittingMode & SplittingMode.Type) != 0) - { SplitByType(outDir); - } // Now re-empty the DAT to make room for the next one DatFormat tempFormat = DatFormat; @@ -4990,27 +4772,23 @@ namespace SabreTools.Library.DatFiles /// True if split succeeded, false otherwise public bool SplitByExtension(string outDir, List extA, List extB) { - // Make sure all of the extensions have a dot at the beginning - List newExtA = new List(); - foreach (string s in extA) - { - newExtA.Add((s.StartsWith(".") ? s.Substring(1) : s).ToUpperInvariant()); - } + // If roms is empty, return false + if (Count == 0) + return false; + + // Make sure all of the extensions don't have a dot at the beginning + var newExtA = extA.Select(s => s.TrimStart('.').ToUpperInvariant()); string newExtAString = string.Join(",", newExtA); - List newExtB = new List(); - foreach (string s in extB) - { - newExtB.Add((s.StartsWith(".") ? s.Substring(1) : s).ToUpperInvariant()); - } + var newExtB = extB.Select(s => s.TrimStart('.').ToUpperInvariant()); string newExtBString = string.Join(",", newExtB); // Set all of the appropriate outputs for each of the subsets DatFile datdataA = new DatFile { - FileName = this.FileName + " (" + newExtAString + ")", - Name = this.Name + " (" + newExtAString + ")", - Description = this.Description + " (" + newExtAString + ")", + FileName = $"{this.FileName} ({newExtAString})", + Name = $"{this.Name} ({newExtAString})", + Description = $"{this.Description} ({newExtAString})", Category = this.Category, Version = this.Version, Date = this.Date, @@ -5023,9 +4801,9 @@ namespace SabreTools.Library.DatFiles }; DatFile datdataB = new DatFile { - FileName = this.FileName + " (" + newExtBString + ")", - Name = this.Name + " (" + newExtBString + ")", - Description = this.Description + " (" + newExtBString + ")", + FileName = $"{this.FileName} ({newExtBString})", + Name = $"{this.Name} ({newExtBString})", + Description = $"{this.Description} ({newExtBString})", Category = this.Category, Version = this.Version, Date = this.Date, @@ -5037,12 +4815,6 @@ namespace SabreTools.Library.DatFiles DatFormat = this.DatFormat, }; - // If roms is empty, return false - if (Count == 0) - { - return false; - } - // Now separate the roms accordingly List keys = Keys; Parallel.ForEach(keys, Globals.ParallelOptions, key => @@ -5281,9 +5053,7 @@ namespace SabreTools.Library.DatFiles { // If the file is not a Rom or Disk, continue if (item.ItemType != ItemType.Disk && item.ItemType != ItemType.Rom) - { return; - } // If the file is a nodump if ((item.ItemType == ItemType.Rom && ((Rom)item).ItemStatus == ItemStatus.Nodump) @@ -5292,43 +5062,43 @@ namespace SabreTools.Library.DatFiles nodump.Add(key, item); } // If the file has a SHA-512 - else if ((item.ItemType == ItemType.Rom && !String.IsNullOrWhiteSpace(((Rom)item).SHA512)) - || (item.ItemType == ItemType.Disk && !String.IsNullOrWhiteSpace(((Disk)item).SHA512))) + else if ((item.ItemType == ItemType.Rom && !string.IsNullOrWhiteSpace(((Rom)item).SHA512)) + || (item.ItemType == ItemType.Disk && !string.IsNullOrWhiteSpace(((Disk)item).SHA512))) { sha512.Add(key, item); } // If the file has a SHA-384 - else if ((item.ItemType == ItemType.Rom && !String.IsNullOrWhiteSpace(((Rom)item).SHA384)) - || (item.ItemType == ItemType.Disk && !String.IsNullOrWhiteSpace(((Disk)item).SHA384))) + else if ((item.ItemType == ItemType.Rom && !string.IsNullOrWhiteSpace(((Rom)item).SHA384)) + || (item.ItemType == ItemType.Disk && !string.IsNullOrWhiteSpace(((Disk)item).SHA384))) { sha384.Add(key, item); } // If the file has a SHA-256 - else if ((item.ItemType == ItemType.Rom && !String.IsNullOrWhiteSpace(((Rom)item).SHA256)) - || (item.ItemType == ItemType.Disk && !String.IsNullOrWhiteSpace(((Disk)item).SHA256))) + else if ((item.ItemType == ItemType.Rom && !string.IsNullOrWhiteSpace(((Rom)item).SHA256)) + || (item.ItemType == ItemType.Disk && !string.IsNullOrWhiteSpace(((Disk)item).SHA256))) { sha256.Add(key, item); } // If the file has a SHA-1 - else if ((item.ItemType == ItemType.Rom && !String.IsNullOrWhiteSpace(((Rom)item).SHA1)) - || (item.ItemType == ItemType.Disk && !String.IsNullOrWhiteSpace(((Disk)item).SHA1))) + else if ((item.ItemType == ItemType.Rom && !string.IsNullOrWhiteSpace(((Rom)item).SHA1)) + || (item.ItemType == ItemType.Disk && !string.IsNullOrWhiteSpace(((Disk)item).SHA1))) { sha1.Add(key, item); } // If the file has a SHA-1 - else if ((item.ItemType == ItemType.Rom && !String.IsNullOrWhiteSpace(((Rom)item).RIPEMD160)) - || (item.ItemType == ItemType.Disk && !String.IsNullOrWhiteSpace(((Disk)item).RIPEMD160))) + else if ((item.ItemType == ItemType.Rom && !string.IsNullOrWhiteSpace(((Rom)item).RIPEMD160)) + || (item.ItemType == ItemType.Disk && !string.IsNullOrWhiteSpace(((Disk)item).RIPEMD160))) { ripemd160.Add(key, item); } // If the file has an MD5 - else if ((item.ItemType == ItemType.Rom && !String.IsNullOrWhiteSpace(((Rom)item).MD5)) - || (item.ItemType == ItemType.Disk && !String.IsNullOrWhiteSpace(((Disk)item).MD5))) + else if ((item.ItemType == ItemType.Rom && !string.IsNullOrWhiteSpace(((Rom)item).MD5)) + || (item.ItemType == ItemType.Disk && !string.IsNullOrWhiteSpace(((Disk)item).MD5))) { md5.Add(key, item); } // If the file has a CRC - else if ((item.ItemType == ItemType.Rom && !String.IsNullOrWhiteSpace(((Rom)item).CRC))) + else if ((item.ItemType == ItemType.Rom && !string.IsNullOrWhiteSpace(((Rom)item).CRC))) { crc.Add(key, item); } @@ -5401,6 +5171,8 @@ namespace SabreTools.Library.DatFiles tempDat.Name = Path.GetDirectoryName(key); }); + // TODO: Investigate why this method seems incomplete + return true; } @@ -5417,9 +5189,8 @@ namespace SabreTools.Library.DatFiles int bdeep = b.Count(c => c == '/' || c == '\\'); if (adeep == bdeep) - { return nc.Compare(a, b); - } + return adeep - bdeep; } @@ -5437,16 +5208,16 @@ namespace SabreTools.Library.DatFiles string expName = name.Replace("/", " - ").Replace("\\", " - "); // Now set the new output values - datFile.FileName = WebUtility.HtmlDecode(String.IsNullOrWhiteSpace(name) + datFile.FileName = WebUtility.HtmlDecode(string.IsNullOrWhiteSpace(name) ? FileName : (shortname ? Path.GetFileName(name) : expName ) ); - datFile.FileName = (restore ? FileName + " (" + datFile.FileName + ")" : datFile.FileName); - datFile.Name = Name + " (" + expName + ")"; - datFile.Description = (String.IsNullOrWhiteSpace(Description) ? datFile.Name : Description + " (" + expName + ")"); + datFile.FileName = (restore ? $"{FileName} ({datFile.FileName})" : datFile.FileName); + datFile.Name = $"{Name} ({expName})"; + datFile.Description = (string.IsNullOrWhiteSpace(Description) ? datFile.Name : $"{Description} ({expName})"); datFile.Type = null; // Write out the temporary DAT to the proper directory @@ -5465,9 +5236,9 @@ namespace SabreTools.Library.DatFiles Globals.Logger.User("Creating and populating new DATs"); DatFile lessDat = new DatFile { - FileName = this.FileName + " (less than " + radix + " )", - Name = this.Name + " (less than " + radix + " )", - Description = this.Description + " (less than " + radix + " )", + FileName = $"{this.FileName} (less than {radix})", + Name = $"{this.Name} (less than {radix})", + Description = $"{this.Description} (less than {radix})", Category = this.Category, Version = this.Version, Date = this.Date, @@ -5486,9 +5257,9 @@ namespace SabreTools.Library.DatFiles }; DatFile greaterEqualDat = new DatFile { - FileName = this.FileName + " (equal-greater than " + radix + " )", - Name = this.Name + " (equal-greater than " + radix + " )", - Description = this.Description + " (equal-greater than " + radix + " )", + FileName = $"{this.FileName} (equal-greater than {radix})", + Name = $"{this.Name} (equal-greater than {radix})", + Description = $"{this.Description} (equal-greater than {radix})", Category = this.Category, Version = this.Version, Date = this.Date, @@ -5515,19 +5286,15 @@ namespace SabreTools.Library.DatFiles { // If the file is not a Rom, it automatically goes in the "lesser" dat if (item.ItemType != ItemType.Rom) - { lessDat.Add(key, item); - } + // If the file is a Rom and less than the radix, put it in the "lesser" dat else if (item.ItemType == ItemType.Rom && ((Rom)item).Size < radix) - { lessDat.Add(key, item); - } + // If the file is a Rom and greater than or equal to the radix, put it in the "greater" dat else if (item.ItemType == ItemType.Rom && ((Rom)item).Size >= radix) - { greaterEqualDat.Add(key, item); - } } }); @@ -5622,19 +5389,15 @@ namespace SabreTools.Library.DatFiles { // If the file is a Rom if (item.ItemType == ItemType.Rom) - { romdat.Add(key, item); - } + // If the file is a Disk else if (item.ItemType == ItemType.Disk) - { diskdat.Add(key, item); - } + // If the file is a Sample else if (item.ItemType == ItemType.Sample) - { sampledat.Add(key, item); - } } }); @@ -5663,42 +5426,31 @@ namespace SabreTools.Library.DatFiles { // If we're supposed to recalculate the statistics, do so if (recalculate) - { RecalculateStats(); - } BucketBy(SortedBy.Game, DedupeType.None, norename: true); if (TotalSize < 0) - { TotalSize = Int64.MaxValue + TotalSize; - } // Log the results to screen - string results = @"For '" + FileName + @"': --------------------------------------------------- - Uncompressed size: " + Utilities.GetBytesReadable(TotalSize) + @" - Games found: " + (game == -1 ? Keys.Count() : game) + @" - Roms found: " + RomCount + @" - Disks found: " + DiskCount + @" - Roms with CRC: " + CRCCount + @" - Roms with MD5: " + MD5Count + @" - Roms with RIPEMD160: " + RIPEMD160Count + @" - Roms with SHA-1: " + SHA1Count + @" - Roms with SHA-256: " + SHA256Count + @" - Roms with SHA-384: " + SHA384Count + @" - Roms with SHA-512: " + SHA512Count + "\n"; - - if (baddumpCol) - { - results += " Roms with BadDump status: " + BaddumpCount + "\n"; - } - if (nodumpCol) - { - results += " Roms with Nodump status: " + NodumpCount + "\n"; - } + string results = $"For '{FileName}':{Environment.NewLine}" + + $"--------------------------------------------------{Environment.NewLine}" + + $" Uncompressed size: {Utilities.GetBytesReadable(TotalSize)}{Environment.NewLine}" + + $" Games found: {(game == -1 ? Keys.Count() : game)}{Environment.NewLine}" + + $" Roms found: {RomCount}{Environment.NewLine}" + + $" Disks found: {DiskCount}{Environment.NewLine}" + + $" Roms with CRC: {CRCCount}{Environment.NewLine}" + + $" Roms with MD5: {MD5Count}{Environment.NewLine}" + + $" Roms with RIPEMD160: {RIPEMD160Count}{Environment.NewLine}" + + $" Roms with SHA-1: {SHA1Count}{Environment.NewLine}" + + $" Roms with SHA-256: {SHA256Count}{Environment.NewLine}" + + $" Roms with SHA-384: {SHA384Count}{Environment.NewLine}" + + $" Roms with SHA-512: {SHA512Count}{Environment.NewLine}" + + (baddumpCol ? $" Roms with BadDump status: {BaddumpCount}{Environment.NewLine}" : string.Empty) + + (nodumpCol ? $" Roms with Nodump status: {NodumpCount}{Environment.NewLine}" : string.Empty); // For spacing between DATs - results += "\n\n"; + results += $"{Environment.NewLine}{Environment.NewLine}"; Globals.Logger.User(results); } @@ -5713,9 +5465,7 @@ namespace SabreTools.Library.DatFiles // If we have a blank Dat in any way, return if (this == null || Count == 0) - { return; - } // Loop through and add List keys = Keys; @@ -5762,72 +5512,62 @@ namespace SabreTools.Library.DatFiles } // Make sure that the three essential fields are filled in - if (String.IsNullOrWhiteSpace(FileName) && String.IsNullOrWhiteSpace(Name) && String.IsNullOrWhiteSpace(Description)) + if (string.IsNullOrWhiteSpace(FileName) && string.IsNullOrWhiteSpace(Name) && string.IsNullOrWhiteSpace(Description)) { FileName = Name = Description = "Default"; } - else if (String.IsNullOrWhiteSpace(FileName) && String.IsNullOrWhiteSpace(Name) && !String.IsNullOrWhiteSpace(Description)) + else if (string.IsNullOrWhiteSpace(FileName) && string.IsNullOrWhiteSpace(Name) && !string.IsNullOrWhiteSpace(Description)) { FileName = Name = Description; } - else if (String.IsNullOrWhiteSpace(FileName) && !String.IsNullOrWhiteSpace(Name) && String.IsNullOrWhiteSpace(Description)) + else if (string.IsNullOrWhiteSpace(FileName) && !string.IsNullOrWhiteSpace(Name) && string.IsNullOrWhiteSpace(Description)) { FileName = Description = Name; } - else if (String.IsNullOrWhiteSpace(FileName) && !String.IsNullOrWhiteSpace(Name) && !String.IsNullOrWhiteSpace(Description)) + else if (string.IsNullOrWhiteSpace(FileName) && !string.IsNullOrWhiteSpace(Name) && !string.IsNullOrWhiteSpace(Description)) { FileName = Description; } - else if (!String.IsNullOrWhiteSpace(FileName) && String.IsNullOrWhiteSpace(Name) && String.IsNullOrWhiteSpace(Description)) + else if (!string.IsNullOrWhiteSpace(FileName) && string.IsNullOrWhiteSpace(Name) && string.IsNullOrWhiteSpace(Description)) { Name = Description = FileName; } - else if (!String.IsNullOrWhiteSpace(FileName) && String.IsNullOrWhiteSpace(Name) && !String.IsNullOrWhiteSpace(Description)) + else if (!string.IsNullOrWhiteSpace(FileName) && string.IsNullOrWhiteSpace(Name) && !string.IsNullOrWhiteSpace(Description)) { Name = Description; } - else if (!String.IsNullOrWhiteSpace(FileName) && !String.IsNullOrWhiteSpace(Name) && String.IsNullOrWhiteSpace(Description)) + else if (!string.IsNullOrWhiteSpace(FileName) && !string.IsNullOrWhiteSpace(Name) && string.IsNullOrWhiteSpace(Description)) { Description = Name; } - else if (!String.IsNullOrWhiteSpace(FileName) && !String.IsNullOrWhiteSpace(Name) && !String.IsNullOrWhiteSpace(Description)) + else if (!string.IsNullOrWhiteSpace(FileName) && !string.IsNullOrWhiteSpace(Name) && !string.IsNullOrWhiteSpace(Description)) { // Nothing is needed } // Output initial statistics, for kicks if (stats) - { WriteStatsToScreen(recalculate: (RomCount + DiskCount == 0), baddumpCol: true, nodumpCol: true); - } // Run the one rom per game logic, if required if (OneRom) - { OneRomPerGame(); - } // Bucket and dedupe according to the flag if (DedupeRoms == DedupeType.Full) - { BucketBy(SortedBy.CRC, DedupeRoms, norename: norename); - } else if (DedupeRoms == DedupeType.Game) - { BucketBy(SortedBy.Game, DedupeRoms, norename: norename); - } // Bucket roms by game name, if not already BucketBy(SortedBy.Game, DedupeType.None, norename: norename); // Output the number of items we're going to be writing - Globals.Logger.User("A total of {0} items will be written out to '{1}'", Count, FileName); + Globals.Logger.User($"A total of {Count} items will be written out to '{FileName}'"); // If we are removing scene dates, do that now if (SceneDateStrip) - { StripSceneDatesFromItems(); - } // Get the outfile names Dictionary outfiles = CreateOutfileNames(outDir, overwrite); @@ -5844,7 +5584,7 @@ namespace SabreTools.Library.DatFiles } catch (Exception ex) { - Globals.Logger.Error("Datfile {0} could not be written out: {1}", outfile, ex.ToString()); + Globals.Logger.Error($"Datfile {outfile} could not be written out: {ex}"); } }); @@ -5882,9 +5622,7 @@ namespace SabreTools.Library.DatFiles // Double check the outDir for the end delim if (!outDir.EndsWith(Path.DirectorySeparatorChar.ToString())) - { outDir += Path.DirectorySeparatorChar; - } // Get the extensions from the output type @@ -5925,9 +5663,9 @@ namespace SabreTools.Library.DatFiles { outfileNames.Add(DatFormat.Logiqx, CreateOutfileNamesHelper(outDir, ".xml", overwrite)); } - if ((DatFormat & DatFormat.LogiqxDepreciated) != 0) + if ((DatFormat & DatFormat.LogiqxDeprecated) != 0) { - outfileNames.Add(DatFormat.LogiqxDepreciated, CreateOutfileNamesHelper(outDir, ".xml", overwrite)); + outfileNames.Add(DatFormat.LogiqxDeprecated, CreateOutfileNamesHelper(outDir, ".xml", overwrite)); } // MAME Listroms @@ -5945,7 +5683,7 @@ namespace SabreTools.Library.DatFiles // MAME Listxml if (((DatFormat & DatFormat.Listxml) != 0) && (DatFormat & DatFormat.Logiqx) == 0 - && (DatFormat & DatFormat.LogiqxDepreciated) == 0 + && (DatFormat & DatFormat.LogiqxDeprecated) == 0 && (DatFormat & DatFormat.SabreDat) == 0 && (DatFormat & DatFormat.SoftwareList) == 0) { @@ -5953,7 +5691,7 @@ namespace SabreTools.Library.DatFiles } if (((DatFormat & DatFormat.Listxml) != 0 && ((DatFormat & DatFormat.Logiqx) != 0 - || (DatFormat & DatFormat.LogiqxDepreciated) != 0 + || (DatFormat & DatFormat.LogiqxDeprecated) != 0 || (DatFormat & DatFormat.SabreDat) != 0 || (DatFormat & DatFormat.SoftwareList) != 0))) { @@ -5977,7 +5715,7 @@ namespace SabreTools.Library.DatFiles // OfflineList if (((DatFormat & DatFormat.OfflineList) != 0) && (DatFormat & DatFormat.Logiqx) == 0 - && (DatFormat & DatFormat.LogiqxDepreciated) == 0 + && (DatFormat & DatFormat.LogiqxDeprecated) == 0 && (DatFormat & DatFormat.Listxml) == 0 && (DatFormat & DatFormat.SabreDat) == 0 && (DatFormat & DatFormat.SoftwareList) == 0) @@ -5986,7 +5724,7 @@ namespace SabreTools.Library.DatFiles } if (((DatFormat & DatFormat.OfflineList) != 0 && ((DatFormat & DatFormat.Logiqx) != 0 - || (DatFormat & DatFormat.LogiqxDepreciated) != 0 + || (DatFormat & DatFormat.LogiqxDeprecated) != 0 || (DatFormat & DatFormat.Listxml) != 0 || (DatFormat & DatFormat.SabreDat) != 0 || (DatFormat & DatFormat.SoftwareList) != 0))) @@ -5997,7 +5735,7 @@ namespace SabreTools.Library.DatFiles // openMSX if (((DatFormat & DatFormat.OpenMSX) != 0) && (DatFormat & DatFormat.Logiqx) == 0 - && (DatFormat & DatFormat.LogiqxDepreciated) == 0 + && (DatFormat & DatFormat.LogiqxDeprecated) == 0 && (DatFormat & DatFormat.Listxml) == 0 && (DatFormat & DatFormat.SabreDat) == 0 && (DatFormat & DatFormat.SoftwareList) == 0 @@ -6007,7 +5745,7 @@ namespace SabreTools.Library.DatFiles } if (((DatFormat & DatFormat.OpenMSX) != 0 && ((DatFormat & DatFormat.Logiqx) != 0 - || (DatFormat & DatFormat.LogiqxDepreciated) != 0 + || (DatFormat & DatFormat.LogiqxDeprecated) != 0 || (DatFormat & DatFormat.Listxml) != 0 || (DatFormat & DatFormat.SabreDat) != 0 || (DatFormat & DatFormat.SoftwareList) != 0 @@ -6059,11 +5797,11 @@ namespace SabreTools.Library.DatFiles }; // SabreDAT - if ((DatFormat & DatFormat.SabreDat) != 0 && ((DatFormat & DatFormat.Logiqx) == 0 || (DatFormat & DatFormat.LogiqxDepreciated) == 0)) + if ((DatFormat & DatFormat.SabreDat) != 0 && ((DatFormat & DatFormat.Logiqx) == 0 || (DatFormat & DatFormat.LogiqxDeprecated) == 0)) { outfileNames.Add(DatFormat.SabreDat, CreateOutfileNamesHelper(outDir, ".xml", overwrite)); }; - if ((DatFormat & DatFormat.SabreDat) != 0 && ((DatFormat & DatFormat.Logiqx) != 0 || (DatFormat & DatFormat.LogiqxDepreciated) != 0)) + if ((DatFormat & DatFormat.SabreDat) != 0 && ((DatFormat & DatFormat.Logiqx) != 0 || (DatFormat & DatFormat.LogiqxDeprecated) != 0)) { outfileNames.Add(DatFormat.SabreDat, CreateOutfileNamesHelper(outDir, ".sd.xml", overwrite)); }; @@ -6087,14 +5825,14 @@ namespace SabreTools.Library.DatFiles // Software List if ((DatFormat & DatFormat.SoftwareList) != 0 && (DatFormat & DatFormat.Logiqx) == 0 - && (DatFormat & DatFormat.LogiqxDepreciated) == 0 + && (DatFormat & DatFormat.LogiqxDeprecated) == 0 && (DatFormat & DatFormat.SabreDat) == 0) { outfileNames.Add(DatFormat.SoftwareList, CreateOutfileNamesHelper(outDir, ".xml", overwrite)); } if ((DatFormat & DatFormat.SoftwareList) != 0 && ((DatFormat & DatFormat.Logiqx) != 0 - || (DatFormat & DatFormat.LogiqxDepreciated) != 0 + || (DatFormat & DatFormat.LogiqxDeprecated) != 0 || (DatFormat & DatFormat.SabreDat) != 0)) { outfileNames.Add(DatFormat.SoftwareList, CreateOutfileNamesHelper(outDir, ".sl.xml", overwrite)); @@ -6124,20 +5862,17 @@ namespace SabreTools.Library.DatFiles /// String containing the new filename private string CreateOutfileNamesHelper(string outDir, string extension, bool overwrite) { - string filename = (String.IsNullOrWhiteSpace(FileName) ? Description : FileName); - string outfile = outDir + filename + extension; - outfile = (outfile.Contains(Path.DirectorySeparatorChar.ToString() + Path.DirectorySeparatorChar.ToString()) ? - outfile.Replace(Path.DirectorySeparatorChar.ToString() + Path.DirectorySeparatorChar.ToString(), Path.DirectorySeparatorChar.ToString()) : - outfile); + string filename = (string.IsNullOrWhiteSpace(FileName) ? Description : FileName); + string outfile = $"{outDir}{filename}{extension}"; + outfile = outfile.Replace($"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}", Path.DirectorySeparatorChar.ToString()); + if (!overwrite) { int i = 1; while (File.Exists(outfile)) { - outfile = outDir + filename + "_" + i + extension; - outfile = (outfile.Contains(Path.DirectorySeparatorChar.ToString() + Path.DirectorySeparatorChar.ToString()) ? - outfile.Replace(Path.DirectorySeparatorChar.ToString() + Path.DirectorySeparatorChar.ToString(), Path.DirectorySeparatorChar.ToString()) : - outfile); + outfile = $"{outDir}{filename}_{i}{extension}"; + outfile = outfile.Replace($"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}", Path.DirectorySeparatorChar.ToString()); i++; } } @@ -6159,13 +5894,10 @@ namespace SabreTools.Library.DatFiles bool quotesBackup = Quotes; bool useRomNameBackup = UseRomName; if (forceRemoveQuotes) - { Quotes = false; - } + if (forceRomName) - { UseRomName = true; - } // Create the proper Prefix and Postfix string pre = CreatePrefixPostfix(item, true); @@ -6176,27 +5908,23 @@ namespace SabreTools.Library.DatFiles { if (item.ItemType == ItemType.Rom) { + Rom romItem = item as Rom; + // We can only write out if there's a SHA-1 - if (!String.IsNullOrWhiteSpace(((Rom)item).SHA1)) + if (!string.IsNullOrWhiteSpace(romItem.SHA1)) { - name = ((Rom)item).SHA1.Substring(0, 2) - + "/" + ((Rom)item).SHA1.Substring(2, 2) - + "/" + ((Rom)item).SHA1.Substring(4, 2) - + "/" + ((Rom)item).SHA1.Substring(6, 2) - + "/" + ((Rom)item).SHA1 + ".gz"; - item.Name = pre + name + post; + name = Utilities.GetRombaPath(romItem.SHA1).Replace('\\', '/'); + item.Name = $"{pre}{name}{post}"; } } else if (item.ItemType == ItemType.Disk) { + Disk diskItem = item as Disk; + // We can only write out if there's a SHA-1 - if (!String.IsNullOrWhiteSpace(((Disk)item).SHA1)) + if (!string.IsNullOrWhiteSpace(diskItem.SHA1)) { - name = ((Disk)item).SHA1.Substring(0, 2) - + "/" + ((Disk)item).SHA1.Substring(2, 2) - + "/" + ((Disk)item).SHA1.Substring(4, 2) - + "/" + ((Disk)item).SHA1.Substring(6, 2) - + "/" + ((Disk)item).SHA1 + ".gz"; + name = Utilities.GetRombaPath(diskItem.SHA1).Replace('\\', '/'); item.Name = pre + name + post; } } @@ -6204,39 +5932,31 @@ namespace SabreTools.Library.DatFiles return; } - if (!String.IsNullOrWhiteSpace(ReplaceExtension) || RemoveExtension) + if (!string.IsNullOrWhiteSpace(ReplaceExtension) || RemoveExtension) { if (RemoveExtension) - { - ReplaceExtension = ""; - } + ReplaceExtension = string.Empty; string dir = Path.GetDirectoryName(name); - dir = (dir.StartsWith(Path.DirectorySeparatorChar.ToString()) ? dir.Remove(0, 1) : dir); + dir = dir.TrimStart(Path.DirectorySeparatorChar); name = Path.Combine(dir, Path.GetFileNameWithoutExtension(name) + ReplaceExtension); } - if (!String.IsNullOrWhiteSpace(AddExtension)) - { + + if (!string.IsNullOrWhiteSpace(AddExtension)) name += AddExtension; - } if (UseRomName && GameName) - { name = Path.Combine(item.MachineName, name); - } // Now assign back the item name item.Name = pre + name + post; // Restore all relevant values if (forceRemoveQuotes) - { Quotes = quotesBackup; - } + if (forceRomName) - { UseRomName = useRomNameBackup; - } } /// @@ -6248,7 +5968,7 @@ namespace SabreTools.Library.DatFiles protected string CreatePrefixPostfix(DatItem item, bool prefix) { // Initialize strings - string fix = "", + string fix = string.Empty, game = item.MachineName, name = item.Name, manufacturer = item.Manufacturer, @@ -6264,14 +5984,11 @@ namespace SabreTools.Library.DatFiles // If we have a prefix if (prefix) - { - fix = Prefix + (Quotes ? "\"" : ""); - } + fix = Prefix + (Quotes ? "\"" : string.Empty); + // If we have a postfix else - { - fix = (Quotes ? "\"" : "") + Postfix; - } + fix = (Quotes ? "\"" : string.Empty) + Postfix; // Ensure we have the proper values for replacement if (item.ItemType == ItemType.Rom) @@ -6338,15 +6055,11 @@ namespace SabreTools.Library.DatFiles { // If there's no output format, set the default if (statDatFormat == StatReportFormat.None) - { statDatFormat = StatReportFormat.Textfile; - } // Get the proper output file name - if (String.IsNullOrWhiteSpace(reportName)) - { + if (string.IsNullOrWhiteSpace(reportName)) reportName = "report"; - } // Get the proper output directory name outDir = Utilities.EnsureOutputDirectory(outDir); @@ -6362,13 +6075,7 @@ namespace SabreTools.Library.DatFiles .ToList(); // Get all of the writers that we need - List reports = new List(); - - // Loop through and output based on the inputs - foreach (KeyValuePair kvp in outputs) - { - reports.Add(Utilities.GetBaseReport(kvp.Key, kvp.Value, baddumpCol, nodumpCol)); - } + List reports = outputs.Select(kvp => Utilities.GetBaseReport(kvp.Key, kvp.Value, baddumpCol, nodumpCol)).ToList(); // Write the header, if any reports.ForEach(report => report.WriteHeader()); @@ -6396,7 +6103,7 @@ namespace SabreTools.Library.DatFiles DatFile lastdirdat = new DatFile { - FileName = "DIR: " + WebUtility.HtmlEncode(lastdir), + FileName = $"DIR: {WebUtility.HtmlEncode(lastdir)}", _datStats = dirStats, }; @@ -6414,14 +6121,14 @@ namespace SabreTools.Library.DatFiles dirStats.Reset(); } - Globals.Logger.Verbose("Beginning stat collection for '{0}'", false, file); + Globals.Logger.Verbose($"Beginning stat collection for '{file}'", false); List games = new List(); DatFile datdata = new DatFile(); datdata.Parse(file, 0, 0); datdata.BucketBy(SortedBy.Game, DedupeType.None, norename: true); // Output single DAT stats (if asked) - Globals.Logger.User("Adding stats for file '{0}'\n", false, file); + Globals.Logger.User($"Adding stats for file '{file}'\n", false); if (single) { datdata.WriteStatsToScreen(recalculate: false, baddumpCol: baddumpCol, nodumpCol: nodumpCol); @@ -6448,7 +6155,7 @@ namespace SabreTools.Library.DatFiles { DatFile dirdat = new DatFile { - FileName = "DIR: " + WebUtility.HtmlEncode(lastdir), + FileName = $"DIR: {WebUtility.HtmlEncode(lastdir)}", _datStats = dirStats, }; @@ -6497,37 +6204,27 @@ Please check the log folder if the stats scrolled offscreen", false); // First try to create the output directory if we need to if (!Directory.Exists(outDir)) - { Directory.CreateDirectory(outDir); - } // Double check the outDir for the end delim if (!outDir.EndsWith(Path.DirectorySeparatorChar.ToString())) - { outDir += Path.DirectorySeparatorChar; - } // For each output format, get the appropriate stream writer if ((statDatFormat & StatReportFormat.Textfile) != 0) - { output.Add(StatReportFormat.Textfile, CreateOutStatsNamesHelper(outDir, ".txt", reportName, overwrite)); - } + if ((statDatFormat & StatReportFormat.CSV) != 0) - { output.Add(StatReportFormat.CSV, CreateOutStatsNamesHelper(outDir, ".csv", reportName, overwrite)); - } + if ((statDatFormat & StatReportFormat.HTML) != 0) - { output.Add(StatReportFormat.HTML, CreateOutStatsNamesHelper(outDir, ".html", reportName, overwrite)); - } + if ((statDatFormat & StatReportFormat.SSV) != 0) - { output.Add(StatReportFormat.SSV, CreateOutStatsNamesHelper(outDir, ".ssv", reportName, overwrite)); - } + if ((statDatFormat & StatReportFormat.TSV) != 0) - { output.Add(StatReportFormat.TSV, CreateOutStatsNamesHelper(outDir, ".tsv", reportName, overwrite)); - } return output; } @@ -6543,18 +6240,15 @@ Please check the log folder if the stats scrolled offscreen", false); private static string CreateOutStatsNamesHelper(string outDir, string extension, string reportName, bool overwrite) { string outfile = outDir + reportName + extension; - outfile = (outfile.Contains(Path.DirectorySeparatorChar.ToString() + Path.DirectorySeparatorChar.ToString()) ? - outfile.Replace(Path.DirectorySeparatorChar.ToString() + Path.DirectorySeparatorChar.ToString(), Path.DirectorySeparatorChar.ToString()) : - outfile); + outfile = outfile.Replace($"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}", Path.DirectorySeparatorChar.ToString()); + if (!overwrite) { int i = 1; while (File.Exists(outfile)) { - outfile = outDir + reportName + "_" + i + extension; - outfile = (outfile.Contains(Path.DirectorySeparatorChar.ToString() + Path.DirectorySeparatorChar.ToString()) ? - outfile.Replace(Path.DirectorySeparatorChar.ToString() + Path.DirectorySeparatorChar.ToString(), Path.DirectorySeparatorChar.ToString()) : - outfile); + outfile = $"{outDir}{reportName}_{i}{extension}"; + outfile = outfile.Replace($"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}", Path.DirectorySeparatorChar.ToString()); i++; } } diff --git a/SabreTools.Library/DatFiles/DatHeader.cs b/SabreTools.Library/DatFiles/DatHeader.cs index 4d7a5dbe..5486d67c 100644 --- a/SabreTools.Library/DatFiles/DatHeader.cs +++ b/SabreTools.Library/DatFiles/DatHeader.cs @@ -1,4 +1,5 @@ using System; + using SabreTools.Library.Data; namespace SabreTools.Library.DatFiles diff --git a/SabreTools.Library/DatFiles/DatStats.cs b/SabreTools.Library/DatFiles/DatStats.cs index b40e0391..89ecab5b 100644 --- a/SabreTools.Library/DatFiles/DatStats.cs +++ b/SabreTools.Library/DatFiles/DatStats.cs @@ -1,5 +1,4 @@ -using System; -using SabreTools.Library.Data; +using SabreTools.Library.Data; using SabreTools.Library.DatItems; namespace SabreTools.Library.DatFiles @@ -14,7 +13,7 @@ namespace SabreTools.Library.DatFiles /// /// Object used to lock stats updates /// - private object _lockObject = new object(); + private readonly object _lockObject = new object(); #endregion @@ -155,12 +154,12 @@ namespace SabreTools.Library.DatFiles this.DiskCount += 1; if (((Disk)item).ItemStatus != ItemStatus.Nodump) { - this.MD5Count += (String.IsNullOrWhiteSpace(((Disk)item).MD5) ? 0 : 1); - this.RIPEMD160Count += (String.IsNullOrWhiteSpace(((Disk)item).RIPEMD160) ? 0 : 1); - this.SHA1Count += (String.IsNullOrWhiteSpace(((Disk)item).SHA1) ? 0 : 1); - this.SHA256Count += (String.IsNullOrWhiteSpace(((Disk)item).SHA256) ? 0 : 1); - this.SHA384Count += (String.IsNullOrWhiteSpace(((Disk)item).SHA384) ? 0 : 1); - this.SHA512Count += (String.IsNullOrWhiteSpace(((Disk)item).SHA512) ? 0 : 1); + this.MD5Count += (string.IsNullOrWhiteSpace(((Disk)item).MD5) ? 0 : 1); + this.RIPEMD160Count += (string.IsNullOrWhiteSpace(((Disk)item).RIPEMD160) ? 0 : 1); + this.SHA1Count += (string.IsNullOrWhiteSpace(((Disk)item).SHA1) ? 0 : 1); + this.SHA256Count += (string.IsNullOrWhiteSpace(((Disk)item).SHA256) ? 0 : 1); + this.SHA384Count += (string.IsNullOrWhiteSpace(((Disk)item).SHA384) ? 0 : 1); + this.SHA512Count += (string.IsNullOrWhiteSpace(((Disk)item).SHA512) ? 0 : 1); } this.BaddumpCount += (((Disk)item).ItemStatus == ItemStatus.BadDump ? 1 : 0); @@ -176,13 +175,13 @@ namespace SabreTools.Library.DatFiles if (((Rom)item).ItemStatus != ItemStatus.Nodump) { this.TotalSize += ((Rom)item).Size; - this.CRCCount += (String.IsNullOrWhiteSpace(((Rom)item).CRC) ? 0 : 1); - this.MD5Count += (String.IsNullOrWhiteSpace(((Rom)item).MD5) ? 0 : 1); - this.RIPEMD160Count += (String.IsNullOrWhiteSpace(((Rom)item).RIPEMD160) ? 0 : 1); - this.SHA1Count += (String.IsNullOrWhiteSpace(((Rom)item).SHA1) ? 0 : 1); - this.SHA256Count += (String.IsNullOrWhiteSpace(((Rom)item).SHA256) ? 0 : 1); - this.SHA384Count += (String.IsNullOrWhiteSpace(((Rom)item).SHA384) ? 0 : 1); - this.SHA512Count += (String.IsNullOrWhiteSpace(((Rom)item).SHA512) ? 0 : 1); + this.CRCCount += (string.IsNullOrWhiteSpace(((Rom)item).CRC) ? 0 : 1); + this.MD5Count += (string.IsNullOrWhiteSpace(((Rom)item).MD5) ? 0 : 1); + this.RIPEMD160Count += (string.IsNullOrWhiteSpace(((Rom)item).RIPEMD160) ? 0 : 1); + this.SHA1Count += (string.IsNullOrWhiteSpace(((Rom)item).SHA1) ? 0 : 1); + this.SHA256Count += (string.IsNullOrWhiteSpace(((Rom)item).SHA256) ? 0 : 1); + this.SHA384Count += (string.IsNullOrWhiteSpace(((Rom)item).SHA384) ? 0 : 1); + this.SHA512Count += (string.IsNullOrWhiteSpace(((Rom)item).SHA512) ? 0 : 1); } this.BaddumpCount += (((Rom)item).ItemStatus == ItemStatus.BadDump ? 1 : 0); @@ -257,12 +256,12 @@ namespace SabreTools.Library.DatFiles this.DiskCount -= 1; if (((Disk)item).ItemStatus != ItemStatus.Nodump) { - this.MD5Count -= (String.IsNullOrWhiteSpace(((Disk)item).MD5) ? 0 : 1); - this.RIPEMD160Count -= (String.IsNullOrWhiteSpace(((Disk)item).RIPEMD160) ? 0 : 1); - this.SHA1Count -= (String.IsNullOrWhiteSpace(((Disk)item).SHA1) ? 0 : 1); - this.SHA256Count -= (String.IsNullOrWhiteSpace(((Disk)item).SHA256) ? 0 : 1); - this.SHA384Count -= (String.IsNullOrWhiteSpace(((Disk)item).SHA384) ? 0 : 1); - this.SHA512Count -= (String.IsNullOrWhiteSpace(((Disk)item).SHA512) ? 0 : 1); + this.MD5Count -= (string.IsNullOrWhiteSpace(((Disk)item).MD5) ? 0 : 1); + this.RIPEMD160Count -= (string.IsNullOrWhiteSpace(((Disk)item).RIPEMD160) ? 0 : 1); + this.SHA1Count -= (string.IsNullOrWhiteSpace(((Disk)item).SHA1) ? 0 : 1); + this.SHA256Count -= (string.IsNullOrWhiteSpace(((Disk)item).SHA256) ? 0 : 1); + this.SHA384Count -= (string.IsNullOrWhiteSpace(((Disk)item).SHA384) ? 0 : 1); + this.SHA512Count -= (string.IsNullOrWhiteSpace(((Disk)item).SHA512) ? 0 : 1); } this.BaddumpCount -= (((Disk)item).ItemStatus == ItemStatus.BadDump ? 1 : 0); @@ -278,13 +277,13 @@ namespace SabreTools.Library.DatFiles if (((Rom)item).ItemStatus != ItemStatus.Nodump) { this.TotalSize -= ((Rom)item).Size; - this.CRCCount -= (String.IsNullOrWhiteSpace(((Rom)item).CRC) ? 0 : 1); - this.MD5Count -= (String.IsNullOrWhiteSpace(((Rom)item).MD5) ? 0 : 1); - this.RIPEMD160Count -= (String.IsNullOrWhiteSpace(((Rom)item).RIPEMD160) ? 0 : 1); - this.SHA1Count -= (String.IsNullOrWhiteSpace(((Rom)item).SHA1) ? 0 : 1); - this.SHA256Count -= (String.IsNullOrWhiteSpace(((Rom)item).SHA256) ? 0 : 1); - this.SHA384Count -= (String.IsNullOrWhiteSpace(((Rom)item).SHA384) ? 0 : 1); - this.SHA512Count -= (String.IsNullOrWhiteSpace(((Rom)item).SHA512) ? 0 : 1); + this.CRCCount -= (string.IsNullOrWhiteSpace(((Rom)item).CRC) ? 0 : 1); + this.MD5Count -= (string.IsNullOrWhiteSpace(((Rom)item).MD5) ? 0 : 1); + this.RIPEMD160Count -= (string.IsNullOrWhiteSpace(((Rom)item).RIPEMD160) ? 0 : 1); + this.SHA1Count -= (string.IsNullOrWhiteSpace(((Rom)item).SHA1) ? 0 : 1); + this.SHA256Count -= (string.IsNullOrWhiteSpace(((Rom)item).SHA256) ? 0 : 1); + this.SHA384Count -= (string.IsNullOrWhiteSpace(((Rom)item).SHA384) ? 0 : 1); + this.SHA512Count -= (string.IsNullOrWhiteSpace(((Rom)item).SHA512) ? 0 : 1); } this.BaddumpCount -= (((Rom)item).ItemStatus == ItemStatus.BadDump ? 1 : 0); diff --git a/SabreTools.Library/DatFiles/DosCenter.cs b/SabreTools.Library/DatFiles/DosCenter.cs index 6afc91cc..3b152e11 100644 --- a/SabreTools.Library/DatFiles/DosCenter.cs +++ b/SabreTools.Library/DatFiles/DosCenter.cs @@ -1,19 +1,12 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; + using SabreTools.Library.Data; using SabreTools.Library.DatItems; using SabreTools.Library.Tools; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using FileStream = System.IO.FileStream; -using StreamWriter = System.IO.StreamWriter; -#endif using NaturalSort; namespace SabreTools.Library.DatFiles @@ -68,13 +61,13 @@ namespace SabreTools.Library.DatFiles { try { - Globals.Logger.User("Opening file for writing: {0}", outfile); + Globals.Logger.User($"Opening file for writing: {outfile}"); FileStream fs = Utilities.TryCreate(outfile); // If we get back null for some reason, just log and return if (fs == null) { - Globals.Logger.Warning("File '{0}' could not be created for writing! Please check to see if the file is writable", outfile); + Globals.Logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable"); return false; } @@ -112,22 +105,18 @@ namespace SabreTools.Library.DatFiles // If we have a different game and we're not at the start of the list, output the end of last item if (lastgame != null && lastgame.ToLowerInvariant() != rom.MachineName.ToLowerInvariant()) - { WriteEndGame(sw, rom); - } // If we have a new game, output the beginning of the new item if (lastgame == null || lastgame.ToLowerInvariant() != rom.MachineName.ToLowerInvariant()) - { WriteStartGame(sw, rom); - } // If we have a "null" game (created by DATFromDir or something similar), log it to file if (rom.ItemType == ItemType.Rom && ((Rom)rom).Size == -1 && ((Rom)rom).CRC == "null") { - Globals.Logger.Verbose("Empty folder found: {0}", rom.MachineName); + Globals.Logger.Verbose($"Empty folder found: {rom.MachineName}"); rom.Name = (rom.Name == "null" ? "-" : rom.Name); ((Rom)rom).Size = Constants.SizeZero; @@ -151,7 +140,7 @@ namespace SabreTools.Library.DatFiles // Write the file footer out WriteFooter(sw); - Globals.Logger.Verbose("File written!" + Environment.NewLine); + Globals.Logger.Verbose($"File written!{Environment.NewLine}"); sw.Dispose(); fs.Dispose(); } @@ -173,15 +162,15 @@ namespace SabreTools.Library.DatFiles { try { - string header = "DOSCenter (\n" + - "\tName: " + Name + "\n" + - "\tDescription: " + Description + "\n" + - "\tVersion: " + Version + "\n" + - "\tDate: " + Date + "\n" + - "\tAuthor: " + Author + "\n" + - "\tHomepage: " + Homepage + "\n" + - "\tComment: " + Comment + "\n" + - ")\n"; + string header = "DOSCenter (\n"; + header += $"\tName: {Name}\n"; + header += $"\tDescription: {Description}\n"; + header += $"\tVersion: {Version}\n"; + header += $"\tDate: {Date}\n"; + header += $"\tAuthor: {Author}\n"; + header += $"\tHomepage: {Homepage}\n"; + header += $"\tComment: {Comment}\n"; + header += ")\n"; // Write the header out sw.Write(header); @@ -200,19 +189,17 @@ namespace SabreTools.Library.DatFiles /// Write out Game start using the supplied StreamWriter /// /// StreamWriter to output to - /// DatItem object to be output + /// DatItem object to be output /// True if the data was written, false on error - private bool WriteStartGame(StreamWriter sw, DatItem rom) + private bool WriteStartGame(StreamWriter sw, DatItem datItem) { try { // No game should start with a path separator - if (rom.MachineName.StartsWith(Path.DirectorySeparatorChar.ToString())) - { - rom.MachineName = rom.MachineName.Substring(1); - } + datItem.MachineName = datItem.MachineName.TrimStart(Path.DirectorySeparatorChar); - string state = "game (\n\tname \"" + (!ExcludeFields[(int)Field.MachineName] ? rom.MachineName + ".zip" : "") + "\"\n"; + // Build the state based on excluded fields + string state = $"game (\n\tname \"{datItem.GetField(Field.MachineName, ExcludeFields)}.zip\n"; sw.Write(state); sw.Flush(); @@ -230,13 +217,19 @@ namespace SabreTools.Library.DatFiles /// Write out Game end using the supplied StreamWriter /// /// StreamWriter to output to - /// DatItem object to be output + /// DatItem object to be output /// True if the data was written, false on error - private bool WriteEndGame(StreamWriter sw, DatItem rom) + private bool WriteEndGame(StreamWriter sw, DatItem datItem) { try { - string state = (!ExcludeFields[(int)Field.SampleOf] && String.IsNullOrWhiteSpace(rom.SampleOf) ? "" : "\tsampleof \"" + rom.SampleOf + "\"\n") + ")\n"; + string state = string.Empty; + + // Build the state based on excluded fields + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SampleOf, ExcludeFields))) + state += $"\tsampleof \"{datItem.SampleOf}\"\n"; + + state += ")\n"; sw.Write(state); sw.Flush(); @@ -254,27 +247,24 @@ namespace SabreTools.Library.DatFiles /// Write out DatItem using the supplied StreamWriter /// /// StreamWriter to output to - /// DatItem object to be output + /// DatItem object to be output /// True if blank roms should be skipped on output, false otherwise (default) /// True if the data was written, false on error - private bool WriteDatItem(StreamWriter sw, DatItem rom, bool ignoreblanks = false) + private bool WriteDatItem(StreamWriter sw, DatItem datItem, bool ignoreblanks = false) { // If we are in ignore blanks mode AND we have a blank (0-size) rom, skip - if (ignoreblanks - && (rom.ItemType == ItemType.Rom - && (((Rom)rom).Size == 0 || ((Rom)rom).Size == -1))) - { + if (ignoreblanks && (datItem.ItemType == ItemType.Rom && ((datItem as Rom).Size == 0 || (datItem as Rom).Size == -1))) return true; - } try { - string state = ""; + string state = string.Empty; // Pre-process the item name - ProcessItemName(rom, true); + ProcessItemName(datItem, true); - switch (rom.ItemType) + // Build the state based on excluded fields + switch (datItem.ItemType) { case ItemType.Archive: case ItemType.BiosSet: @@ -284,11 +274,15 @@ namespace SabreTools.Library.DatFiles // We don't output these at all for DosCenter break; case ItemType.Rom: - state += "\tfile ( name " + (!ExcludeFields[(int)Field.Name] ? ((Rom)rom).Name : "") - + (!ExcludeFields[(int)Field.Size] && ((Rom)rom).Size != -1 ? " size " + ((Rom)rom).Size : "") - + (!ExcludeFields[(int)Field.Date] && !String.IsNullOrWhiteSpace(((Rom)rom).Date) ? " date " + ((Rom)rom).Date : "") - + (!ExcludeFields[(int)Field.CRC] && !String.IsNullOrWhiteSpace(((Rom)rom).CRC) ? " crc " + ((Rom)rom).CRC.ToLowerInvariant() : "") - + " )\n"; + var rom = datItem as Rom; + state += $"\file ( name \"{datItem.GetField(Field.Name, ExcludeFields)}\""; + if (!ExcludeFields[(int)Field.Size] && rom.Size != -1) + state += $" size \"{rom.Size}\""; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Date, ExcludeFields))) + state += $" date \"{rom.Date}\""; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CRC, ExcludeFields))) + state += $" crc \"{rom.CRC.ToLowerInvariant()}\""; + state += " )\n"; break; } diff --git a/SabreTools.Library/DatFiles/EverdriveSmdb.cs b/SabreTools.Library/DatFiles/EverdriveSmdb.cs index 4d9b2f7d..9eb4a446 100644 --- a/SabreTools.Library/DatFiles/EverdriveSmdb.cs +++ b/SabreTools.Library/DatFiles/EverdriveSmdb.cs @@ -1,19 +1,11 @@ using System; using System.Collections.Generic; +using System.IO; using System.Text; + using SabreTools.Library.Data; using SabreTools.Library.DatItems; using SabreTools.Library.Tools; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using FileStream = System.IO.FileStream; -using StreamReader = System.IO.StreamReader; -using StreamWriter = System.IO.StreamWriter; -#endif using NaturalSort; namespace SabreTools.Library.DatFiles @@ -103,13 +95,13 @@ namespace SabreTools.Library.DatFiles { try { - Globals.Logger.User("Opening file for writing: {0}", outfile); + Globals.Logger.User($"Opening file for writing: {outfile}"); FileStream fs = Utilities.TryCreate(outfile); // If we get back null for some reason, just log and return if (fs == null) { - Globals.Logger.Warning("File '{0}' could not be created for writing! Please check to see if the file is writable", outfile); + Globals.Logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable"); return false; } @@ -142,7 +134,7 @@ namespace SabreTools.Library.DatFiles && ((Rom)item).Size == -1 && ((Rom)item).CRC == "null") { - Globals.Logger.Verbose("Empty folder found: {0}", item.MachineName); + Globals.Logger.Verbose($"Empty folder found: {item.MachineName}"); item.Name = (item.Name == "null" ? "-" : item.Name); ((Rom)item).Size = Constants.SizeZero; @@ -152,7 +144,7 @@ namespace SabreTools.Library.DatFiles } } - Globals.Logger.Verbose("File written!" + Environment.NewLine); + Globals.Logger.Verbose($"File written!{Environment.NewLine}"); sw.Dispose(); fs.Dispose(); } @@ -169,35 +161,46 @@ namespace SabreTools.Library.DatFiles /// Write out Game start using the supplied StreamWriter /// /// StreamWriter to output to - /// DatItem object to be output + /// DatItem object to be output /// True if the data was written, false on error - private bool WriteDatItem(StreamWriter sw, DatItem rom, bool ignoreblanks = false) + private bool WriteDatItem(StreamWriter sw, DatItem datItem, bool ignoreblanks = false) { // If we are in ignore blanks mode AND we have a blank (0-size) rom, skip - if (ignoreblanks - && (rom.ItemType == ItemType.Rom - && (((Rom)rom).Size == 0 || ((Rom)rom).Size == -1))) - { + if (ignoreblanks && (datItem.ItemType == ItemType.Rom && ((datItem as Rom).Size == 0 || (datItem as Rom).Size == -1))) return true; - } try { // No game should start with a path separator - if (rom.MachineName.StartsWith(Path.DirectorySeparatorChar.ToString())) - rom.MachineName = rom.MachineName.Substring(1); + datItem.MachineName = datItem.MachineName.TrimStart(Path.DirectorySeparatorChar); - // If the DatItem isn't a rom, we don't output it - if (rom.ItemType != ItemType.Rom) - return true; + // Pre-process the item name + ProcessItemName(datItem, true); - Rom temp = rom as Rom; - string state = (!ExcludeFields[(int)Field.SHA256] ? temp.SHA256 : "") + "\t" - + (!ExcludeFields[(int)Field.MachineName] ? temp.MachineName + "/" : "") - + temp.Name + "\t" - + (!ExcludeFields[(int)Field.SHA1] ? temp.SHA1 : "") + "\t" - + (!ExcludeFields[(int)Field.MD5] ? temp.MD5 : "") + "\t" - + (!ExcludeFields[(int)Field.CRC] ? temp.CRC : "") + "\n"; + string state = string.Empty; + + // Build the state based on excluded fields + switch (datItem.ItemType) + { + case ItemType.Archive: + case ItemType.BiosSet: + case ItemType.Disk: + case ItemType.Release: + case ItemType.Sample: + // We don't output these at all for Everdrive SMDB + break; + + case ItemType.Rom: + var rom = datItem as Rom; + state += $"{rom.GetField(Field.SHA256, ExcludeFields)}\t"; + state += $"{rom.GetField(Field.MachineName, ExcludeFields)}/\t"; + state += $"{rom.GetField(Field.Name, ExcludeFields)}\t"; + state += $"{rom.GetField(Field.SHA1, ExcludeFields)}\t"; + state += $"{rom.GetField(Field.MD5, ExcludeFields)}\t"; + state += $"{rom.GetField(Field.CRC, ExcludeFields)}"; + state += "\n"; + break; + } sw.Write(state); sw.Flush(); diff --git a/SabreTools.Library/DatFiles/Filter.cs b/SabreTools.Library/DatFiles/Filter.cs index 90e9c70c..8d9dc0f9 100644 --- a/SabreTools.Library/DatFiles/Filter.cs +++ b/SabreTools.Library/DatFiles/Filter.cs @@ -1,19 +1,16 @@ using System; using System.Collections.Generic; +using System.IO; + using SabreTools.Library.Data; using SabreTools.Library.DatItems; -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; -#endif - namespace SabreTools.Library.DatFiles { /// /// Represents the filtering operations that need to be performed on a set of items, usually a DAT /// + /// TODO: Can this use `Field` instead of explicit filters? public class Filter { #region Pubically facing variables diff --git a/SabreTools.Library/DatFiles/FilterItem.cs b/SabreTools.Library/DatFiles/FilterItem.cs index 2d90249b..14d44725 100644 --- a/SabreTools.Library/DatFiles/FilterItem.cs +++ b/SabreTools.Library/DatFiles/FilterItem.cs @@ -155,13 +155,13 @@ namespace SabreTools.Library.DatFiles { string needleString = needle as string; string strawString = straw as string; - if (!String.IsNullOrWhiteSpace(strawString) && needleString != null) + if (!string.IsNullOrWhiteSpace(strawString) && needleString != null) { string regexStraw = strawString; // If the straw has no special characters at all (excluding whitespace), treat it as an exact match if (regexStraw == Regex.Escape(regexStraw).Replace("\\ ", " ")) - regexStraw = "^" + regexStraw + "$"; + regexStraw = $"^{regexStraw}$"; // Check if a match is found with the regex found |= Regex.IsMatch(needleString, regexStraw, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); diff --git a/SabreTools.Library/DatFiles/Hashfile.cs b/SabreTools.Library/DatFiles/Hashfile.cs index ea39a8af..1a111f36 100644 --- a/SabreTools.Library/DatFiles/Hashfile.cs +++ b/SabreTools.Library/DatFiles/Hashfile.cs @@ -1,19 +1,11 @@ using System; using System.Collections.Generic; +using System.IO; using System.Text; + using SabreTools.Library.Data; using SabreTools.Library.DatItems; using SabreTools.Library.Tools; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using FileStream = System.IO.FileStream; -using StreamReader = System.IO.StreamReader; -using StreamWriter = System.IO.StreamWriter; -#endif using NaturalSort; namespace SabreTools.Library.DatFiles @@ -24,7 +16,7 @@ namespace SabreTools.Library.DatFiles internal class Hashfile : DatFile { // Private instance variables specific to Hashfile DATs - Hash _hash; + private readonly Hash _hash; /// /// Constructor designed for casting a base DatFile @@ -67,8 +59,8 @@ namespace SabreTools.Library.DatFiles // Split the line and get the name and hash string[] split = line.Split(' '); - string name = ""; - string hash = ""; + string name = string.Empty; + string hash = string.Empty; // If we have CRC, then it's an SFV file and the name is first are if ((_hash & Hash.CRC) != 0) @@ -119,13 +111,13 @@ namespace SabreTools.Library.DatFiles { try { - Globals.Logger.User("Opening file for writing: {0}", outfile); + Globals.Logger.User($"Opening file for writing: {outfile}"); FileStream fs = Utilities.TryCreate(outfile); // If we get back null for some reason, just log and return if (fs == null) { - Globals.Logger.Warning("File '{0}' could not be created for writing! Please check to see if the file is writable", outfile); + Globals.Logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable"); return false; } @@ -158,7 +150,7 @@ namespace SabreTools.Library.DatFiles && ((Rom)rom).Size == -1 && ((Rom)rom).CRC == "null") { - Globals.Logger.Verbose("Empty folder found: {0}", rom.MachineName); + Globals.Logger.Verbose($"Empty folder found: {rom.MachineName}"); } // Now, output the rom data @@ -166,7 +158,7 @@ namespace SabreTools.Library.DatFiles } } - Globals.Logger.Verbose("File written!" + Environment.NewLine); + Globals.Logger.Verbose($"File written!{Environment.NewLine}"); sw.Dispose(); fs.Dispose(); } @@ -183,118 +175,61 @@ namespace SabreTools.Library.DatFiles /// Write out DatItem using the supplied StreamWriter /// /// StreamWriter to output to - /// DatItem object to be output + /// DatItem object to be output /// True if blank roms should be skipped on output, false otherwise (default) /// True if the data was written, false on error - private bool WriteDatItem(StreamWriter sw, DatItem rom, bool ignoreblanks = false) + private bool WriteDatItem(StreamWriter sw, DatItem datItem, bool ignoreblanks = false) { // If we are in ignore blanks mode AND we have a blank (0-size) rom, skip - if (ignoreblanks - && (rom.ItemType == ItemType.Rom - && (((Rom)rom).Size == 0 || ((Rom)rom).Size == -1))) - { + if (ignoreblanks && (datItem.ItemType == ItemType.Rom && ((datItem as Rom).Size == 0 || (datItem as Rom).Size == -1))) return true; - } try { - string state = ""; + string state = string.Empty; // Pre-process the item name - ProcessItemName(rom, true); + ProcessItemName(datItem, true); + // Build the state based on excluded fields switch (_hash) { case Hash.CRC: - if (rom.ItemType == ItemType.Rom) + if (datItem.ItemType == ItemType.Rom) { - state += (!ExcludeFields[(int)Field.MachineName] && GameName ? rom.MachineName + Path.DirectorySeparatorChar : "") - + (!ExcludeFields[(int)Field.Name] ? rom.Name : "") - + " " + (!ExcludeFields[(int)Field.CRC] ? ((Rom)rom).CRC : "") + "\n"; + var rom = datItem as Rom; + if (GameName) + state += $"{rom.GetField(Field.MachineName, ExcludeFields)}{Path.DirectorySeparatorChar}"; + state += $"{rom.GetField(Field.Name, ExcludeFields)}"; + state += $"{rom.GetField(Field.CRC, ExcludeFields)}"; + state += "\n"; } break; + case Hash.MD5: - if (rom.ItemType == ItemType.Rom) - { - state += (!ExcludeFields[(int)Field.MD5] ? ((Rom)rom).MD5 : "") - + " *" + (!ExcludeFields[(int)Field.MachineName] && GameName ? rom.MachineName + Path.DirectorySeparatorChar : "") - + (!ExcludeFields[(int)Field.Name] ? rom.Name : "") + "\n"; - } - else if (rom.ItemType == ItemType.Disk) - { - state += (!ExcludeFields[(int)Field.MD5] ? ((Disk)rom).MD5 : "") - + " *" + (!ExcludeFields[(int)Field.MachineName] && GameName ? rom.MachineName + Path.DirectorySeparatorChar : "") - + (!ExcludeFields[(int)Field.Name] ? rom.Name : "") + "\n"; - } - break; case Hash.RIPEMD160: - if (rom.ItemType == ItemType.Rom) - { - state += (!ExcludeFields[(int)Field.RIPEMD160] ? ((Rom)rom).RIPEMD160 : "") - + " *" + (!ExcludeFields[(int)Field.MachineName] && GameName ? rom.MachineName + Path.DirectorySeparatorChar : "") - + (!ExcludeFields[(int)Field.Name] ? rom.Name : "") + "\n"; - } - else if (rom.ItemType == ItemType.Disk) - { - state += (!ExcludeFields[(int)Field.RIPEMD160] ? ((Disk)rom).RIPEMD160 : "") - + " *" + (!ExcludeFields[(int)Field.MachineName] && GameName ? rom.MachineName + Path.DirectorySeparatorChar : "") - + (!ExcludeFields[(int)Field.Name] ? rom.Name : "") + "\n"; - } - break; case Hash.SHA1: - if (rom.ItemType == ItemType.Rom) - { - state += (!ExcludeFields[(int)Field.SHA1] ? ((Rom)rom).SHA1 : "") - + " *" + (!ExcludeFields[(int)Field.MachineName] && GameName ? rom.MachineName + Path.DirectorySeparatorChar : "") - + (!ExcludeFields[(int)Field.Name] ? rom.Name : "") + "\n"; - } - else if (rom.ItemType == ItemType.Disk) - { - state += (!ExcludeFields[(int)Field.SHA1] ? ((Disk)rom).SHA1 : "") - + " *" + (!ExcludeFields[(int)Field.MachineName] && GameName ? rom.MachineName + Path.DirectorySeparatorChar : "") - + (!ExcludeFields[(int)Field.Name] ? rom.Name : "") + "\n"; - } - break; case Hash.SHA256: - if (rom.ItemType == ItemType.Rom) - { - state += (!ExcludeFields[(int)Field.SHA256] ? ((Rom)rom).SHA256 : "") - + " *" + (!ExcludeFields[(int)Field.MachineName] && GameName ? rom.MachineName + Path.DirectorySeparatorChar : "") - + (!ExcludeFields[(int)Field.Name] ? rom.Name : "") + "\n"; - } - else if (rom.ItemType == ItemType.Disk) - { - state += (!ExcludeFields[(int)Field.SHA256] ? ((Disk)rom).SHA256 : "") - + " *" + (!ExcludeFields[(int)Field.MachineName] && GameName ? rom.MachineName + Path.DirectorySeparatorChar : "") - + (!ExcludeFields[(int)Field.Name] ? rom.Name : "") + "\n"; - } - break; case Hash.SHA384: - if (rom.ItemType == ItemType.Rom) - { - state += (!ExcludeFields[(int)Field.SHA384] ? ((Rom)rom).SHA384 : "") - + " *" + (!ExcludeFields[(int)Field.MachineName] && GameName ? rom.MachineName + Path.DirectorySeparatorChar : "") - + (!ExcludeFields[(int)Field.Name] ? rom.Name : "") + "\n"; - } - else if (rom.ItemType == ItemType.Disk) - { - state += (!ExcludeFields[(int)Field.SHA384] ? ((Disk)rom).SHA384 : "") - + " *" + (!ExcludeFields[(int)Field.MachineName] && GameName ? rom.MachineName + Path.DirectorySeparatorChar : "") - + (!ExcludeFields[(int)Field.Name] ? rom.Name : "") + "\n"; - } - break; case Hash.SHA512: - if (rom.ItemType == ItemType.Rom) + Field hashField = Utilities.GetFieldFromHash(_hash); + if (datItem.ItemType == ItemType.Rom) { - state += (!ExcludeFields[(int)Field.SHA512] ? ((Rom)rom).SHA512 : "") - + " *" + (!ExcludeFields[(int)Field.MachineName] && GameName ? rom.MachineName + Path.DirectorySeparatorChar : "") - + (!ExcludeFields[(int)Field.Name] ? rom.Name : "") + "\n"; + var rom = datItem as Rom; + state += $"{rom.GetField(hashField, ExcludeFields)}"; + if (GameName) + state += $"{rom.GetField(Field.MachineName, ExcludeFields)}{Path.DirectorySeparatorChar}"; + state += $"{rom.GetField(Field.Name, ExcludeFields)}"; + state += "\n"; } - else if (rom.ItemType == ItemType.Disk) + else if (datItem.ItemType == ItemType.Disk) { - state += (!ExcludeFields[(int)Field.SHA512] ? ((Disk)rom).SHA512 : "") - + " *" + (!ExcludeFields[(int)Field.MachineName] && GameName ? rom.MachineName + Path.DirectorySeparatorChar : "") - + (!ExcludeFields[(int)Field.Name] ? rom.Name : "") + "\n"; + var disk = datItem as Disk; + state += $"{disk.GetField(hashField, ExcludeFields)}"; + if (GameName) + state += $"{disk.GetField(Field.MachineName, ExcludeFields)}{Path.DirectorySeparatorChar}"; + state += $"{disk.GetField(Field.Name, ExcludeFields)}"; + state += "\n"; } break; } diff --git a/SabreTools.Library/DatFiles/Listrom.cs b/SabreTools.Library/DatFiles/Listrom.cs index c123988d..f8762c70 100644 --- a/SabreTools.Library/DatFiles/Listrom.cs +++ b/SabreTools.Library/DatFiles/Listrom.cs @@ -1,20 +1,12 @@ using System; using System.Collections.Generic; +using System.IO; using System.Text; using System.Text.RegularExpressions; + using SabreTools.Library.Data; using SabreTools.Library.DatItems; using SabreTools.Library.Tools; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using FileStream = System.IO.FileStream; -using StreamReader = System.IO.StreamReader; -using StreamWriter = System.IO.StreamWriter; -#endif using NaturalSort; namespace SabreTools.Library.DatFiles @@ -66,13 +58,13 @@ namespace SabreTools.Library.DatFiles Encoding enc = Utilities.GetEncoding(filename); StreamReader sr = new StreamReader(Utilities.TryOpenRead(filename), enc); - string gamename = ""; + string gamename = string.Empty; while (!sr.EndOfStream) { string line = sr.ReadLine().Trim(); // If we have a blank line, we just skip it - if (String.IsNullOrWhiteSpace(line)) + if (string.IsNullOrWhiteSpace(line)) { continue; } @@ -86,7 +78,7 @@ namespace SabreTools.Library.DatFiles // If we have the beginning of a game, set the name of the game else if (line.StartsWith("ROMs required for")) { - gamename = Regex.Match(line, @"^ROMs required for \S*? ""(.*?)""\.").Groups[1].Value; + gamename = Regex.Match(line, @"^ROMs required for \S*? string.Empty(.*?)string.Empty\.").Groups[1].Value; } // If we have a machine with no required roms (usually internal devices), skip it @@ -99,20 +91,16 @@ namespace SabreTools.Library.DatFiles else { // First, we preprocess the line so that the rom name is consistently correct - string romname = ""; + string romname = string.Empty; string[] split = line.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries); // If the line doesn't have the 4 spaces of padding, check for 3 if (split.Length == 1) - { split = line.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries); - } // If the split is still unsuccessful, log it and skip if (split.Length == 1) - { - Globals.Logger.Warning("Possibly malformed line: '{0}'", line); - } + Globals.Logger.Warning($"Possibly malformed line: '{line}'"); romname = split[0]; line = line.Substring(romname.Length); @@ -153,9 +141,7 @@ namespace SabreTools.Library.DatFiles else if (split.Length == 3) { if (!Int64.TryParse(split[0], out long size)) - { size = 0; - } Rom rom = new Rom() { @@ -188,9 +174,7 @@ namespace SabreTools.Library.DatFiles else if (split.Length == 5 && line.EndsWith("BAD_DUMP")) { if (!Int64.TryParse(split[0], out long size)) - { size = 0; - } Rom rom = new Rom() { @@ -210,9 +194,7 @@ namespace SabreTools.Library.DatFiles else if (split.Length == 5 && line.EndsWith("NO GOOD DUMP KNOWN")) { if (!Int64.TryParse(split[0], out long size)) - { size = 0; - } Rom rom = new Rom() { @@ -229,7 +211,7 @@ namespace SabreTools.Library.DatFiles // If we have something else, it's invalid else { - Globals.Logger.Warning("Invalid line detected: '{0} {1}'", romname, line); + Globals.Logger.Warning($"Invalid line detected: '{romname} {line}'"); } } } @@ -246,13 +228,13 @@ namespace SabreTools.Library.DatFiles { try { - Globals.Logger.User("Opening file for writing: {0}", outfile); + Globals.Logger.User($"Opening file for writing: {outfile}"); FileStream fs = Utilities.TryCreate(outfile); // If we get back null for some reason, just log and return if (fs == null) { - Globals.Logger.Warning("File '{0}' could not be created for writing! Please check to see if the file is writable", outfile); + Globals.Logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable"); return false; } @@ -285,22 +267,18 @@ namespace SabreTools.Library.DatFiles // If we have a different game and we're not at the start of the list, output the end of last item if (lastgame != null && lastgame.ToLowerInvariant() != rom.MachineName.ToLowerInvariant()) - { WriteEndGame(sw); - } // If we have a new game, output the beginning of the new item if (lastgame == null || lastgame.ToLowerInvariant() != rom.MachineName.ToLowerInvariant()) - { WriteStartGame(sw, rom); - } // If we have a "null" game (created by DATFromDir or something similar), log it to file if (rom.ItemType == ItemType.Rom && ((Rom)rom).Size == -1 && ((Rom)rom).CRC == "null") { - Globals.Logger.Verbose("Empty folder found: {0}", rom.MachineName); + Globals.Logger.Verbose($"Empty folder found: {rom.MachineName}"); rom.Name = (rom.Name == "null" ? "-" : rom.Name); ((Rom)rom).Size = Constants.SizeZero; @@ -344,13 +322,11 @@ namespace SabreTools.Library.DatFiles try { // No game should start with a path separator - if (rom.MachineName.StartsWith(Path.DirectorySeparatorChar.ToString())) - { - rom.MachineName = rom.MachineName.Substring(1); - } + rom.MachineName = rom.MachineName.TrimStart(Path.DirectorySeparatorChar); - string state = "ROMs required for driver \"" + (!ExcludeFields[(int)Field.MachineName] ? rom.MachineName : "") + "\".\n" + - "Name Size Checksum\n"; + // Build the state based on excluded fields + string state = $"ROMs required for driver \"{rom.GetField(Field.MachineName, ExcludeFields)}\".\n"; + state += "Name Size Checksum\n"; sw.Write(state); sw.Flush(); @@ -391,110 +367,94 @@ namespace SabreTools.Library.DatFiles /// Write out DatItem using the supplied StreamWriter /// /// StreamWriter to output to - /// DatItem object to be output + /// DatItem object to be output /// True if blank roms should be skipped on output, false otherwise (default) /// True if the data was written, false on error - private bool WriteDatItem(StreamWriter sw, DatItem rom, bool ignoreblanks = false) + private bool WriteDatItem(StreamWriter sw, DatItem datItem, bool ignoreblanks = false) { // If we are in ignore blanks mode AND we have a blank (0-size) rom, skip - if (ignoreblanks - && (rom.ItemType == ItemType.Rom - && (((Rom)rom).Size == 0 || ((Rom)rom).Size == -1))) - { + if (ignoreblanks && (datItem.ItemType == ItemType.Rom && ((datItem as Rom).Size == 0 || (datItem as Rom).Size == -1))) return true; - } try { - string state = ""; + string state = string.Empty; // Pre-process the item name - ProcessItemName(rom, true); + ProcessItemName(datItem, true); - switch (rom.ItemType) + // Build the state based on excluded fields + switch (datItem.ItemType) { case ItemType.Archive: case ItemType.BiosSet: case ItemType.Release: case ItemType.Sample: - // We don't output these at all + // We don't output these at all for Listrom break; + case ItemType.Disk: + var disk = datItem as Disk; + // The name is padded out to a particular length - if (rom.Name.Length < 43) - { - state += rom.Name.PadRight(43, ' '); - } + if (disk.Name.Length < 43) + state += disk.Name.PadRight(43, ' '); else - { - state += rom.Name + " "; - } + state += disk.Name + " "; // If we have a baddump, put the first indicator - if (!ExcludeFields[(int)Field.Status] && ((Disk)rom).ItemStatus == ItemStatus.BadDump) - { + if (!ExcludeFields[(int)Field.Status] && disk.ItemStatus == ItemStatus.BadDump) state += " BAD"; - } // If we have a nodump, write out the indicator - if (!ExcludeFields[(int)Field.Status] && ((Disk)rom).ItemStatus == ItemStatus.Nodump) - { + if (!ExcludeFields[(int)Field.Status] && disk.ItemStatus == ItemStatus.Nodump) state += " NO GOOD DUMP KNOWN"; - } + // Otherwise, write out the SHA-1 hash - else if (!ExcludeFields[(int)Field.SHA1]) - { - state += " SHA1(" + ((Disk)rom).SHA1 + ")"; - } + else if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields))) + state += $" SHA1({disk.SHA1})"; // If we have a baddump, put the second indicator - if (!ExcludeFields[(int)Field.Status] && ((Disk)rom).ItemStatus == ItemStatus.BadDump) - { + if (!ExcludeFields[(int)Field.Status] && disk.ItemStatus == ItemStatus.BadDump) state += " BAD_DUMP"; - } state += "\n"; break; + case ItemType.Rom: + var rom = datItem as Rom; + // The name is padded out to a particular length if (rom.Name.Length < 40) - { - state += rom.Name.PadRight(43 - (((Rom)rom).Size.ToString().Length), ' '); - } + state += rom.Name.PadRight(43 - rom.Size.ToString().Length, ' '); else - { state += rom.Name + " "; - } // If we don't have a nodump, write out the size - if (((Rom)rom).ItemStatus != ItemStatus.Nodump) - { - state += ((Rom)rom).Size; - } + if (rom.ItemStatus != ItemStatus.Nodump) + state += rom.Size; // If we have a baddump, put the first indicator - if (!ExcludeFields[(int)Field.Status] && ((Rom)rom).ItemStatus == ItemStatus.BadDump) - { + if (!ExcludeFields[(int)Field.Status] && rom.ItemStatus == ItemStatus.BadDump) state += " BAD"; - } // If we have a nodump, write out the indicator - if (!ExcludeFields[(int)Field.Status] && ((Rom)rom).ItemStatus == ItemStatus.Nodump) + if (!ExcludeFields[(int)Field.Status] && rom.ItemStatus == ItemStatus.Nodump) { state += " NO GOOD DUMP KNOWN"; } // Otherwise, write out the CRC and SHA-1 hashes else { - state += (!ExcludeFields[(int)Field.CRC] ? " CRC(" + ((Rom)rom).CRC + ")" : ""); - state += (!ExcludeFields[(int)Field.SHA1] ? " SHA1(" + ((Rom)rom).SHA1 + ")" : ""); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CRC, ExcludeFields))) + state += $" CRC({rom.CRC})"; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields))) + state += $" SHA1({rom.SHA1})"; } // If we have a baddump, put the second indicator - if (!ExcludeFields[(int)Field.Status] && ((Rom)rom).ItemStatus == ItemStatus.BadDump) - { + if (!ExcludeFields[(int)Field.Status] && rom.ItemStatus == ItemStatus.BadDump) state += " BAD_DUMP"; - } state += "\n"; break; diff --git a/SabreTools.Library/DatFiles/Listxml.cs b/SabreTools.Library/DatFiles/Listxml.cs index 937345aa..62425ff2 100644 --- a/SabreTools.Library/DatFiles/Listxml.cs +++ b/SabreTools.Library/DatFiles/Listxml.cs @@ -1,20 +1,12 @@ using System; using System.Collections.Generic; -using System.Net; +using System.IO; using System.Text; using System.Xml; + using SabreTools.Library.Data; using SabreTools.Library.DatItems; using SabreTools.Library.Tools; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using FileStream = System.IO.FileStream; -using StreamWriter = System.IO.StreamWriter; -#endif using NaturalSort; namespace SabreTools.Library.DatFiles @@ -62,9 +54,7 @@ namespace SabreTools.Library.DatFiles // If we got a null reader, just return if (xtr == null) - { return; - } // Otherwise, read the file to the end try @@ -82,27 +72,30 @@ namespace SabreTools.Library.DatFiles switch (xtr.Name) { case "mame": - Name = (String.IsNullOrWhiteSpace(Name) ? xtr.GetAttribute("build") : Name); - Description = (String.IsNullOrWhiteSpace(Description) ? Name : Name); + Name = (string.IsNullOrWhiteSpace(Name) ? xtr.GetAttribute("build") : Name); + Description = (string.IsNullOrWhiteSpace(Description) ? Name : Name); // string mame_debug = xtr.GetAttribute("debug"); // (yes|no) "no" // string mame_mameconfig = xtr.GetAttribute("mameconfig"); CDATA xtr.Read(); break; + // Handle M1 DATs since they're 99% the same as a SL DAT case "m1": - Name = (String.IsNullOrWhiteSpace(Name) ? "M1" : Name); - Description = (String.IsNullOrWhiteSpace(Description) ? "M1" : Description); - Version = (String.IsNullOrWhiteSpace(Version) ? xtr.GetAttribute("version") ?? "" : Version); + Name = (string.IsNullOrWhiteSpace(Name) ? "M1" : Name); + Description = (string.IsNullOrWhiteSpace(Description) ? "M1" : Description); + Version = (string.IsNullOrWhiteSpace(Version) ? xtr.GetAttribute("version") ?? string.Empty : Version); xtr.Read(); break; + // We want to process the entire subtree of the machine case "game": // Some older DATs still use "game" case "machine": - ReadMachine(xtr.ReadSubtree(), filename, sysid, srcid, keep, clean, remUnicode); + ReadMachine(xtr.ReadSubtree(), filename, sysid, srcid, clean, remUnicode); // Skip the machine now that we've processed it xtr.Skip(); break; + default: xtr.Read(); break; @@ -111,7 +104,7 @@ namespace SabreTools.Library.DatFiles } catch (Exception ex) { - Globals.Logger.Warning("Exception found while parsing '{0}': {1}", filename, ex); + Globals.Logger.Warning($"Exception found while parsing '{filename}': {ex}"); // For XML errors, just skip the affected node xtr?.Read(); @@ -127,7 +120,6 @@ namespace SabreTools.Library.DatFiles /// Name of the file to be parsed /// System ID for the DAT /// Source ID for the DAT - /// True if full pathnames are to be kept, false otherwise (default) /// True if game names are sanitized, false otherwise (default) /// True if we should remove non-ASCII characters from output, false otherwise (default) private void ReadMachine( @@ -139,37 +131,30 @@ namespace SabreTools.Library.DatFiles int srcid, // Miscellaneous - bool keep, bool clean, bool remUnicode) { // If we have an empty machine, skip it if (reader == null) - { return; - } // Otherwise, add what is possible reader.MoveToContent(); - string key = ""; + string key = string.Empty; string temptype = reader.Name; bool containsItems = false; // Create a new machine MachineType machineType = MachineType.NULL; if (Utilities.GetYesNo(reader.GetAttribute("isbios")) == true) - { machineType |= MachineType.Bios; - } + if (Utilities.GetYesNo(reader.GetAttribute("isdevice")) == true) - { machineType |= MachineType.Device; - } + if (Utilities.GetYesNo(reader.GetAttribute("ismechanical")) == true) - { machineType |= MachineType.Mechanical; - } Machine machine = new Machine { @@ -178,11 +163,11 @@ namespace SabreTools.Library.DatFiles SourceFile = reader.GetAttribute("sourcefile"), Runnable = Utilities.GetYesNo(reader.GetAttribute("runnable")), - Comment = "", + Comment = string.Empty, - CloneOf = reader.GetAttribute("cloneof") ?? "", - RomOf = reader.GetAttribute("romof") ?? "", - SampleOf = reader.GetAttribute("sampleof") ?? "", + CloneOf = reader.GetAttribute("cloneof") ?? string.Empty, + RomOf = reader.GetAttribute("romof") ?? string.Empty, + SampleOf = reader.GetAttribute("sampleof") ?? string.Empty, Devices = new List(), SlotOptions = new List(), @@ -204,12 +189,15 @@ namespace SabreTools.Library.DatFiles case "description": machine.Description = reader.ReadElementContentAsString(); break; + case "year": machine.Year = reader.ReadElementContentAsString(); break; + case "manufacturer": machine.Manufacturer = reader.ReadElementContentAsString(); break; + case "biosset": containsItems = true; @@ -231,6 +219,7 @@ namespace SabreTools.Library.DatFiles reader.Read(); break; + case "rom": containsItems = true; @@ -264,6 +253,7 @@ namespace SabreTools.Library.DatFiles reader.Read(); break; + case "disk": containsItems = true; @@ -295,15 +285,15 @@ namespace SabreTools.Library.DatFiles reader.Read(); break; + case "device_ref": string device_ref_name = reader.GetAttribute("name"); if (!machine.Devices.Contains(device_ref_name)) - { machine.Devices.Add(device_ref_name); - } reader.Read(); break; + case "sample": containsItems = true; @@ -323,6 +313,7 @@ namespace SabreTools.Library.DatFiles reader.Read(); break; + case "chip": // string chip_name = reader.GetAttribute("name"); // string chip_tag = reader.GetAttribute("tag"); @@ -331,6 +322,7 @@ namespace SabreTools.Library.DatFiles reader.Read(); break; + case "display": // string display_tag = reader.GetAttribute("tag"); // string display_type = reader.GetAttribute("type"); // (raster|vector|lcd|svg|unknown) @@ -349,11 +341,13 @@ namespace SabreTools.Library.DatFiles reader.Read(); break; + case "sound": // string sound_channels = reader.GetAttribute("channels"); reader.Read(); break; + case "condition": // string condition_tag = reader.GetAttribute("tag"); // string condition_mask = reader.GetAttribute("mask"); @@ -362,6 +356,7 @@ namespace SabreTools.Library.DatFiles reader.Read(); break; + case "input": // bool? input_service = Utilities.GetYesNo(reader.GetAttribute("service")); // bool? input_tilt = Utilities.GetYesNo(reader.GetAttribute("tilt")); @@ -384,6 +379,7 @@ namespace SabreTools.Library.DatFiles reader.Skip(); break; + case "dipswitch": // string dipswitch_name = reader.GetAttribute("name"); // string dipswitch_tag = reader.GetAttribute("tag"); @@ -401,6 +397,7 @@ namespace SabreTools.Library.DatFiles reader.Skip(); break; + case "configuration": // string configuration_name = reader.GetAttribute("name"); // string configuration_tag = reader.GetAttribute("tag"); @@ -418,6 +415,7 @@ namespace SabreTools.Library.DatFiles reader.Skip(); break; + case "port": // string port_tag = reader.GetAttribute("tag"); @@ -426,6 +424,7 @@ namespace SabreTools.Library.DatFiles reader.Skip(); break; + case "adjuster": // string adjuster_name = reader.GetAttribute("name"); // bool? adjuster_default = Utilities.GetYesNo(reader.GetAttribute("default")); @@ -446,6 +445,7 @@ namespace SabreTools.Library.DatFiles reader.Read(); break; + case "feature": // string feature_type = reader.GetAttribute("type"); // (protection|palette|graphics|sound|controls|keyboard|mouse|microphone|camera|disk|printer|lan|wan|timing) // string feature_status = reader.GetAttribute("status"); // (unemulated|imperfect) @@ -469,6 +469,7 @@ namespace SabreTools.Library.DatFiles reader.Skip(); break; + case "slot": // string slot_name = reader.GetAttribute("name"); ReadSlot(reader.ReadSubtree(), machine); @@ -476,6 +477,7 @@ namespace SabreTools.Library.DatFiles // Skip the slot now that we've processed it reader.Skip(); break; + case "softwarelist": // string softwarelist_name = reader.GetAttribute("name"); // string softwarelist_status = reader.GetAttribute("status"); // (original|compatible) @@ -483,11 +485,13 @@ namespace SabreTools.Library.DatFiles reader.Read(); break; + case "ramoption": // string ramoption_default = reader.GetAttribute("default"); reader.Read(); break; + default: reader.Read(); break; @@ -519,9 +523,7 @@ namespace SabreTools.Library.DatFiles { // If we have an empty machine, skip it if (reader == null) - { return; - } // Otherwise, add what is possible reader.MoveToContent(); @@ -548,6 +550,7 @@ namespace SabreTools.Library.DatFiles // bool? slotoption_default = Utilities.GetYesNo(reader.GetAttribute("default")); reader.Read(); break; + default: reader.Read(); break; @@ -565,20 +568,21 @@ namespace SabreTools.Library.DatFiles { try { - Globals.Logger.User("Opening file for writing: {0}", outfile); + Globals.Logger.User($"Opening file for writing: {outfile}"); FileStream fs = Utilities.TryCreate(outfile); // If we get back null for some reason, just log and return if (fs == null) { - Globals.Logger.Warning("File '{0}' could not be created for writing! Please check to see if the file is writable", outfile); + Globals.Logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable"); return false; } - StreamWriter sw = new StreamWriter(fs, new UTF8Encoding(false)); + XmlTextWriter xtw = new XmlTextWriter(fs, new UTF8Encoding(false)); + xtw.Formatting = Formatting.Indented; // Write out the header - WriteHeader(sw); + WriteHeader(xtw); // Write out each of the machines and roms string lastgame = null; @@ -607,29 +611,25 @@ namespace SabreTools.Library.DatFiles // If we have a different game and we're not at the start of the list, output the end of last item if (lastgame != null && lastgame.ToLowerInvariant() != rom.MachineName.ToLowerInvariant()) - { - WriteEndGame(sw); - } + WriteEndGame(xtw); // If we have a new game, output the beginning of the new item if (lastgame == null || lastgame.ToLowerInvariant() != rom.MachineName.ToLowerInvariant()) - { - WriteStartGame(sw, rom); - } + WriteStartGame(xtw, rom); // If we have a "null" game (created by DATFromDir or something similar), log it to file if (rom.ItemType == ItemType.Rom && ((Rom)rom).Size == -1 && ((Rom)rom).CRC == "null") { - Globals.Logger.Verbose("Empty folder found: {0}", rom.MachineName); + Globals.Logger.Verbose($"Empty folder found: {rom.MachineName}"); lastgame = rom.MachineName; continue; } // Now, output the rom data - WriteDatItem(sw, rom, ignoreblanks); + WriteDatItem(xtw, rom, ignoreblanks); // Set the new data to compare against lastgame = rom.MachineName; @@ -637,10 +637,10 @@ namespace SabreTools.Library.DatFiles } // Write the file footer out - WriteFooter(sw); + WriteFooter(xtw); Globals.Logger.Verbose("File written!" + Environment.NewLine); - sw.Dispose(); + xtw.Dispose(); fs.Dispose(); } catch (Exception ex) @@ -655,21 +655,20 @@ namespace SabreTools.Library.DatFiles /// /// Write out DAT header using the supplied StreamWriter /// - /// StreamWriter to output to + /// XmlTextWriter to output to /// True if the data was written, false on error - private bool WriteHeader(StreamWriter sw) + private bool WriteHeader(XmlTextWriter xtw) { try { - string header = "\n" + - "\n\n"; + xtw.WriteStartDocument(); - // Write the header out - sw.Write(header); - sw.Flush(); + xtw.WriteStartElement("mame"); + xtw.WriteAttributeString("build", Name); + //xtw.WriteAttributeString("debug", Debug); + //xtw.WriteAttributeString("mameconfig", MameConfig); + + xtw.Flush(); } catch (Exception ex) { @@ -683,51 +682,66 @@ namespace SabreTools.Library.DatFiles /// /// Write out Game start using the supplied StreamWriter /// - /// StreamWriter to output to - /// DatItem object to be output + /// XmlTextWriter to output to + /// DatItem object to be output /// True if the data was written, false on error - private bool WriteStartGame(StreamWriter sw, DatItem rom) + private bool WriteStartGame(XmlTextWriter xtw, DatItem datItem) { try { // No game should start with a path separator - if (rom.MachineName.StartsWith(Path.DirectorySeparatorChar.ToString())) + datItem.MachineName = datItem.MachineName.TrimStart(Path.DirectorySeparatorChar); + + // Build the state based on excluded fields + xtw.WriteStartElement("machine"); + xtw.WriteAttributeString("name", datItem.GetField(Field.MachineName, ExcludeFields)); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SourceFile, ExcludeFields))) + xtw.WriteElementString("sourcefile", datItem.SourceFile); + + if (!ExcludeFields[(int)Field.MachineType]) { - rom.MachineName = rom.MachineName.Substring(1); + if ((datItem.MachineType & MachineType.Bios) != 0) + xtw.WriteAttributeString("isbios", "yes"); + if ((datItem.MachineType & MachineType.Device) != 0) + xtw.WriteAttributeString("isdevice", "yes"); + if ((datItem.MachineType & MachineType.Mechanical) != 0) + xtw.WriteAttributeString("ismechanical", "yes"); } - string state = "\t\n" - + (!ExcludeFields[(int)Field.Description] ? "\t\t" + WebUtility.HtmlEncode(rom.MachineDescription) + "\n" : "") - + (!ExcludeFields[(int)Field.Year] && rom.Year != null ? "\t\t" + WebUtility.HtmlEncode(rom.Year) + "\n" : "") - + (!ExcludeFields[(int)Field.Publisher] && rom.Publisher != null ? "\t\t" + WebUtility.HtmlEncode(rom.Publisher) + "\n" : ""); - - if (!ExcludeFields[(int)Field.Infos] && rom.Infos != null && rom.Infos.Count > 0) + if (!ExcludeFields[(int)Field.Runnable]) { - foreach (Tuple kvp in rom.Infos) + if (datItem.Runnable == true) + xtw.WriteAttributeString("runnable", "yes"); + else if (datItem.Runnable == false) + xtw.WriteAttributeString("runnable", "no"); + } + + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CloneOf, ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.CloneOf, StringComparison.OrdinalIgnoreCase)) + xtw.WriteAttributeString("cloneof", datItem.CloneOf); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RomOf, ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.RomOf, StringComparison.OrdinalIgnoreCase)) + xtw.WriteAttributeString("romof", datItem.RomOf); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SampleOf, ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.SampleOf, StringComparison.OrdinalIgnoreCase)) + xtw.WriteAttributeString("sampleof", datItem.SampleOf); + + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Description, ExcludeFields))) + xtw.WriteElementString("description", datItem.MachineDescription); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Year, ExcludeFields))) + xtw.WriteElementString("year", datItem.Year); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Publisher, ExcludeFields))) + xtw.WriteElementString("publisher", datItem.Publisher); + + if (!ExcludeFields[(int)Field.Infos] && datItem.Infos != null && datItem.Infos.Count > 0) + { + foreach (Tuple kvp in datItem.Infos) { - state += "\t\t\n"; + xtw.WriteStartElement("info"); + xtw.WriteAttributeString("name", kvp.Item1); + xtw.WriteAttributeString("value", kvp.Item2); + xtw.WriteEndElement(); } } - sw.Write(state); - sw.Flush(); + xtw.Flush(); } catch (Exception ex) { @@ -741,16 +755,16 @@ namespace SabreTools.Library.DatFiles /// /// Write out Game start using the supplied StreamWriter /// - /// StreamWriter to output to + /// XmlTextWriter to output to /// True if the data was written, false on error - private bool WriteEndGame(StreamWriter sw) + private bool WriteEndGame(XmlTextWriter xtw) { try { - string state = "\t\n"; + // End machine + xtw.WriteEndElement(); - sw.Write(state); - sw.Flush(); + xtw.Flush(); } catch (Exception ex) { @@ -764,84 +778,109 @@ namespace SabreTools.Library.DatFiles /// /// Write out DatItem using the supplied StreamWriter /// - /// StreamWriter to output to - /// DatItem object to be output + /// XmlTextWriter to output to + /// DatItem object to be output /// True if blank roms should be skipped on output, false otherwise (default) /// True if the data was written, false on error - private bool WriteDatItem(StreamWriter sw, DatItem rom, bool ignoreblanks = false) + private bool WriteDatItem(XmlTextWriter xtw, DatItem datItem, bool ignoreblanks = false) { // If we are in ignore blanks mode AND we have a blank (0-size) rom, skip - if (ignoreblanks - && (rom.ItemType == ItemType.Rom - && (((Rom)rom).Size == 0 || ((Rom)rom).Size == -1))) - { + if (ignoreblanks && (datItem.ItemType == ItemType.Rom && ((datItem as Rom).Size == 0 || (datItem as Rom).Size == -1))) return true; - } try { - string state = ""; - // Pre-process the item name - ProcessItemName(rom, true); + ProcessItemName(datItem, true); - switch (rom.ItemType) + // Build the state based on excluded fields + switch (datItem.ItemType) { - case ItemType.Archive: - break; - case ItemType.BiosSet: // TODO: Separate out MachineDescription from Description - state += "\t\t\n"; + case ItemType.BiosSet: + var biosSet = datItem as BiosSet; + xtw.WriteStartElement("biosset"); + xtw.WriteAttributeString("name", biosSet.GetField(Field.Name, ExcludeFields)); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.BiosDescription, ExcludeFields))) + xtw.WriteAttributeString("description", biosSet.Description); + if (!ExcludeFields[(int)Field.Default] && biosSet.Default != null) + xtw.WriteAttributeString("default", biosSet.Default.ToString().ToLowerInvariant()); + xtw.WriteEndElement(); break; + case ItemType.Disk: - state += "\t\t\n"; - break; - case ItemType.Release: - //TODO: Am I missing this? + var disk = datItem as Disk; + xtw.WriteStartElement("disk"); + xtw.WriteAttributeString("name", disk.GetField(Field.Name, ExcludeFields)); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields))) + xtw.WriteAttributeString("md5", disk.MD5.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, ExcludeFields))) + xtw.WriteAttributeString("ripemd160", disk.RIPEMD160.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields))) + xtw.WriteAttributeString("sha1", disk.SHA1.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, ExcludeFields))) + xtw.WriteAttributeString("sha256", disk.SHA256.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, ExcludeFields))) + xtw.WriteAttributeString("sha384", disk.SHA384.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, ExcludeFields))) + xtw.WriteAttributeString("sha512", disk.SHA512.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Merge, ExcludeFields))) + xtw.WriteAttributeString("merge", disk.MergeTag); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Region, ExcludeFields))) + xtw.WriteAttributeString("region", disk.Region); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Index, ExcludeFields))) + xtw.WriteAttributeString("index", disk.Index); + if (!ExcludeFields[(int)Field.Writable] && disk.Writable != null) + xtw.WriteAttributeString("writable", disk.Writable == true ? "yes" : "no"); + if (!ExcludeFields[(int)Field.Status] && disk.ItemStatus != ItemStatus.None) + xtw.WriteAttributeString("status", disk.ItemStatus.ToString()); + if (!ExcludeFields[(int)Field.Optional] && disk.Optional != null) + xtw.WriteAttributeString("optional", disk.Optional == true ? "yes" : "no"); + xtw.WriteEndElement(); break; + case ItemType.Rom: - state += "\t\t\n"; + var rom = datItem as Rom; + xtw.WriteStartElement("rom"); + xtw.WriteAttributeString("name", rom.GetField(Field.Name, ExcludeFields)); + if (!ExcludeFields[(int)Field.Size] && rom.Size != -1) + xtw.WriteAttributeString("size", rom.Size.ToString()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields))) + xtw.WriteAttributeString("crc", rom.CRC.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields))) + xtw.WriteAttributeString("md5", rom.MD5.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, ExcludeFields))) + xtw.WriteAttributeString("ripemd160", rom.RIPEMD160.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields))) + xtw.WriteAttributeString("sha1", rom.SHA1.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, ExcludeFields))) + xtw.WriteAttributeString("sha256", rom.SHA256.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, ExcludeFields))) + xtw.WriteAttributeString("sha384", rom.SHA384.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, ExcludeFields))) + xtw.WriteAttributeString("sha512", rom.SHA512.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Bios, ExcludeFields))) + xtw.WriteAttributeString("bios", rom.Bios); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Merge, ExcludeFields))) + xtw.WriteAttributeString("merge", rom.MergeTag); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Region, ExcludeFields))) + xtw.WriteAttributeString("region", rom.Region); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Offset, ExcludeFields))) + xtw.WriteAttributeString("offset", rom.Offset); + if (!ExcludeFields[(int)Field.Status] && rom.ItemStatus != ItemStatus.None) + xtw.WriteAttributeString("status", rom.ItemStatus.ToString().ToLowerInvariant()); + if (!ExcludeFields[(int)Field.Optional] && rom.Optional != null) + xtw.WriteAttributeString("optional", rom.Optional == true ? "yes" : "no"); + xtw.WriteEndElement(); break; + case ItemType.Sample: - state += "\t\t\n"; + xtw.WriteStartElement("sample"); + xtw.WriteAttributeString("name", datItem.GetField(Field.Name, ExcludeFields)); + xtw.WriteEndElement(); break; } - sw.Write(state); - sw.Flush(); + xtw.Flush(); } catch (Exception ex) { @@ -855,17 +894,19 @@ namespace SabreTools.Library.DatFiles /// /// Write out DAT footer using the supplied StreamWriter /// - /// StreamWriter to output to + /// XmlTextWriter to output to /// True if the data was written, false on error - private bool WriteFooter(StreamWriter sw) + private bool WriteFooter(XmlTextWriter xtw) { try { - string footer = "\t\n\n"; + // End machine + xtw.WriteEndElement(); - // Write the footer out - sw.Write(footer); - sw.Flush(); + // End mame + xtw.WriteEndElement(); + + xtw.Flush(); } catch (Exception ex) { diff --git a/SabreTools.Library/DatFiles/Logiqx.cs b/SabreTools.Library/DatFiles/Logiqx.cs index 8564f173..5a932b9e 100644 --- a/SabreTools.Library/DatFiles/Logiqx.cs +++ b/SabreTools.Library/DatFiles/Logiqx.cs @@ -1,22 +1,14 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; -using System.Net; using System.Text; using System.Text.RegularExpressions; using System.Xml; + using SabreTools.Library.Data; using SabreTools.Library.DatItems; using SabreTools.Library.Tools; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using FileStream = System.IO.FileStream; -using StreamWriter = System.IO.StreamWriter; -#endif using NaturalSort; namespace SabreTools.Library.DatFiles @@ -29,17 +21,17 @@ namespace SabreTools.Library.DatFiles internal class Logiqx : DatFile { // Private instance variables specific to Logiqx DATs - bool _depreciated; + private readonly bool _deprecated; /// /// Constructor designed for casting a base DatFile /// /// Parent DatFile to copy from - /// True if the output uses "game", false if the output uses "machine" - public Logiqx(DatFile datFile, bool depreciated) + /// True if the output uses "game", false if the output uses "machine" + public Logiqx(DatFile datFile, bool deprecated) : base(datFile, cloneHeader: false) { - _depreciated = depreciated; + _deprecated = deprecated; } /// @@ -69,9 +61,7 @@ namespace SabreTools.Library.DatFiles // If we got a null reader, just return if (xtr == null) - { return; - } // Otherwise, read the file to the end try @@ -84,9 +74,7 @@ namespace SabreTools.Library.DatFiles { // If we're ending a dir, remove the last item from the dirs list, if possible if (xtr.Name == "dir" && dirs.Count > 0) - { dirs.RemoveAt(dirs.Count - 1); - } xtr.Read(); continue; @@ -100,6 +88,7 @@ namespace SabreTools.Library.DatFiles // string debug = xtr.GetAttribute("debug"); // (yes|no) "no" xtr.Read(); break; + // We want to process the entire subtree of the header case "header": ReadHeader(xtr.ReadSubtree(), keep); @@ -107,12 +96,14 @@ namespace SabreTools.Library.DatFiles // Skip the header node now that we've processed it xtr.Skip(); break; + // Unique to RomVault-created DATs case "dir": Type = "SuperDAT"; - dirs.Add(xtr.GetAttribute("name") ?? ""); + dirs.Add(xtr.GetAttribute("name") ?? string.Empty); xtr.Read(); break; + // We want to process the entire subtree of the game case "machine": // New-style Logiqx case "game": // Old-style Logiqx @@ -121,6 +112,7 @@ namespace SabreTools.Library.DatFiles // Skip the machine now that we've processed it xtr.Skip(); break; + default: xtr.Read(); break; @@ -129,7 +121,7 @@ namespace SabreTools.Library.DatFiles } catch (Exception ex) { - Globals.Logger.Warning("Exception found while parsing '{0}': {1}", filename, ex); + Globals.Logger.Warning($"Exception found while parsing '{filename}': {ex}"); // For XML errors, just skip the affected node xtr?.Read(); @@ -149,9 +141,7 @@ namespace SabreTools.Library.DatFiles // If there's no subtree to the header, skip it if (reader == null) - { return; - } // Otherwise, add what is possible reader.MoveToContent(); @@ -166,111 +156,127 @@ namespace SabreTools.Library.DatFiles } // Get all header items (ONLY OVERWRITE IF THERE'S NO DATA) - string content = ""; + string content = string.Empty; switch (reader.Name) { case "name": content = reader.ReadElementContentAsString(); ; - Name = (String.IsNullOrWhiteSpace(Name) ? content : Name); + Name = (string.IsNullOrWhiteSpace(Name) ? content : Name); superdat = superdat || content.Contains(" - SuperDAT"); if (keep && superdat) { - Type = (String.IsNullOrWhiteSpace(Type) ? "SuperDAT" : Type); + Type = (string.IsNullOrWhiteSpace(Type) ? "SuperDAT" : Type); } break; + case "description": content = reader.ReadElementContentAsString(); - Description = (String.IsNullOrWhiteSpace(Description) ? content : Description); + Description = (string.IsNullOrWhiteSpace(Description) ? content : Description); break; + case "rootdir": // This is exclusive to TruRip XML content = reader.ReadElementContentAsString(); - RootDir = (String.IsNullOrWhiteSpace(RootDir) ? content : RootDir); + RootDir = (string.IsNullOrWhiteSpace(RootDir) ? content : RootDir); break; + case "category": content = reader.ReadElementContentAsString(); - Category = (String.IsNullOrWhiteSpace(Category) ? content : Category); + Category = (string.IsNullOrWhiteSpace(Category) ? content : Category); break; + case "version": content = reader.ReadElementContentAsString(); - Version = (String.IsNullOrWhiteSpace(Version) ? content : Version); + Version = (string.IsNullOrWhiteSpace(Version) ? content : Version); break; + case "date": content = reader.ReadElementContentAsString(); - Date = (String.IsNullOrWhiteSpace(Date) ? content.Replace(".", "/") : Date); + Date = (string.IsNullOrWhiteSpace(Date) ? content.Replace(".", "/") : Date); break; + case "author": content = reader.ReadElementContentAsString(); - Author = (String.IsNullOrWhiteSpace(Author) ? content : Author); + Author = (string.IsNullOrWhiteSpace(Author) ? content : Author); break; + case "email": content = reader.ReadElementContentAsString(); - Email = (String.IsNullOrWhiteSpace(Email) ? content : Email); + Email = (string.IsNullOrWhiteSpace(Email) ? content : Email); break; + case "homepage": content = reader.ReadElementContentAsString(); - Homepage = (String.IsNullOrWhiteSpace(Homepage) ? content : Homepage); + Homepage = (string.IsNullOrWhiteSpace(Homepage) ? content : Homepage); break; + case "url": content = reader.ReadElementContentAsString(); - Url = (String.IsNullOrWhiteSpace(Url) ? content : Url); + Url = (string.IsNullOrWhiteSpace(Url) ? content : Url); break; + case "comment": content = reader.ReadElementContentAsString(); - Comment = (String.IsNullOrWhiteSpace(Comment) ? content : Comment); + Comment = (string.IsNullOrWhiteSpace(Comment) ? content : Comment); break; + case "type": // This is exclusive to TruRip XML content = reader.ReadElementContentAsString(); - Type = (String.IsNullOrWhiteSpace(Type) ? content : Type); + Type = (string.IsNullOrWhiteSpace(Type) ? content : Type); superdat = superdat || content.Contains("SuperDAT"); break; + case "clrmamepro": - if (String.IsNullOrWhiteSpace(Header)) - { + if (string.IsNullOrWhiteSpace(Header)) Header = reader.GetAttribute("header"); - } + if (ForceMerging == ForceMerging.None) - { ForceMerging = Utilities.GetForceMerging(reader.GetAttribute("forcemerging")); - } + if (ForceNodump == ForceNodump.None) - { ForceNodump = Utilities.GetForceNodump(reader.GetAttribute("forcenodump")); - } + if (ForcePacking == ForcePacking.None) - { ForcePacking = Utilities.GetForcePacking(reader.GetAttribute("forcepacking")); - } + reader.Read(); break; + case "romcenter": if (reader.GetAttribute("plugin") != null) { // CDATA } + if (reader.GetAttribute("rommode") != null) { // (merged|split|unmerged) "split" } + if (reader.GetAttribute("biosmode") != null) { // merged|split|unmerged) "split" } + if (reader.GetAttribute("samplemode") != null) { // (merged|unmerged) "merged" } + if (reader.GetAttribute("lockrommode") != null) { // (yes|no) "no" } + if (reader.GetAttribute("lockbiosmode") != null) { // (yes|no) "no" } + if (reader.GetAttribute("locksamplemode") != null) { // (yes|no) "no" } + reader.Read(); break; default: @@ -307,33 +313,27 @@ namespace SabreTools.Library.DatFiles { // If we have an empty machine, skip it if (reader == null) - { return; - } // Otherwise, add what is possible reader.MoveToContent(); - string key = ""; + string key = string.Empty; string temptype = reader.Name; bool containsItems = false; // Create a new machine MachineType machineType = MachineType.NULL; if (Utilities.GetYesNo(reader.GetAttribute("isbios")) == true) - { machineType |= MachineType.Bios; - } - if (Utilities.GetYesNo(reader.GetAttribute("isdevice")) == true) // Listxml-specific, used by older DATs - { - machineType |= MachineType.Device; - } - if (Utilities.GetYesNo(reader.GetAttribute("ismechanical")) == true) // Listxml-specific, used by older DATs - { - machineType |= MachineType.Mechanical; - } - string dirsString = (dirs != null && dirs.Count() > 0 ? string.Join("/", dirs) + "/" : ""); + if (Utilities.GetYesNo(reader.GetAttribute("isdevice")) == true) // Listxml-specific, used by older DATs + machineType |= MachineType.Device; + + if (Utilities.GetYesNo(reader.GetAttribute("ismechanical")) == true) // Listxml-specific, used by older DATs + machineType |= MachineType.Mechanical; + + string dirsString = (dirs != null && dirs.Count() > 0 ? string.Join("/", dirs) + "/" : string.Empty); Machine machine = new Machine { Name = dirsString + reader.GetAttribute("name"), @@ -343,11 +343,11 @@ namespace SabreTools.Library.DatFiles RebuildTo = reader.GetAttribute("rebuildto"), Runnable = Utilities.GetYesNo(reader.GetAttribute("runnable")), // Listxml-specific, used by older DATs - Comment = "", + Comment = string.Empty, - CloneOf = reader.GetAttribute("cloneof") ?? "", - RomOf = reader.GetAttribute("romof") ?? "", - SampleOf = reader.GetAttribute("sampleof") ?? "", + CloneOf = reader.GetAttribute("cloneof") ?? string.Empty, + RomOf = reader.GetAttribute("romof") ?? string.Empty, + SampleOf = reader.GetAttribute("sampleof") ?? string.Empty, MachineType = (machineType == MachineType.NULL ? MachineType.None : machineType), }; @@ -355,10 +355,8 @@ namespace SabreTools.Library.DatFiles if (Type == "SuperDAT" && !keep) { string tempout = Regex.Match(machine.Name, @".*?\\(.*)").Groups[1].Value; - if (!String.IsNullOrWhiteSpace(tempout)) - { + if (!string.IsNullOrWhiteSpace(tempout)) machine.Name = tempout; - } } while (!reader.EOF) @@ -376,24 +374,30 @@ namespace SabreTools.Library.DatFiles case "comment": // There can be multiple comments by spec machine.Comment += reader.ReadElementContentAsString(); break; + case "description": machine.Description = reader.ReadElementContentAsString(); break; + case "year": machine.Year = reader.ReadElementContentAsString(); break; + case "manufacturer": machine.Manufacturer = reader.ReadElementContentAsString(); break; + case "publisher": // Not technically supported but used by some legacy DATs machine.Publisher = reader.ReadElementContentAsString(); break; + case "trurip": // This is special metadata unique to TruRip ReadTruRip(reader.ReadSubtree(), machine); // Skip the trurip node now that we've processed it reader.Skip(); break; + case "release": containsItems = true; @@ -413,6 +417,7 @@ namespace SabreTools.Library.DatFiles reader.Read(); break; + case "biosset": containsItems = true; @@ -434,6 +439,7 @@ namespace SabreTools.Library.DatFiles reader.Read(); break; + case "rom": containsItems = true; @@ -464,6 +470,7 @@ namespace SabreTools.Library.DatFiles reader.Read(); break; + case "disk": containsItems = true; @@ -491,6 +498,7 @@ namespace SabreTools.Library.DatFiles reader.Read(); break; + case "sample": containsItems = true; @@ -510,6 +518,7 @@ namespace SabreTools.Library.DatFiles reader.Read(); break; + case "archive": containsItems = true; @@ -529,6 +538,7 @@ namespace SabreTools.Library.DatFiles reader.Read(); break; + default: reader.Read(); break; @@ -560,9 +570,7 @@ namespace SabreTools.Library.DatFiles { // If we have an empty trurip, skip it if (reader == null) - { return; - } // Otherwise, add what is possible reader.MoveToContent(); @@ -577,60 +585,74 @@ namespace SabreTools.Library.DatFiles } // Get the information from the trurip - string content = ""; + string content = string.Empty; switch (reader.Name) { case "titleid": content = reader.ReadElementContentAsString(); // string titleid = content; break; + case "publisher": machine.Publisher = reader.ReadElementContentAsString(); break; + case "developer": // Manufacturer is as close as this gets machine.Manufacturer = reader.ReadElementContentAsString(); break; + case "year": machine.Year = reader.ReadElementContentAsString(); break; + case "genre": content = reader.ReadElementContentAsString(); // string genre = content; break; + case "subgenre": content = reader.ReadElementContentAsString(); // string subgenre = content; break; + case "ratings": content = reader.ReadElementContentAsString(); // string ratings = content; break; + case "score": content = reader.ReadElementContentAsString(); // string score = content; break; + case "players": content = reader.ReadElementContentAsString(); // string players = content; break; + case "enabled": content = reader.ReadElementContentAsString(); // string enabled = content; break; + case "crc": content = reader.ReadElementContentAsString(); // string crc = Utilities.GetYesNo(content); break; + case "source": machine.SourceFile = reader.ReadElementContentAsString(); break; + case "cloneof": machine.CloneOf = reader.ReadElementContentAsString(); break; + case "relatedto": content = reader.ReadElementContentAsString(); // string relatedto = content; break; + default: reader.Read(); break; @@ -648,20 +670,21 @@ namespace SabreTools.Library.DatFiles { try { - Globals.Logger.User("Opening file for writing: {0}", outfile); + Globals.Logger.User($"Opening file for writing: {outfile}"); FileStream fs = Utilities.TryCreate(outfile); // If we get back null for some reason, just log and return if (fs == null) { - Globals.Logger.Warning("File '{0}' could not be created for writing! Please check to see if the file is writable", outfile); + Globals.Logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable"); return false; } - StreamWriter sw = new StreamWriter(fs, new UTF8Encoding(false)); + XmlTextWriter xtw = new XmlTextWriter(fs, new UTF8Encoding(false)); + xtw.Formatting = Formatting.Indented; // Write out the header - WriteHeader(sw); + WriteHeader(xtw); // Write out each of the machines and roms string lastgame = null; @@ -690,22 +713,18 @@ namespace SabreTools.Library.DatFiles // If we have a different game and we're not at the start of the list, output the end of last item if (lastgame != null && lastgame.ToLowerInvariant() != rom.MachineName.ToLowerInvariant()) - { - WriteEndGame(sw); - } + WriteEndGame(xtw); // If we have a new game, output the beginning of the new item if (lastgame == null || lastgame.ToLowerInvariant() != rom.MachineName.ToLowerInvariant()) - { - WriteStartGame(sw, rom); - } + WriteStartGame(xtw, rom); // If we have a "null" game (created by DATFromDir or something similar), log it to file if (rom.ItemType == ItemType.Rom && ((Rom)rom).Size == -1 && ((Rom)rom).CRC == "null") { - Globals.Logger.Verbose("Empty folder found: {0}", rom.MachineName); + Globals.Logger.Verbose($"Empty folder found: {rom.MachineName}"); rom.Name = (rom.Name == "null" ? "-" : rom.Name); ((Rom)rom).Size = Constants.SizeZero; @@ -719,7 +738,7 @@ namespace SabreTools.Library.DatFiles } // Now, output the rom data - WriteDatItem(sw, rom, ignoreblanks); + WriteDatItem(xtw, rom, ignoreblanks); // Set the new data to compare against lastgame = rom.MachineName; @@ -727,10 +746,10 @@ namespace SabreTools.Library.DatFiles } // Write the file footer out - WriteFooter(sw); + WriteFooter(xtw); Globals.Logger.Verbose("File written!" + Environment.NewLine); - sw.Dispose(); + xtw.Dispose(); fs.Dispose(); } catch (Exception ex) @@ -745,47 +764,91 @@ namespace SabreTools.Library.DatFiles /// /// Write out DAT header using the supplied StreamWriter /// - /// StreamWriter to output to + /// XmlTextWriter to output to /// True if the data was written, false on error - private bool WriteHeader(StreamWriter sw) + private bool WriteHeader(XmlTextWriter xtw) { try { - string header = "\n" + - "\n\n" + - "\n" + - "\t
\n" + - "\t\t" + WebUtility.HtmlEncode(Name) + "\n" + - "\t\t" + WebUtility.HtmlEncode(Description) + "\n" + - (!String.IsNullOrWhiteSpace(RootDir) ? "\t\t" + WebUtility.HtmlEncode(RootDir) + "\n" : "") + - (!String.IsNullOrWhiteSpace(Category) ? "\t\t" + WebUtility.HtmlEncode(Category) + "\n" : "") + - "\t\t" + WebUtility.HtmlEncode(Version) + "\n" + - (!String.IsNullOrWhiteSpace(Date) ? "\t\t" + WebUtility.HtmlEncode(Date) + "\n" : "") + - "\t\t" + WebUtility.HtmlEncode(Author) + "\n" + - (!String.IsNullOrWhiteSpace(Email) ? "\t\t" + WebUtility.HtmlEncode(Email) + "\n" : "") + - (!String.IsNullOrWhiteSpace(Homepage) ? "\t\t" + WebUtility.HtmlEncode(Homepage) + "\n" : "") + - (!String.IsNullOrWhiteSpace(Url) ? "\t\t" + WebUtility.HtmlEncode(Url) + "\n" : "") + - (!String.IsNullOrWhiteSpace(Comment) ? "\t\t" + WebUtility.HtmlEncode(Comment) + "\n" : "") + - (!String.IsNullOrWhiteSpace(Type) ? "\t\t" + WebUtility.HtmlEncode(Type) + "\n" : "") + - (ForcePacking != ForcePacking.None || ForceMerging != ForceMerging.None || ForceNodump != ForceNodump.None || !String.IsNullOrWhiteSpace(Header) ? - "\t\t\n" - : "") + - "\t
\n"; + xtw.WriteStartDocument(); + xtw.WriteDocType("datafile", "-//Logiqx//DTD ROM Management Datafile//EN", "http://www.logiqx.com/Dats/datafile.dtd", null); + + xtw.WriteStartElement("datafile"); + + xtw.WriteStartElement("header"); + xtw.WriteElementString("name", Name); + xtw.WriteElementString("description", Description); + if (!string.IsNullOrWhiteSpace(RootDir)) + xtw.WriteElementString("rootdir", RootDir); + if (!string.IsNullOrWhiteSpace(Category)) + xtw.WriteElementString("category", Category); + xtw.WriteElementString("version", Version); + if (!string.IsNullOrWhiteSpace(Date)) + xtw.WriteElementString("date", Date); + xtw.WriteElementString("author", Author); + if (!string.IsNullOrWhiteSpace(Email)) + xtw.WriteElementString("email", Email); + if (!string.IsNullOrWhiteSpace(Homepage)) + xtw.WriteElementString("homepage", Homepage); + if (!string.IsNullOrWhiteSpace(Url)) + xtw.WriteElementString("url", Url); + if (!string.IsNullOrWhiteSpace(Comment)) + xtw.WriteElementString("comment", Comment); + if (!string.IsNullOrWhiteSpace(Type)) + xtw.WriteElementString("type", Type); - // Write the header out - sw.Write(header); - sw.Flush(); + if (ForcePacking != ForcePacking.None + || ForceMerging != ForceMerging.None + || ForceNodump != ForceNodump.None + || !string.IsNullOrWhiteSpace(Header)) + { + xtw.WriteStartElement("clrmamepro"); + switch (ForcePacking) + { + case ForcePacking.Unzip: + xtw.WriteAttributeString("forcepacking", "unzip"); + break; + case ForcePacking.Zip: + xtw.WriteAttributeString("forcepacking", "zip"); + break; + } + + switch (ForceMerging) + { + case ForceMerging.Full: + xtw.WriteAttributeString("forcemerging", "full"); + break; + case ForceMerging.Split: + xtw.WriteAttributeString("forcemerging", "split"); + break; + case ForceMerging.Merged: + xtw.WriteAttributeString("forcemerging", "merged"); + break; + case ForceMerging.NonMerged: + xtw.WriteAttributeString("forcemerging", "nonmerged"); + break; + } + + switch (ForceNodump) + { + case ForceNodump.Ignore: + xtw.WriteAttributeString("forcenodump", "ignore"); + break; + case ForceNodump.Obsolete: + xtw.WriteAttributeString("forcenodump", "obsolete"); + break; + case ForceNodump.Required: + xtw.WriteAttributeString("forcenodump", "required"); + break; + } + + if (!string.IsNullOrWhiteSpace(Header)) + xtw.WriteAttributeString("header", Header); + + xtw.WriteEndElement(); + } + + xtw.Flush(); } catch (Exception ex) { @@ -799,44 +862,56 @@ namespace SabreTools.Library.DatFiles /// /// Write out Game start using the supplied StreamWriter /// - /// StreamWriter to output to - /// DatItem object to be output + /// XmlTextWriter to output to + /// DatItem object to be output /// True if the data was written, false on error - private bool WriteStartGame(StreamWriter sw, DatItem rom) + private bool WriteStartGame(XmlTextWriter xtw, DatItem datItem) { try { // No game should start with a path separator - if (rom.MachineName.StartsWith(Path.DirectorySeparatorChar.ToString())) + datItem.MachineName = datItem.MachineName.TrimStart(Path.DirectorySeparatorChar); + + // Build the state based on excluded fields + xtw.WriteStartElement(_deprecated ? "game" : "machine"); + xtw.WriteAttributeString("name", datItem.GetField(Field.MachineName, ExcludeFields)); + if (!ExcludeFields[(int)Field.MachineType]) { - rom.MachineName = rom.MachineName.Substring(1); + if ((datItem.MachineType & MachineType.Bios) != 0) + xtw.WriteAttributeString("isbios", "yes"); + if ((datItem.MachineType & MachineType.Device) != 0) + xtw.WriteAttributeString("isdevice", "yes"); + if ((datItem.MachineType & MachineType.Mechanical) != 0) + xtw.WriteAttributeString("ismechanical", "yes"); } - string state = "\t<" + (_depreciated ? "game" : "machine") + " name=\"" + (!ExcludeFields[(int)Field.MachineName] ? WebUtility.HtmlEncode(rom.MachineName) : "") + "\"" - + (!ExcludeFields[(int)Field.MachineType] && (rom.MachineType & MachineType.Bios) != 0 ? " isbios=\"yes\"" : "") - + (!ExcludeFields[(int)Field.MachineType] && (rom.MachineType & MachineType.Device) != 0 ? " isdevice=\"yes\"" : "") - + (!ExcludeFields[(int)Field.MachineType] && (rom.MachineType & MachineType.Mechanical) != 0 ? " ismechanical=\"yes\"" : "") - + (!ExcludeFields[(int)Field.Runnable] && rom.Runnable == true - ? " runnable=\"yes\"" - : (!ExcludeFields[(int)Field.Runnable] && rom.Runnable == false ? " runnable=\"no\"" : "")) - + (!ExcludeFields[(int)Field.CloneOf] && !String.IsNullOrWhiteSpace(rom.CloneOf) && (rom.MachineName.ToLowerInvariant() != rom.CloneOf.ToLowerInvariant()) - ? " cloneof=\"" + WebUtility.HtmlEncode(rom.CloneOf) + "\"" - : "") - + (!ExcludeFields[(int)Field.RomOf] && !String.IsNullOrWhiteSpace(rom.RomOf) && (rom.MachineName.ToLowerInvariant() != rom.RomOf.ToLowerInvariant()) - ? " romof=\"" + WebUtility.HtmlEncode(rom.RomOf) + "\"" - : "") - + (!ExcludeFields[(int)Field.SampleOf] && !String.IsNullOrWhiteSpace(rom.SampleOf) && (rom.MachineName.ToLowerInvariant() != rom.SampleOf.ToLowerInvariant()) - ? " sampleof=\"" + WebUtility.HtmlEncode(rom.SampleOf) + "\"" - : "") - + ">\n" - + (!ExcludeFields[(int)Field.Comment] && !String.IsNullOrWhiteSpace(rom.Comment) ? "\t\t" + WebUtility.HtmlEncode(rom.Comment) + "\n" : "") - + (!ExcludeFields[(int)Field.Description] ? "\t\t" + WebUtility.HtmlEncode((String.IsNullOrWhiteSpace(rom.MachineDescription) ? rom.MachineName : rom.MachineDescription)) + "\n" : "") - + (!ExcludeFields[(int)Field.Year] && !String.IsNullOrWhiteSpace(rom.Year) ? "\t\t" + WebUtility.HtmlEncode(rom.Year) + "\n" : "") - + (!ExcludeFields[(int)Field.Publisher] && !String.IsNullOrWhiteSpace(rom.Publisher) ? "\t\t" + WebUtility.HtmlEncode(rom.Publisher) + "\n" : "") - + (!ExcludeFields[(int)Field.Manufacturer] && !String.IsNullOrWhiteSpace(rom.Manufacturer) ? "\t\t" + WebUtility.HtmlEncode(rom.Manufacturer) + "\n" : ""); + if (!ExcludeFields[(int)Field.Runnable] && datItem.Runnable != null) + { + if (datItem.Runnable == true) + xtw.WriteAttributeString("runnable", "yes"); + else if (datItem.Runnable == false) + xtw.WriteAttributeString("runnable", "no"); + } - sw.Write(state); - sw.Flush(); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CloneOf, ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.CloneOf, StringComparison.OrdinalIgnoreCase)) + xtw.WriteAttributeString("cloneof", datItem.CloneOf); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RomOf, ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.RomOf, StringComparison.OrdinalIgnoreCase)) + xtw.WriteAttributeString("romof", datItem.RomOf); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SampleOf, ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.SampleOf, StringComparison.OrdinalIgnoreCase)) + xtw.WriteAttributeString("sampleof", datItem.SampleOf); + + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Comment, ExcludeFields))) + xtw.WriteElementString("comment", datItem.Comment); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Description, ExcludeFields))) + xtw.WriteElementString("description", datItem.MachineDescription); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Year, ExcludeFields))) + xtw.WriteElementString("year", datItem.Year); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Publisher, ExcludeFields))) + xtw.WriteElementString("publisher", datItem.Publisher); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Manufacturer, ExcludeFields))) + xtw.WriteElementString("manufacturer", datItem.Manufacturer); + + xtw.Flush(); } catch (Exception ex) { @@ -850,16 +925,16 @@ namespace SabreTools.Library.DatFiles /// /// Write out Game end using the supplied StreamWriter /// - /// StreamWriter to output to + /// XmlTextWriter to output to /// True if the data was written, false on error - private bool WriteEndGame(StreamWriter sw) + private bool WriteEndGame(XmlTextWriter xtw) { try { - string state = "\t\n"; + // End machine + xtw.WriteEndElement(); - sw.Write(state); - sw.Flush(); + xtw.Flush(); } catch (Exception ex) { @@ -873,84 +948,112 @@ namespace SabreTools.Library.DatFiles /// /// Write out DatItem using the supplied StreamWriter /// - /// StreamWriter to output to - /// DatItem object to be output + /// XmlTextWriter to output to + /// DatItem object to be output /// True if blank roms should be skipped on output, false otherwise (default) /// True if the data was written, false on error - private bool WriteDatItem(StreamWriter sw, DatItem rom, bool ignoreblanks = false) + private bool WriteDatItem(XmlTextWriter xtw, DatItem datItem, bool ignoreblanks = false) { // If we are in ignore blanks mode AND we have a blank (0-size) rom, skip - if (ignoreblanks - && (rom.ItemType == ItemType.Rom - && (((Rom)rom).Size == 0 || ((Rom)rom).Size == -1))) - { + if (ignoreblanks && (datItem.ItemType == ItemType.Rom && ((datItem as Rom).Size == 0 || (datItem as Rom).Size == -1))) return true; - } try { - string state = ""; - // Pre-process the item name - ProcessItemName(rom, true); + ProcessItemName(datItem, true); - switch (rom.ItemType) + // Build the state based on excluded fields + switch (datItem.ItemType) { case ItemType.Archive: - state += "\t\t\n"; + xtw.WriteStartElement("archive"); + xtw.WriteAttributeString("name", datItem.GetField(Field.Name, ExcludeFields)); + xtw.WriteEndElement(); break; + case ItemType.BiosSet: - state += "\t\t\n"; + var biosSet = datItem as BiosSet; + xtw.WriteStartElement("biosset"); + xtw.WriteAttributeString("name", biosSet.GetField(Field.Name, ExcludeFields)); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.BiosDescription, ExcludeFields))) + xtw.WriteAttributeString("description", biosSet.Description); + if (!ExcludeFields[(int)Field.Default] && biosSet.Default != null) + xtw.WriteAttributeString("default", biosSet.Default.ToString().ToLowerInvariant()); + xtw.WriteEndElement(); break; + case ItemType.Disk: - state += "\t\t\n"; + var disk = datItem as Disk; + xtw.WriteStartElement("disk"); + xtw.WriteAttributeString("name", disk.GetField(Field.Name, ExcludeFields)); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields))) + xtw.WriteAttributeString("md5", disk.MD5.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, ExcludeFields))) + xtw.WriteAttributeString("ripemd160", disk.RIPEMD160.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields))) + xtw.WriteAttributeString("sha1", disk.SHA1.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, ExcludeFields))) + xtw.WriteAttributeString("sha256", disk.SHA256.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, ExcludeFields))) + xtw.WriteAttributeString("sha384", disk.SHA384.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, ExcludeFields))) + xtw.WriteAttributeString("sha512", disk.SHA512.ToLowerInvariant()); + if (!ExcludeFields[(int)Field.Status] && disk.ItemStatus != ItemStatus.None) + xtw.WriteAttributeString("status", disk.ItemStatus.ToString().ToLowerInvariant()); + xtw.WriteEndElement(); break; + case ItemType.Release: - state += "\t\t\n"; + var release = datItem as Release; + xtw.WriteStartElement("release"); + xtw.WriteAttributeString("name", release.GetField(Field.Name, ExcludeFields)); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Region, ExcludeFields))) + xtw.WriteAttributeString("region", release.Region); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Language, ExcludeFields))) + xtw.WriteAttributeString("language", release.Language); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Date, ExcludeFields))) + xtw.WriteAttributeString("date", release.Date); + if (!ExcludeFields[(int)Field.Default] && release.Default != null) + xtw.WriteAttributeString("default", release.Default.ToString().ToLowerInvariant()); + xtw.WriteEndElement(); break; + case ItemType.Rom: - state += "\t\t\n"; + var rom = datItem as Rom; + xtw.WriteStartElement("rom"); + xtw.WriteAttributeString("name", rom.GetField(Field.Name, ExcludeFields)); + if (!ExcludeFields[(int)Field.Size] && rom.Size != -1) + xtw.WriteAttributeString("size", rom.Size.ToString()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields))) + xtw.WriteAttributeString("crc", rom.CRC.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields))) + xtw.WriteAttributeString("md5", rom.MD5.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, ExcludeFields))) + xtw.WriteAttributeString("ripemd160", rom.RIPEMD160.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields))) + xtw.WriteAttributeString("sha1", rom.SHA1.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, ExcludeFields))) + xtw.WriteAttributeString("sha256", rom.SHA256.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, ExcludeFields))) + xtw.WriteAttributeString("sha384", rom.SHA384.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, ExcludeFields))) + xtw.WriteAttributeString("sha512", rom.SHA512.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Date, ExcludeFields))) + xtw.WriteAttributeString("date", rom.Date); + if (!ExcludeFields[(int)Field.Status] && rom.ItemStatus != ItemStatus.None) + xtw.WriteAttributeString("status", rom.ItemStatus.ToString().ToLowerInvariant()); + xtw.WriteEndElement(); break; + case ItemType.Sample: - state += "\t\t\n"; + xtw.WriteStartElement("sample"); + xtw.WriteAttributeString("name", datItem.GetField(Field.Name, ExcludeFields)); + xtw.WriteEndElement(); break; } - sw.Write(state); - sw.Flush(); + xtw.Flush(); } catch (Exception ex) { @@ -964,17 +1067,19 @@ namespace SabreTools.Library.DatFiles /// /// Write out DAT footer using the supplied StreamWriter /// - /// StreamWriter to output to + /// XmlTextWriter to output to /// True if the data was written, false on error - private bool WriteFooter(StreamWriter sw) + private bool WriteFooter(XmlTextWriter xtw) { try { - string footer = "\t\n
\n"; + // End machine + xtw.WriteEndElement(); - // Write the footer out - sw.Write(footer); - sw.Flush(); + // End datafile + xtw.WriteEndElement(); + + xtw.Flush(); } catch (Exception ex) { diff --git a/SabreTools.Library/DatFiles/Missfile.cs b/SabreTools.Library/DatFiles/Missfile.cs index 778ddc46..01d28d92 100644 --- a/SabreTools.Library/DatFiles/Missfile.cs +++ b/SabreTools.Library/DatFiles/Missfile.cs @@ -1,18 +1,11 @@ using System; using System.Collections.Generic; +using System.IO; using System.Text; + using SabreTools.Library.Data; using SabreTools.Library.DatItems; using SabreTools.Library.Tools; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using FileStream = System.IO.FileStream; -using StreamWriter = System.IO.StreamWriter; -#endif using NaturalSort; namespace SabreTools.Library.DatFiles @@ -65,13 +58,13 @@ namespace SabreTools.Library.DatFiles { try { - Globals.Logger.User("Opening file for writing: {0}", outfile); + Globals.Logger.User($"Opening file for writing: {outfile}"); FileStream fs = Utilities.TryCreate(outfile); // If we get back null for some reason, just log and return if (fs == null) { - Globals.Logger.Warning("File '{0}' could not be created for writing! Please check to see if the file is writable", outfile); + Globals.Logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable"); return false; } @@ -107,7 +100,7 @@ namespace SabreTools.Library.DatFiles && ((Rom)rom).Size == -1 && ((Rom)rom).CRC == "null") { - Globals.Logger.Verbose("Empty folder found: {0}", rom.MachineName); + Globals.Logger.Verbose($"Empty folder found: {rom.MachineName}"); lastgame = rom.MachineName; continue; } @@ -137,43 +130,39 @@ namespace SabreTools.Library.DatFiles /// Write out DatItem using the supplied StreamWriter ///
/// StreamWriter to output to - /// DatItem object to be output + /// DatItem object to be output /// The name of the last game to be output /// True if blank roms should be skipped on output, false otherwise (default) /// True if the data was written, false on error - private bool WriteDatItem(StreamWriter sw, DatItem rom, string lastgame, bool ignoreblanks = false) + private bool WriteDatItem(StreamWriter sw, DatItem datItem, string lastgame, bool ignoreblanks = false) { // If we are in ignore blanks mode AND we have a blank (0-size) rom, skip - if (ignoreblanks - && (rom.ItemType == ItemType.Rom - && (((Rom)rom).Size == 0 || ((Rom)rom).Size == -1))) - { + if (ignoreblanks && (datItem.ItemType == ItemType.Rom && ((datItem as Rom).Size == 0 || (datItem as Rom).Size == -1))) return true; - } try { - string state = ""; + string state = string.Empty; // Process the item name - ProcessItemName(rom, false, forceRomName: false); + ProcessItemName(datItem, false, forceRomName: false); // If we're in Romba mode, the state is consistent if (Romba) { - state += (!ExcludeFields[(int)Field.Name] ? rom.Name : "") + "\n"; + state += $"{datItem.GetField(Field.SHA1, ExcludeFields)}\n"; } // Otherwise, use any flags else { - if (!UseRomName && rom.MachineName != lastgame) + if (!UseRomName && datItem.MachineName != lastgame) { - state += (!ExcludeFields[(int)Field.Name] ? rom.Name : "") + "\n"; - lastgame = rom.MachineName; + state += $"{datItem.GetField(Field.MachineName, ExcludeFields)}\n"; + lastgame = datItem.MachineName; } else if (UseRomName) { - state += (!ExcludeFields[(int)Field.Name] ? rom.Name : "") + "\n"; + state += $"{datItem.GetField(Field.Name, ExcludeFields)}\n"; } } diff --git a/SabreTools.Library/DatFiles/OfflineList.cs b/SabreTools.Library/DatFiles/OfflineList.cs index c825b7da..ebd04d07 100644 --- a/SabreTools.Library/DatFiles/OfflineList.cs +++ b/SabreTools.Library/DatFiles/OfflineList.cs @@ -1,20 +1,12 @@ using System; using System.Collections.Generic; -using System.Net; +using System.IO; using System.Text; using System.Xml; + using SabreTools.Library.Data; using SabreTools.Library.DatItems; using SabreTools.Library.Tools; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using FileStream = System.IO.FileStream; -using StreamWriter = System.IO.StreamWriter; -#endif using NaturalSort; namespace SabreTools.Library.DatFiles @@ -61,9 +53,7 @@ namespace SabreTools.Library.DatFiles // If we got a null reader, just return if (xtr == null) - { return; - } // Otherwise, read the file to the end try @@ -86,12 +76,14 @@ namespace SabreTools.Library.DatFiles // Skip the configuration node now that we've processed it xtr.Skip(); break; + case "games": ReadGames(xtr.ReadSubtree(), keep, clean, remUnicode); // Skip the games node now that we've processed it xtr.Skip(); break; + default: xtr.Read(); break; @@ -100,7 +92,7 @@ namespace SabreTools.Library.DatFiles } catch (Exception ex) { - Globals.Logger.Warning("Exception found while parsing '{0}': {1}", filename, ex); + Globals.Logger.Warning($"Exception found while parsing '{filename}': {ex}"); // For XML errors, just skip the affected node xtr?.Read(); @@ -120,9 +112,7 @@ namespace SabreTools.Library.DatFiles // If there's no subtree to the configuration, skip it if (reader == null) - { return; - } // Otherwise, add what is possible reader.MoveToContent(); @@ -138,63 +128,73 @@ namespace SabreTools.Library.DatFiles } // Get all configuration items (ONLY OVERWRITE IF THERE'S NO DATA) - string content = ""; + string content = string.Empty; switch (reader.Name.ToLowerInvariant()) { case "datname": content = reader.ReadElementContentAsString(); - Name = (String.IsNullOrWhiteSpace(Name) ? content : Name); + Name = (string.IsNullOrWhiteSpace(Name) ? content : Name); superdat = superdat || content.Contains(" - SuperDAT"); if (keep && superdat) { - Type = (String.IsNullOrWhiteSpace(Type) ? "SuperDAT" : Type); + Type = (string.IsNullOrWhiteSpace(Type) ? "SuperDAT" : Type); } break; + case "datversion": content = reader.ReadElementContentAsString(); - Version = (String.IsNullOrWhiteSpace(Version) ? content : Version); + Version = (string.IsNullOrWhiteSpace(Version) ? content : Version); break; + case "system": content = reader.ReadElementContentAsString(); // string system = content; break; + case "screenshotswidth": content = reader.ReadElementContentAsString(); // string screenshotsWidth = content; // Int32? break; + case "screenshotsheight": content = reader.ReadElementContentAsString(); // string screenshotsHeight = content; // Int32? break; + case "infos": ReadInfos(reader.ReadSubtree()); // Skip the infos node now that we've processed it reader.Skip(); break; + case "canopen": ReadCanOpen(reader.ReadSubtree()); // Skip the canopen node now that we've processed it reader.Skip(); break; + case "newdat": ReadNewDat(reader.ReadSubtree()); // Skip the newdat node now that we've processed it reader.Skip(); break; + case "search": ReadSearch(reader.ReadSubtree()); // Skip the search node now that we've processed it reader.Skip(); break; + case "romtitle": content = reader.ReadElementContentAsString(); // string romtitle = content; break; + default: reader.Read(); break; @@ -210,9 +210,7 @@ namespace SabreTools.Library.DatFiles { // If there's no subtree to the configuration, skip it if (reader == null) - { return; - } // Otherwise, add what is possible reader.MoveToContent(); @@ -236,78 +234,91 @@ namespace SabreTools.Library.DatFiles // string title_default = reader.GetAttribute("default"); // (true|false) reader.Read(); break; + case "location": // string location_visible = reader.GetAttribute("visible"); // (true|false) // string location_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false) // string location_default = reader.GetAttribute("default"); // (true|false) reader.Read(); break; + case "publisher": // string publisher_visible = reader.GetAttribute("visible"); // (true|false) // string publisher_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false) // string publisher_default = reader.GetAttribute("default"); // (true|false) reader.Read(); break; + case "sourcerom": // string sourceRom_visible = reader.GetAttribute("visible"); // (true|false) // string sourceRom_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false) // string sourceRom_default = reader.GetAttribute("default"); // (true|false) reader.Read(); break; + case "savetype": // string saveType_visible = reader.GetAttribute("visible"); // (true|false) // string saveType_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false) // string saveType_default = reader.GetAttribute("default"); // (true|false) reader.Read(); break; + case "romsize": // string romSize_visible = reader.GetAttribute("visible"); // (true|false) // string romSize_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false) // string romSize_default = reader.GetAttribute("default"); // (true|false) reader.Read(); break; + case "releasenumber": // string releaseNumber_visible = reader.GetAttribute("visible"); // (true|false) // string releaseNumber_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false) // string releaseNumber_default = reader.GetAttribute("default"); // (true|false) reader.Read(); break; + case "languagenumber": // string languageNumber_visible = reader.GetAttribute("visible"); // (true|false) // string languageNumber_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false) // string languageNumber_default = reader.GetAttribute("default"); // (true|false) reader.Read(); break; + case "comment": // string comment_visible = reader.GetAttribute("visible"); // (true|false) // string comment_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false) // string comment_default = reader.GetAttribute("default"); // (true|false) reader.Read(); break; + case "romcrc": // string romCRC_visible = reader.GetAttribute("visible"); // (true|false) // string romCRC_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false) // string romCRC_default = reader.GetAttribute("default"); // (true|false) reader.Read(); break; + case "im1crc": // string im1CRC_visible = reader.GetAttribute("visible"); // (true|false) // string im1CRC_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false) // string im1CRC_default = reader.GetAttribute("default"); // (true|false) reader.Read(); break; + case "im2crc": // string im2CRC_visible = reader.GetAttribute("visible"); // (true|false) // string im2CRC_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false) // string im2CRC_default = reader.GetAttribute("default"); // (true|false) reader.Read(); break; + case "languages": // string languages_visible = reader.GetAttribute("visible"); // (true|false) // string languages_inNamingOption = reader.GetAttribute("inNamingOption"); // (true|false) // string languages_default = reader.GetAttribute("default"); // (true|false) reader.Read(); break; + default: reader.Read(); break; @@ -326,9 +337,7 @@ namespace SabreTools.Library.DatFiles // If there's no subtree to the configuration, skip it if (reader == null) - { return; - } // Otherwise, add what is possible reader.MoveToContent(); @@ -349,6 +358,7 @@ namespace SabreTools.Library.DatFiles case "extension": extensions.Add(reader.ReadElementContentAsString()); break; + default: reader.Read(); break; @@ -364,9 +374,7 @@ namespace SabreTools.Library.DatFiles { // If there's no subtree to the configuration, skip it if (reader == null) - { return; - } // Otherwise, add what is possible reader.MoveToContent(); @@ -382,22 +390,25 @@ namespace SabreTools.Library.DatFiles } // Get all newdat items - string content = ""; + string content = string.Empty; switch (reader.Name.ToLowerInvariant()) { case "datversionurl": content = reader.ReadElementContentAsString(); - Url = (String.IsNullOrWhiteSpace(Name) ? content : Url); + Url = (string.IsNullOrWhiteSpace(Name) ? content : Url); break; + case "daturl": // string fileName = reader.GetAttribute("fileName"); content = reader.ReadElementContentAsString(); // string url = content; break; + case "imurl": content = reader.ReadElementContentAsString(); // string url = content; break; + default: reader.Read(); break; @@ -413,9 +424,7 @@ namespace SabreTools.Library.DatFiles { // If there's no subtree to the configuration, skip it if (reader == null) - { return; - } // Otherwise, add what is possible reader.MoveToContent(); @@ -431,7 +440,7 @@ namespace SabreTools.Library.DatFiles } // Get all search items - string content = ""; + string content = string.Empty; switch (reader.Name.ToLowerInvariant()) { case "to": @@ -444,6 +453,7 @@ namespace SabreTools.Library.DatFiles // Skip the to node now that we've processed it reader.Skip(); break; + default: reader.Read(); break; @@ -459,9 +469,7 @@ namespace SabreTools.Library.DatFiles { // If there's no subtree to the configuration, skip it if (reader == null) - { return; - } // Otherwise, add what is possible reader.MoveToContent(); @@ -477,7 +485,7 @@ namespace SabreTools.Library.DatFiles } // Get all search items - string content = ""; + string content = string.Empty; switch (reader.Name.ToLowerInvariant()) { case "find": @@ -486,6 +494,7 @@ namespace SabreTools.Library.DatFiles content = reader.ReadElementContentAsString(); // string findValue = content; break; + default: reader.Read(); break; @@ -509,9 +518,7 @@ namespace SabreTools.Library.DatFiles { // If there's no subtree to the configuration, skip it if (reader == null) - { return; - } // Otherwise, add what is possible reader.MoveToContent(); @@ -535,6 +542,7 @@ namespace SabreTools.Library.DatFiles // Skip the game node now that we've processed it reader.Skip(); break; + default: reader.Read(); break; @@ -557,16 +565,14 @@ namespace SabreTools.Library.DatFiles bool remUnicode) { // Prepare all internal variables - string releaseNumber = "", key = "", publisher = "", duplicateid = ""; + string releaseNumber = string.Empty, key = string.Empty, publisher = string.Empty, duplicateid = string.Empty; long size = -1; List roms = new List(); Machine machine = new Machine(); // If there's no subtree to the configuration, skip it if (reader == null) - { return; - } // Otherwise, add what is possible reader.MoveToContent(); @@ -582,82 +588,80 @@ namespace SabreTools.Library.DatFiles } // Get all games items - string content = ""; + string content = string.Empty; switch (reader.Name.ToLowerInvariant()) { case "imagenumber": content = reader.ReadElementContentAsString(); // string imageNumber = content; - break; + case "releasenumber": releaseNumber = reader.ReadElementContentAsString(); - break; + case "title": content = reader.ReadElementContentAsString(); machine.Name = content; - break; + case "savetype": content = reader.ReadElementContentAsString(); // string saveType = content; - break; + case "romsize": if (!Int64.TryParse(reader.ReadElementContentAsString(), out size)) - { size = -1; - } break; + case "publisher": publisher = reader.ReadElementContentAsString(); - break; + case "location": content = reader.ReadElementContentAsString(); // string location = content; - break; + case "sourcerom": content = reader.ReadElementContentAsString(); // string sourceRom = content; - break; + case "language": content = reader.ReadElementContentAsString(); // string language = content; - break; + case "files": roms = ReadFiles(reader.ReadSubtree(), releaseNumber, machine.Name, keep, clean, remUnicode); - // Skip the files node now that we've processed it reader.Skip(); break; + case "im1crc": content = reader.ReadElementContentAsString(); // string im1crc = content; - break; + case "im2crc": content = reader.ReadElementContentAsString(); // string im2crc = content; - break; + case "comment": machine.Comment = reader.ReadElementContentAsString(); - break; + case "duplicateid": duplicateid = reader.ReadElementContentAsString(); if (duplicateid != "0") - { machine.CloneOf = duplicateid; - } break; + default: reader.Read(); break; @@ -700,9 +704,7 @@ namespace SabreTools.Library.DatFiles // If there's no subtree to the configuration, skip it if (reader == null) - { return roms; - } // Otherwise, add what is possible reader.MoveToContent(); @@ -723,9 +725,10 @@ namespace SabreTools.Library.DatFiles case "romcrc": extensionToCrc.Add( new Tuple( - reader.GetAttribute("extension") ?? "", + reader.GetAttribute("extension") ?? string.Empty, reader.ReadElementContentAsString().ToLowerInvariant())); break; + default: reader.Read(); break; @@ -737,7 +740,7 @@ namespace SabreTools.Library.DatFiles { roms.Add(new Rom() { - Name = (releaseNumber != "0" ? releaseNumber + " - " : "") + machineName + pair.Item1, + Name = (releaseNumber != "0" ? releaseNumber + " - " : string.Empty) + machineName + pair.Item1, CRC = Utilities.CleanHashData(pair.Item2, Constants.CRCLength), ItemStatus = ItemStatus.None, @@ -757,20 +760,21 @@ namespace SabreTools.Library.DatFiles { try { - Globals.Logger.User("Opening file for writing: {0}", outfile); + Globals.Logger.User($"Opening file for writing: {outfile}"); FileStream fs = Utilities.TryCreate(outfile); // If we get back null for some reason, just log and return if (fs == null) { - Globals.Logger.Warning("File '{0}' could not be created for writing! Please check to see if the file is writable", outfile); + Globals.Logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable"); return false; } - StreamWriter sw = new StreamWriter(fs, new UTF8Encoding(false)); + XmlTextWriter xtw = new XmlTextWriter(fs, new UTF8Encoding(false)); + xtw.Formatting = Formatting.Indented; // Write out the header - WriteHeader(sw); + WriteHeader(xtw); // Write out each of the machines and roms string lastgame = null; @@ -797,18 +801,12 @@ namespace SabreTools.Library.DatFiles continue; } - // If we have a different game and we're not at the start of the list, output the end of last item - if (lastgame != null && lastgame.ToLowerInvariant() != rom.MachineName.ToLowerInvariant()) - { - WriteEndGame(sw); - } - // If we have a "null" game (created by DATFromDir or something similar), log it to file if (rom.ItemType == ItemType.Rom && ((Rom)rom).Size == -1 && ((Rom)rom).CRC == "null") { - Globals.Logger.Verbose("Empty folder found: {0}", rom.MachineName); + Globals.Logger.Verbose($"Empty folder found: {rom.MachineName}"); rom.Name = (rom.Name == "null" ? "-" : rom.Name); ((Rom)rom).Size = Constants.SizeZero; @@ -822,7 +820,7 @@ namespace SabreTools.Library.DatFiles } // Now, output the rom data - WriteDatItem(sw, rom, ignoreblanks); + WriteDatItem(xtw, rom, ignoreblanks); // Set the new data to compare against lastgame = rom.MachineName; @@ -830,10 +828,10 @@ namespace SabreTools.Library.DatFiles } // Write the file footer out - WriteFooter(sw); + WriteFooter(xtw); Globals.Logger.Verbose("File written!" + Environment.NewLine); - sw.Dispose(); + xtw.Dispose(); fs.Dispose(); } catch (Exception ex) @@ -848,81 +846,174 @@ namespace SabreTools.Library.DatFiles /// /// Write out DAT header using the supplied StreamWriter /// - /// StreamWriter to output to + /// XmlTextWriter to output to /// True if the data was written, false on error - private bool WriteHeader(StreamWriter sw) + private bool WriteHeader(XmlTextWriter xtw) { try { - string header = "\n" - + "\n" - + "\t\n" - + "\t\t" + WebUtility.HtmlEncode(Name) + "\n" - + "\t\t" + Count + "\n" - + "\t\tnone\n" - + "\t\t240\n" - + "\t\t160\n" - + "\t\t\n" - + "\t\t\t\n" - + "\t\t\t<location visible=\"true\" inNamingOption=\"true\" default=\"true\"/>\n" - + "\t\t\t<publisher visible=\"true\" inNamingOption=\"true\" default=\"true\"/>\n" - + "\t\t\t<sourceRom visible=\"true\" inNamingOption=\"true\" default=\"true\"/>\n" - + "\t\t\t<saveType visible=\"true\" inNamingOption=\"true\" default=\"true\"/>\n" - + "\t\t\t<romSize visible=\"true\" inNamingOption=\"true\" default=\"true\"/>\n" - + "\t\t\t<releaseNumber visible=\"true\" inNamingOption=\"true\" default=\"false\"/>\n" - + "\t\t\t<languageNumber visible=\"true\" inNamingOption=\"true\" default=\"false\"/>\n" - + "\t\t\t<comment visible=\"true\" inNamingOption=\"true\" default=\"false\"/>\n" - + "\t\t\t<romCRC visible=\"true\" inNamingOption=\"true\" default=\"false\"/>\n" - + "\t\t\t<im1CRC visible=\"false\" inNamingOption=\"false\" default=\"false\"/>\n" - + "\t\t\t<im2CRC visible=\"false\" inNamingOption=\"false\" default=\"false\"/>\n" - + "\t\t\t<languages visible=\"true\" inNamingOption=\"true\" default=\"true\"/>\n" - + "\t\t</infos>\n" - + "\t\t<canOpen>\n" - + "\t\t\t<extension>.bin</extension>\n" - + "\t\t</canOpen>\n" - + "\t\t<newDat>\n" - + "\t\t\t<datVersionURL>" + WebUtility.HtmlEncode(Url) + "</datVersionURL>\n" - + "\t\t\t<datURL fileName=\"" + WebUtility.HtmlEncode(FileName) + ".zip\">" + WebUtility.HtmlEncode(Url) + "</datURL>\n" - + "\t\t\t<imURL>" + WebUtility.HtmlEncode(Url) + "</imURL>\n" - + "\t\t</newDat>\n" - + "\t\t<search>\n" - + "\t\t\t<to value=\"location\" default=\"true\" auto=\"true\"/>\n" - + "\t\t\t<to value=\"romSize\" default=\"true\" auto=\"false\"/>\n" - + "\t\t\t<to value=\"languages\" default=\"true\" auto=\"true\"/>\n" - + "\t\t\t<to value=\"saveType\" default=\"false\" auto=\"false\"/>\n" - + "\t\t\t<to value=\"publisher\" default=\"false\" auto=\"true\"/>\n" - + "\t\t\t<to value=\"sourceRom\" default=\"false\" auto=\"true\"/>\n" - + "\t\t</search>\n" - + "\t\t<romTitle >%u - %n</romTitle>\n" - + "\t</configuration>\n" - + "\t<games>\n"; + xtw.WriteStartDocument(false); - // Write the header out - sw.Write(header); - sw.Flush(); - } - catch (Exception ex) - { - Globals.Logger.Error(ex.ToString()); - return false; - } + xtw.WriteStartElement("dat"); + xtw.WriteAttributeString("xsi", "xmlns", "http://www.w3.org/2001/XMLSchema-instance"); + xtw.WriteAttributeString("noNamespaceSchemaLocation", "xsi", "datas.xsd"); - return true; - } + xtw.WriteStartElement("configuration"); + xtw.WriteElementString("datName", Name); + xtw.WriteElementString("datVersion", Count.ToString()); + xtw.WriteElementString("system", "none"); + xtw.WriteElementString("screenshotsWidth", "240"); + xtw.WriteElementString("screenshotsHeight", "160"); - /// <summary> - /// Write out Game start using the supplied StreamWriter - /// </summary> - /// <param name="sw">StreamWriter to output to</param> - /// <returns>True if the data was written, false on error</returns> - private bool WriteEndGame(StreamWriter sw) - { - try - { - string state = "\t\t</game>\n"; + xtw.WriteStartElement("infos"); - sw.Write(state); - sw.Flush(); + xtw.WriteStartElement("title"); + xtw.WriteAttributeString("visible", "false"); + xtw.WriteAttributeString("inNamingOption", "true"); + xtw.WriteAttributeString("default", "false"); + xtw.WriteEndElement(); + + xtw.WriteStartElement("location"); + xtw.WriteAttributeString("visible", "true"); + xtw.WriteAttributeString("inNamingOption", "true"); + xtw.WriteAttributeString("default", "true"); + xtw.WriteEndElement(); + + xtw.WriteStartElement("publisher"); + xtw.WriteAttributeString("visible", "true"); + xtw.WriteAttributeString("inNamingOption", "true"); + xtw.WriteAttributeString("default", "true"); + xtw.WriteEndElement(); + + xtw.WriteStartElement("sourceRom"); + xtw.WriteAttributeString("visible", "true"); + xtw.WriteAttributeString("inNamingOption", "true"); + xtw.WriteAttributeString("default", "true"); + xtw.WriteEndElement(); + + xtw.WriteStartElement("saveType"); + xtw.WriteAttributeString("visible", "true"); + xtw.WriteAttributeString("inNamingOption", "true"); + xtw.WriteAttributeString("default", "true"); + xtw.WriteEndElement(); + + xtw.WriteStartElement("romSize"); + xtw.WriteAttributeString("visible", "true"); + xtw.WriteAttributeString("inNamingOption", "true"); + xtw.WriteAttributeString("default", "true"); + xtw.WriteEndElement(); + + xtw.WriteStartElement("releaseNumber"); + xtw.WriteAttributeString("visible", "true"); + xtw.WriteAttributeString("inNamingOption", "true"); + xtw.WriteAttributeString("default", "false"); + xtw.WriteEndElement(); + + xtw.WriteStartElement("languageNumber"); + xtw.WriteAttributeString("visible", "true"); + xtw.WriteAttributeString("inNamingOption", "true"); + xtw.WriteAttributeString("default", "false"); + xtw.WriteEndElement(); + + xtw.WriteStartElement("comment"); + xtw.WriteAttributeString("visible", "true"); + xtw.WriteAttributeString("inNamingOption", "true"); + xtw.WriteAttributeString("default", "false"); + xtw.WriteEndElement(); + + xtw.WriteStartElement("romCRC"); + xtw.WriteAttributeString("visible", "true"); + xtw.WriteAttributeString("inNamingOption", "true"); + xtw.WriteAttributeString("default", "false"); + xtw.WriteEndElement(); + + xtw.WriteStartElement("im1CRC"); + xtw.WriteAttributeString("visible", "false"); + xtw.WriteAttributeString("inNamingOption", "false"); + xtw.WriteAttributeString("default", "false"); + xtw.WriteEndElement(); + + xtw.WriteStartElement("im2CRC"); + xtw.WriteAttributeString("visible", "false"); + xtw.WriteAttributeString("inNamingOption", "false"); + xtw.WriteAttributeString("default", "false"); + xtw.WriteEndElement(); + + xtw.WriteStartElement("languages"); + xtw.WriteAttributeString("visible", "true"); + xtw.WriteAttributeString("inNamingOption", "true"); + xtw.WriteAttributeString("default", "true"); + xtw.WriteEndElement(); + + // End infos + xtw.WriteEndElement(); + + xtw.WriteStartElement("canOpen"); + xtw.WriteElementString("extension", ".bin"); + xtw.WriteEndElement(); + + xtw.WriteStartElement("newDat"); + xtw.WriteElementString("datVersionURL", Url); + + xtw.WriteStartElement("datUrl"); + xtw.WriteAttributeString("fileName", $"{FileName}.zip"); + xtw.WriteString(Url); + xtw.WriteEndElement(); + + xtw.WriteElementString("imURL", Url); + + // End newDat + xtw.WriteEndElement(); + + xtw.WriteStartElement("search"); + + xtw.WriteStartElement("to"); + xtw.WriteAttributeString("value", "location"); + xtw.WriteAttributeString("default", "true"); + xtw.WriteAttributeString("auto", "true"); + xtw.WriteEndElement(); + + xtw.WriteStartElement("to"); + xtw.WriteAttributeString("value", "romSize"); + xtw.WriteAttributeString("default", "true"); + xtw.WriteAttributeString("auto", "false"); + xtw.WriteEndElement(); + + xtw.WriteStartElement("to"); + xtw.WriteAttributeString("value", "languages"); + xtw.WriteAttributeString("default", "true"); + xtw.WriteAttributeString("auto", "true"); + xtw.WriteEndElement(); + + xtw.WriteStartElement("to"); + xtw.WriteAttributeString("value", "saveType"); + xtw.WriteAttributeString("default", "false"); + xtw.WriteAttributeString("auto", "false"); + xtw.WriteEndElement(); + + xtw.WriteStartElement("to"); + xtw.WriteAttributeString("value", "publisher"); + xtw.WriteAttributeString("default", "false"); + xtw.WriteAttributeString("auto", "true"); + xtw.WriteEndElement(); + + xtw.WriteStartElement("to"); + xtw.WriteAttributeString("value", "sourceRom"); + xtw.WriteAttributeString("default", "false"); + xtw.WriteAttributeString("auto", "true"); + xtw.WriteEndElement(); + + // End search + xtw.WriteEndElement(); + + xtw.WriteElementString("romTitle", "%u - %n"); + + // End configuration + xtw.WriteEndElement(); + + xtw.WriteStartElement("games"); + + xtw.Flush(); } catch (Exception ex) { @@ -936,72 +1027,104 @@ namespace SabreTools.Library.DatFiles /// <summary> /// Write out DatItem using the supplied StreamWriter /// </summary> - /// <param name="sw">StreamWriter to output to</param> - /// <param name="rom">DatItem object to be output</param> + /// <param name="xtw">XmlTextWriter to output to</param> + /// <param name="datItem">DatItem object to be output</param> /// <param name="ignoreblanks">True if blank roms should be skipped on output, false otherwise (default)</param> /// <returns>True if the data was written, false on error</returns> - private bool WriteDatItem(StreamWriter sw, DatItem rom, bool ignoreblanks = false) + private bool WriteDatItem(XmlTextWriter xtw, DatItem datItem, bool ignoreblanks = false) { // If we are in ignore blanks mode AND we have a blank (0-size) rom, skip - if (ignoreblanks - && (rom.ItemType == ItemType.Rom - && (((Rom)rom).Size == 0 || ((Rom)rom).Size == -1))) - { + if (ignoreblanks && (datItem.ItemType == ItemType.Rom && ((datItem as Rom).Size == 0 || (datItem as Rom).Size == -1))) return true; - } try { - string state = ""; + string state = string.Empty; // Pre-process the item name - ProcessItemName(rom, true); + ProcessItemName(datItem, true); - state += "\t\t<game>\n" - + "\t\t\t<imageNumber>1</imageNumber>\n" - + "\t\t\t<releaseNumber>1</releaseNumber>\n" - + "\t\t\t<title>" + (!ExcludeFields[(int)Field.Name] ? WebUtility.HtmlEncode(rom.Name) : "") + "\n" - + "\t\t\tNone\n"; + // Build the state based on excluded fields + xtw.WriteStartElement("game"); + xtw.WriteElementString("imageNumber", "1"); + xtw.WriteElementString("releaseNumber", "1"); + xtw.WriteElementString("title", datItem.GetField(Field.Name, ExcludeFields)); + xtw.WriteElementString("saveType", "None"); - if (rom.ItemType == ItemType.Rom) + if (datItem.ItemType == ItemType.Rom) { - state += "\t\t\t" + (!ExcludeFields[(int)Field.Size] ? ((Rom)rom).Size.ToString() : "") + "\n"; + var rom = datItem as Rom; + xtw.WriteElementString("romSize", datItem.GetField(Field.Size, ExcludeFields)); } - state += "\t\t\tNone\n" - + "\t\t\t0\n" - + "\t\t\tNone\n" - + "\t\t\t0\n"; + xtw.WriteElementString("publisher", "None"); + xtw.WriteElementString("location", "0"); + xtw.WriteElementString("sourceRom", "None"); + xtw.WriteElementString("language", "0"); - if (rom.ItemType == ItemType.Disk) + if (datItem.ItemType == ItemType.Disk) { - state += "\t\t\t\n" - + (((Disk)rom).MD5 != null - ? "\t\t\t\t" + (!ExcludeFields[(int)Field.MD5] ? ((Disk)rom).MD5.ToUpperInvariant() : "") + "\n" - : "\t\t\t\t" + (!ExcludeFields[(int)Field.SHA1] ? ((Disk)rom).SHA1.ToUpperInvariant() : "") + "\n") - + "\t\t\t\n"; + var disk = datItem as Disk; + xtw.WriteStartElement("files"); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields))) + { + xtw.WriteStartElement("romMD5"); + xtw.WriteAttributeString("extension", ".chd"); + xtw.WriteString(disk.MD5.ToUpperInvariant()); + xtw.WriteEndElement(); + } + else if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields))) + { + xtw.WriteStartElement("romSHA1"); + xtw.WriteAttributeString("extension", ".chd"); + xtw.WriteString(disk.SHA1.ToUpperInvariant()); + xtw.WriteEndElement(); + } + + // End files + xtw.WriteEndElement(); } - else if (rom.ItemType == ItemType.Rom) + else if (datItem.ItemType == ItemType.Rom) { - string tempext = "." + Utilities.GetExtension(((Rom)rom).Name); + var rom = datItem as Rom; + string tempext = "." + Utilities.GetExtension(rom.Name); - state += "\t\t\t\n" - + (((Rom)rom).CRC != null - ? "\t\t\t\t" + (!ExcludeFields[(int)Field.CRC] ? ((Rom)rom).CRC.ToUpperInvariant() : "") + "\n" - : ((Rom)rom).MD5 != null - ? "\t\t\t\t" + (!ExcludeFields[(int)Field.MD5] ? ((Rom)rom).MD5.ToUpperInvariant() : "") + "\n" - : "\t\t\t\t" + (!ExcludeFields[(int)Field.SHA1] ? ((Rom)rom).SHA1.ToUpperInvariant() : "") + "\n") - + "\t\t\t\n"; + xtw.WriteStartElement("files"); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CRC, ExcludeFields))) + { + xtw.WriteStartElement("romCRC"); + xtw.WriteAttributeString("extension", tempext); + xtw.WriteString(rom.CRC.ToUpperInvariant()); + xtw.WriteEndElement(); + } + else if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields))) + { + xtw.WriteStartElement("romMD5"); + xtw.WriteAttributeString("extension", tempext); + xtw.WriteString(rom.MD5.ToUpperInvariant()); + xtw.WriteEndElement(); + } + else if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields))) + { + xtw.WriteStartElement("romSHA1"); + xtw.WriteAttributeString("extension", tempext); + xtw.WriteString(rom.SHA1.ToUpperInvariant()); + xtw.WriteEndElement(); + } + + // End files + xtw.WriteEndElement(); } - state += "\t\t\t00000000\n" - + "\t\t\t00000000\n" - + "\t\t\t\n" - + "\t\t\t0\n" - + "\t\t\n"; + xtw.WriteElementString("im1CRC", "00000000"); + xtw.WriteElementString("im2CRC", "00000000"); + xtw.WriteElementString("comment", ""); + xtw.WriteElementString("duplicateID", "0"); + + // End game + xtw.WriteEndElement(); - sw.Write(state); - sw.Flush(); + xtw.Flush(); } catch (Exception ex) { @@ -1015,25 +1138,45 @@ namespace SabreTools.Library.DatFiles /// /// Write out DAT footer using the supplied StreamWriter /// - /// StreamWriter to output to + /// XmlTextWriter to output to /// True if the data was written, false on error - private bool WriteFooter(StreamWriter sw) + private bool WriteFooter(XmlTextWriter xtw) { try { - string footer = "\t\t" - + "\t\n" - + "\t\n" - + "\t\t\n" - + "\t\t\t\n" - + "\t\t\t\n" - + "\t\t\n" - + "\t\n" - + ""; + // End games + xtw.WriteEndElement(); - // Write the footer out - sw.Write(footer); - sw.Flush(); + xtw.WriteStartElement("gui"); + + xtw.WriteStartElement("images"); + xtw.WriteAttributeString("width", "487"); + xtw.WriteAttributeString("height", "162"); + + xtw.WriteStartElement("image"); + xtw.WriteAttributeString("x", "0"); + xtw.WriteAttributeString("y", "0"); + xtw.WriteAttributeString("width", "240"); + xtw.WriteAttributeString("height", "160"); + xtw.WriteEndElement(); + + xtw.WriteStartElement("image"); + xtw.WriteAttributeString("x", "245"); + xtw.WriteAttributeString("y", "0"); + xtw.WriteAttributeString("width", "240"); + xtw.WriteAttributeString("height", "160"); + xtw.WriteEndElement(); + + // End images + xtw.WriteEndElement(); + + // End gui + xtw.WriteEndElement(); + + // End dat + xtw.WriteEndElement(); + + xtw.Flush(); } catch (Exception ex) { diff --git a/SabreTools.Library/DatFiles/OpenMSX.cs b/SabreTools.Library/DatFiles/OpenMSX.cs index 2991ea4f..c0f3473c 100644 --- a/SabreTools.Library/DatFiles/OpenMSX.cs +++ b/SabreTools.Library/DatFiles/OpenMSX.cs @@ -1,20 +1,12 @@ using System; using System.Collections.Generic; -using System.Net; +using System.IO; using System.Text; using System.Xml; + using SabreTools.Library.Data; using SabreTools.Library.DatItems; using SabreTools.Library.Tools; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using FileStream = System.IO.FileStream; -using StreamWriter = System.IO.StreamWriter; -#endif using NaturalSort; namespace SabreTools.Library.DatFiles @@ -62,9 +54,7 @@ namespace SabreTools.Library.DatFiles // If we got a null reader, just return if (xtr == null) - { return; - } // Otherwise, read the file to the end try @@ -82,11 +72,12 @@ namespace SabreTools.Library.DatFiles switch (xtr.Name) { case "softwaredb": - Name = (String.IsNullOrWhiteSpace(Name) ? "openMSX Software List" : Name); - Description = (String.IsNullOrWhiteSpace(Description) ? Name : Name); + Name = (string.IsNullOrWhiteSpace(Name) ? "openMSX Software List" : Name); + Description = (string.IsNullOrWhiteSpace(Description) ? Name : Name); // string timestamp = xtr.GetAttribute("timestamp"); // CDATA xtr.Read(); break; + // We want to process the entire subtree of the software case "software": ReadSoftware(xtr.ReadSubtree(), filename, sysid, srcid, keep, clean, remUnicode); @@ -94,6 +85,7 @@ namespace SabreTools.Library.DatFiles // Skip the software now that we've processed it xtr.Skip(); break; + default: xtr.Read(); break; @@ -102,7 +94,7 @@ namespace SabreTools.Library.DatFiles } catch (Exception ex) { - Globals.Logger.Warning("Exception found while parsing '{0}': {1}", filename, ex); + Globals.Logger.Warning($"Exception found while parsing '{filename}': {ex}"); // For XML errors, just skip the affected node xtr?.Read(); @@ -136,9 +128,7 @@ namespace SabreTools.Library.DatFiles { // If we have an empty machine, skip it if (reader == null) - { return; - } // Otherwise, add what is possible reader.MoveToContent(); @@ -164,24 +154,30 @@ namespace SabreTools.Library.DatFiles case "title": machine.Name = reader.ReadElementContentAsString(); break; + case "genmsxid": // string id = reader.ReadElementContentAsString(); reader.Read(); break; + case "system": // string system = reader.ReadElementContentAsString(); reader.Read(); break; + case "company": machine.Manufacturer = reader.ReadElementContentAsString(); break; + case "year": machine.Year = reader.ReadElementContentAsString(); break; + case "country": // string country = reader.ReadElementContentAsString(); reader.Read(); break; + case "dump": containsItems = ReadDump(reader.ReadSubtree(), machine, diskno, filename, sysid, srcid, keep, clean, remUnicode); diskno++; @@ -189,6 +185,7 @@ namespace SabreTools.Library.DatFiles // Skip the dump now that we've processed it reader.Skip(); break; + default: reader.Read(); break; @@ -258,23 +255,27 @@ namespace SabreTools.Library.DatFiles // Skip the rom now that we've processed it reader.Skip(); break; + case "megarom": containsItems = ReadMegaRom(reader.ReadSubtree(), machine, diskno, filename, sysid, srcid, keep, clean, remUnicode); // Skip the megarom now that we've processed it reader.Skip(); break; + case "sccpluscart": containsItems = ReadSccPlusCart(reader.ReadSubtree(), machine, diskno, filename, sysid, srcid, keep, clean, remUnicode); // Skip the sccpluscart now that we've processed it reader.Skip(); break; + case "original": // bool value = Utilities.GetYesNo(reader.GetAttribute("value"); // string original = reader.ReadElementContentAsString(); reader.Read(); break; + default: reader.Read(); break; @@ -311,7 +312,7 @@ namespace SabreTools.Library.DatFiles bool clean, bool remUnicode) { - string hash = "", offset = "", type = "", remark = ""; + string hash = string.Empty, offset = string.Empty, type = string.Empty, remark = string.Empty; bool containsItems = false; while (!reader.EOF) @@ -330,15 +331,19 @@ namespace SabreTools.Library.DatFiles containsItems = true; hash = reader.ReadElementContentAsString(); break; + case "start": offset = reader.ReadElementContentAsString(); break; + case "type": type = reader.ReadElementContentAsString(); break; + case "remark": remark = reader.ReadElementContentAsString(); break; + default: reader.Read(); break; @@ -348,7 +353,7 @@ namespace SabreTools.Library.DatFiles // Create and add the new rom Rom rom = new Rom { - Name = machine.Name + "_" + diskno + (!String.IsNullOrWhiteSpace(remark) ? " " + remark : ""), + Name = machine.Name + "_" + diskno + (!string.IsNullOrWhiteSpace(remark) ? " " + remark : string.Empty), Offset = offset, Size = -1, SHA1 = Utilities.CleanHashData(hash, Constants.SHA1Length), @@ -387,7 +392,7 @@ namespace SabreTools.Library.DatFiles bool clean, bool remUnicode) { - string hash = "", offset = "", type = "", remark = ""; + string hash = string.Empty, offset = string.Empty, type = string.Empty, remark = string.Empty; bool containsItems = false; while (!reader.EOF) @@ -406,15 +411,19 @@ namespace SabreTools.Library.DatFiles containsItems = true; hash = reader.ReadElementContentAsString(); break; + case "start": offset = reader.ReadElementContentAsString(); break; + case "type": type = reader.ReadElementContentAsString(); break; + case "remark": remark = reader.ReadElementContentAsString(); break; + default: reader.Read(); break; @@ -424,7 +433,7 @@ namespace SabreTools.Library.DatFiles // Create and add the new rom Rom rom = new Rom { - Name = machine.Name + "_" + diskno + (!String.IsNullOrWhiteSpace(remark) ? " " + remark : ""), + Name = machine.Name + "_" + diskno + (!string.IsNullOrWhiteSpace(remark) ? " " + remark : string.Empty), Offset = offset, Size = -1, SHA1 = Utilities.CleanHashData(hash, Constants.SHA1Length), @@ -463,7 +472,7 @@ namespace SabreTools.Library.DatFiles bool clean, bool remUnicode) { - string hash = "", boot = "", remark = ""; + string hash = string.Empty, boot = string.Empty, remark = string.Empty; bool containsItems = false; while (!reader.EOF) @@ -481,13 +490,16 @@ namespace SabreTools.Library.DatFiles case "boot": boot = reader.ReadElementContentAsString(); break; + case "hash": containsItems = true; hash = reader.ReadElementContentAsString(); break; + case "remark": remark = reader.ReadElementContentAsString(); break; + default: reader.Read(); break; @@ -497,7 +509,7 @@ namespace SabreTools.Library.DatFiles // Create and add the new rom Rom rom = new Rom { - Name = machine.Name + "_" + diskno + (!String.IsNullOrWhiteSpace(remark) ? " " + remark : ""), + Name = machine.Name + "_" + diskno + (!string.IsNullOrWhiteSpace(remark) ? " " + remark : string.Empty), Size = -1, SHA1 = Utilities.CleanHashData(hash, Constants.SHA1Length), }; @@ -518,20 +530,21 @@ namespace SabreTools.Library.DatFiles { try { - Globals.Logger.User("Opening file for writing: {0}", outfile); + Globals.Logger.User($"Opening file for writing: {outfile}"); FileStream fs = Utilities.TryCreate(outfile); // If we get back null for some reason, just log and return if (fs == null) { - Globals.Logger.Warning("File '{0}' could not be created for writing! Please check to see if the file is writable", outfile); + Globals.Logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable"); return false; } - StreamWriter sw = new StreamWriter(fs, new UTF8Encoding(false)); + XmlTextWriter xtw = new XmlTextWriter(fs, new UTF8Encoding(false)); + xtw.Formatting = Formatting.Indented; // Write out the header - WriteHeader(sw); + WriteHeader(xtw); // Write out each of the machines and roms string lastgame = null; @@ -560,29 +573,25 @@ namespace SabreTools.Library.DatFiles // If we have a different game and we're not at the start of the list, output the end of last item if (lastgame != null && lastgame.ToLowerInvariant() != rom.MachineName.ToLowerInvariant()) - { - WriteEndGame(sw); - } + WriteEndGame(xtw); // If we have a new game, output the beginning of the new item if (lastgame == null || lastgame.ToLowerInvariant() != rom.MachineName.ToLowerInvariant()) - { - WriteStartGame(sw, rom); - } + WriteStartGame(xtw, rom); // If we have a "null" game (created by DATFromDir or something similar), log it to file if (rom.ItemType == ItemType.Rom && ((Rom)rom).Size == -1 && ((Rom)rom).CRC == "null") { - Globals.Logger.Verbose("Empty folder found: {0}", rom.MachineName); + Globals.Logger.Verbose($"Empty folder found: {rom.MachineName}"); lastgame = rom.MachineName; continue; } // Now, output the rom data - WriteDatItem(sw, rom, ignoreblanks); + WriteDatItem(xtw, rom, ignoreblanks); // Set the new data to compare against lastgame = rom.MachineName; @@ -590,10 +599,10 @@ namespace SabreTools.Library.DatFiles } // Write the file footer out - WriteFooter(sw); + WriteFooter(xtw); Globals.Logger.Verbose("File written!" + Environment.NewLine); - sw.Dispose(); + xtw.Dispose(); fs.Dispose(); } catch (Exception ex) @@ -608,32 +617,28 @@ namespace SabreTools.Library.DatFiles /// /// Write out DAT header using the supplied StreamWriter /// - /// StreamWriter to output to + /// XmlTextWriter to output to /// True if the data was written, false on error - private bool WriteHeader(StreamWriter sw) + private bool WriteHeader(XmlTextWriter xtw) { try { - string header = "\n" + - "\n" + - "\n" + - @" - -"; + // TODO: Figure out how to fix the issue with removed formatting after this point + //xtw.WriteComment("Credits"); + //xtw.WriteCData(@"The softwaredb.xml file contains information about rom mapper types - // Write the header out - sw.Write(header); - sw.Flush(); +//Copyright 2003 Nicolas Beyaert (Initial Database) +//Copyright 2004-2013 BlueMSX Team +//Copyright 2005-2018 openMSX Team +//Generation MSXIDs by www.generation-msx.nl"); + + xtw.Flush(); } catch (Exception ex) { @@ -647,29 +652,26 @@ Generation MSXIDs by www.generation-msx.nl /// /// Write out Game start using the supplied StreamWriter /// - /// StreamWriter to output to - /// DatItem object to be output + /// XmlTextWriter to output to + /// DatItem object to be output /// True if the data was written, false on error - private bool WriteStartGame(StreamWriter sw, DatItem rom) + private bool WriteStartGame(XmlTextWriter xtw, DatItem datItem) { try { // No game should start with a path separator - if (rom.MachineName.StartsWith(Path.DirectorySeparatorChar.ToString())) - { - rom.MachineName = rom.MachineName.Substring(1); - } + datItem.MachineName = datItem.MachineName.TrimStart(Path.DirectorySeparatorChar); - string state = "\n" - + "\t" + (!ExcludeFields[(int)Field.MachineName] ? WebUtility.HtmlEncode(rom.MachineName) : "") + "\n" - // + "\t" + msxid + "\n" - // + "\t" + system + "\n" - + "\t" + (!ExcludeFields[(int)Field.Manufacturer] ? rom.Manufacturer : "") + "\n" - + "\t" + (!ExcludeFields[(int)Field.Year] ? rom.Year : "") + "\n"; - // + "\t" + country + "\n"; + // Build the state based on excluded fields + xtw.WriteStartElement("software"); + xtw.WriteElementString("title", datItem.GetField(Field.MachineName, ExcludeFields)); + //xtw.WriteElementString("genmsxid", msxid); + //xtw.WriteElementString("system", system)); + xtw.WriteElementString("company", datItem.GetField(Field.Manufacturer, ExcludeFields)); + xtw.WriteElementString("year", datItem.GetField(Field.Year, ExcludeFields)); + //xtw.WriteElementString("country", country); - sw.Write(state); - sw.Flush(); + xtw.Flush(); } catch (Exception ex) { @@ -683,16 +685,16 @@ Generation MSXIDs by www.generation-msx.nl /// /// Write out Game start using the supplied StreamWriter /// - /// StreamWriter to output to + /// XmlTextWriter to output to /// True if the data was written, false on error - private bool WriteEndGame(StreamWriter sw) + private bool WriteEndGame(XmlTextWriter xtw) { try { - string state = "\n"; + // End software + xtw.WriteEndElement(); - sw.Write(state); - sw.Flush(); + xtw.Flush(); } catch (Exception ex) { @@ -706,53 +708,49 @@ Generation MSXIDs by www.generation-msx.nl /// /// Write out DatItem using the supplied StreamWriter /// - /// StreamWriter to output to - /// DatItem object to be output + /// XmlTextWriter to output to + /// DatItem object to be output /// True if blank roms should be skipped on output, false otherwise (default) /// True if the data was written, false on error - private bool WriteDatItem(StreamWriter sw, DatItem rom, bool ignoreblanks = false) + private bool WriteDatItem(XmlTextWriter xtw, DatItem datItem, bool ignoreblanks = false) { // If we are in ignore blanks mode AND we have a blank (0-size) rom, skip - if (ignoreblanks - && (rom.ItemType == ItemType.Rom - && (((Rom)rom).Size == 0 || ((Rom)rom).Size == -1))) - { + if (ignoreblanks && (datItem.ItemType == ItemType.Rom && ((datItem as Rom).Size == 0 || (datItem as Rom).Size == -1))) return true; - } try { - string state = ""; - // Pre-process the item name - ProcessItemName(rom, true); + ProcessItemName(datItem, true); - switch (rom.ItemType) + // Build the state based on excluded fields + switch (datItem.ItemType) { - case ItemType.Archive: - break; - case ItemType.BiosSet: - break; - case ItemType.Disk: - break; - case ItemType.Release: - break; case ItemType.Rom: // Currently this encapsulates rom, megarom, and sccpluscart - state += "\t\t" - // + "GoodMSX" - + "" - + (!ExcludeFields[(int)Field.Offset] && !String.IsNullOrWhiteSpace(((Rom)rom).Offset) ? "" + ((Rom)rom).Offset + "" : "") - // + "Normal" - + "" + (!ExcludeFields[(int)Field.SHA1] ? ((Rom)rom).SHA1 : "") + "" - // + "" - + "\n"; - break; - case ItemType.Sample: + var rom = datItem as Rom; + xtw.WriteStartElement("dump"); + + //xtw.WriteStartElement("original"); + //xtw.WriteAttributeString("value", "true"); + //xtw.WriteString("GoodMSX"); + //xtw.WriteEndElement(); + + xtw.WriteStartElement("rom"); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Offset, ExcludeFields))) + xtw.WriteElementString("start", rom.Offset); + //xtw.WriteElementString("type", "Normal"); + xtw.WriteElementString("hash", rom.GetField(Field.SHA1, ExcludeFields).ToLowerInvariant()); + //xtw.WriteElementString("remark", ""); + + // End rom + xtw.WriteEndElement(); + + // End dump + xtw.WriteEndElement(); break; } - sw.Write(state); - sw.Flush(); + xtw.Flush(); } catch (Exception ex) { @@ -766,17 +764,19 @@ Generation MSXIDs by www.generation-msx.nl /// /// Write out DAT footer using the supplied StreamWriter /// - /// StreamWriter to output to + /// XmlTextWriter to output to /// True if the data was written, false on error - private bool WriteFooter(StreamWriter sw) + private bool WriteFooter(XmlTextWriter xtw) { try { - string footer = "\n\n"; + // End software + xtw.WriteEndElement(); - // Write the footer out - sw.Write(footer); - sw.Flush(); + // End softwaredb + xtw.WriteEndElement(); + + xtw.Flush(); } catch (Exception ex) { diff --git a/SabreTools.Library/DatFiles/RomCenter.cs b/SabreTools.Library/DatFiles/RomCenter.cs index a06bc25d..97862d79 100644 --- a/SabreTools.Library/DatFiles/RomCenter.cs +++ b/SabreTools.Library/DatFiles/RomCenter.cs @@ -1,20 +1,11 @@ using System; using System.Collections.Generic; -using System.Net; +using System.IO; using System.Text; + using SabreTools.Library.Data; using SabreTools.Library.DatItems; using SabreTools.Library.Tools; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using FileStream = System.IO.FileStream; -using StreamReader = System.IO.StreamReader; -using StreamWriter = System.IO.StreamWriter; -#endif using NaturalSort; namespace SabreTools.Library.DatFiles @@ -22,6 +13,7 @@ namespace SabreTools.Library.DatFiles /// /// Represents parsing and writing of a RomCenter DAT /// + /// TODO: Port over the INI parser from DICUI to make this flow a little better internal class RomCenter : DatFile { /// @@ -57,7 +49,7 @@ namespace SabreTools.Library.DatFiles Encoding enc = Utilities.GetEncoding(filename); StreamReader sr = new StreamReader(Utilities.TryOpenRead(filename), enc); - string blocktype = ""; + string blocktype = string.Empty; while (!sr.EndOfStream) { string line = sr.ReadLine(); @@ -67,87 +59,93 @@ namespace SabreTools.Library.DatFiles { blocktype = "credits"; } + // If the line is the start of the dat section else if (line.ToLowerInvariant().StartsWith("[dat]")) { blocktype = "dat"; } + // If the line is the start of the emulator section else if (line.ToLowerInvariant().StartsWith("[emulator]")) { blocktype = "emulator"; } + // If the line is the start of the game section else if (line.ToLowerInvariant().StartsWith("[games]")) { blocktype = "games"; } + // Otherwise, it's not a section and it's data, so get out all data else { // If we have an author if (line.ToLowerInvariant().StartsWith("author=")) { - Author = (String.IsNullOrWhiteSpace(Author) ? line.Split('=')[1] : Author); + Author = (string.IsNullOrWhiteSpace(Author) ? line.Split('=')[1] : Author); } + // If we have one of the three version tags else if (line.ToLowerInvariant().StartsWith("version=")) { switch (blocktype) { case "credits": - Version = (String.IsNullOrWhiteSpace(Version) ? line.Split('=')[1] : Version); + Version = (string.IsNullOrWhiteSpace(Version) ? line.Split('=')[1] : Version); break; + case "emulator": - Description = (String.IsNullOrWhiteSpace(Description) ? line.Split('=')[1] : Description); + Description = (string.IsNullOrWhiteSpace(Description) ? line.Split('=')[1] : Description); break; } } + // If we have a URL else if (line.ToLowerInvariant().StartsWith("url=")) { - Url = (String.IsNullOrWhiteSpace(Url) ? line.Split('=')[1] : Url); + Url = (string.IsNullOrWhiteSpace(Url) ? line.Split('=')[1] : Url); } + // If we have a comment else if (line.ToLowerInvariant().StartsWith("comment=")) { - Comment = (String.IsNullOrWhiteSpace(Comment) ? line.Split('=')[1] : Comment); + Comment = (string.IsNullOrWhiteSpace(Comment) ? line.Split('=')[1] : Comment); } + // If we have the split flag else if (line.ToLowerInvariant().StartsWith("split=")) { if (Int32.TryParse(line.Split('=')[1], out int split)) { if (split == 1 && ForceMerging == ForceMerging.None) - { ForceMerging = ForceMerging.Split; - } } } + // If we have the merge tag else if (line.ToLowerInvariant().StartsWith("merge=")) { if (Int32.TryParse(line.Split('=')[1], out int merge)) { if (merge == 1 && ForceMerging == ForceMerging.None) - { ForceMerging = ForceMerging.Full; - } } } + // If we have the refname tag else if (line.ToLowerInvariant().StartsWith("refname=")) { - Name = (String.IsNullOrWhiteSpace(Name) ? line.Split('=')[1] : Name); + Name = (string.IsNullOrWhiteSpace(Name) ? line.Split('=')[1] : Name); } + // If we have a rom else if (line.StartsWith("¬")) { // Some old RC DATs have this behavior if (line.Contains("¬N¬O")) - { - line = line.Replace("¬N¬O", "") + "¬¬"; - } + line = line.Replace("¬N¬O", string.Empty) + "¬¬"; /* The rominfo order is as follows: @@ -165,9 +163,7 @@ namespace SabreTools.Library.DatFiles // Try getting the size separately if (!Int64.TryParse(rominfo[7], out long size)) - { size = 0; - } Rom rom = new Rom { @@ -204,13 +200,13 @@ namespace SabreTools.Library.DatFiles { try { - Globals.Logger.User("Opening file for writing: {0}", outfile); + Globals.Logger.User($"Opening file for writing: {outfile}"); FileStream fs = Utilities.TryCreate(outfile); // If we get back null for some reason, just log and return if (fs == null) { - Globals.Logger.Warning("File '{0}' could not be created for writing! Please check to see if the file is writable", outfile); + Globals.Logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable"); return false; } @@ -250,7 +246,7 @@ namespace SabreTools.Library.DatFiles && ((Rom)rom).Size == -1 && ((Rom)rom).CRC == "null") { - Globals.Logger.Verbose("Empty folder found: {0}", rom.MachineName); + Globals.Logger.Verbose($"Empty folder found: {rom.MachineName}"); rom.Name = (rom.Name == "null" ? "-" : rom.Name); ((Rom)rom).Size = Constants.SizeZero; @@ -293,18 +289,18 @@ namespace SabreTools.Library.DatFiles { try { - string header = header = "[CREDITS]\n" + - "author=" + Author + "\n" + - "version=" + Version + "\n" + - "comment=" + Comment + "\n" + - "[DAT]\n" + - "version=2.50\n" + - "split=" + (ForceMerging == ForceMerging.Split ? "1" : "0") + "\n" + - "merge=" + (ForceMerging == ForceMerging.Full || ForceMerging == ForceMerging.Merged ? "1" : "0") + "\n" + - "[EMULATOR]\n" + - "refname=" + Name + "\n" + - "version=" + Description + "\n" + - "[GAMES]\n"; + string header = "[CREDITS]\n"; + header += $"author={Author}\n"; + header += $"version={Version}\n"; + header += $"comment={Comment}\n"; + header += "[DAT]\n"; + header += "version=2.50\n"; + header += $"split={(ForceMerging == ForceMerging.Split ? "1" : "0")}\n"; + header += $"merge={(ForceMerging == ForceMerging.Full || ForceMerging == ForceMerging.Merged ? "1" : "0")}\n"; + header += "[EMULATOR]\n"; + header += $"refname={Name}\n"; + header += $"version={Description}\n"; + header += "[GAMES]\n"; // Write the header out sw.Write(header); @@ -323,44 +319,59 @@ namespace SabreTools.Library.DatFiles /// Write out DatItem using the supplied StreamWriter /// /// StreamWriter to output to - /// DatItem object to be output + /// DatItem object to be output /// True if blank roms should be skipped on output, false otherwise (default) /// True if the data was written, false on error - private bool WriteDatItem(StreamWriter sw, DatItem rom, bool ignoreblanks = false) + private bool WriteDatItem(StreamWriter sw, DatItem datItem, bool ignoreblanks = false) { // If we are in ignore blanks mode AND we have a blank (0-size) rom, skip - if (ignoreblanks - && (rom.ItemType == ItemType.Rom - && (((Rom)rom).Size == 0 || ((Rom)rom).Size == -1))) - { + if (ignoreblanks && (datItem.ItemType == ItemType.Rom && ((datItem as Rom).Size == 0 || (datItem as Rom).Size == -1))) return true; - } try { - string state = ""; + string state = string.Empty; // Pre-process the item name - ProcessItemName(rom, true); + ProcessItemName(datItem, true); - if (rom.ItemType == ItemType.Rom) + // Build the state based on excluded fields + switch (datItem.ItemType) { - state += "¬" + (!ExcludeFields[(int)Field.CloneOf] && String.IsNullOrWhiteSpace(rom.CloneOf) ? WebUtility.HtmlEncode(rom.CloneOf) : "") + - "¬" + (!ExcludeFields[(int)Field.CloneOf] && String.IsNullOrWhiteSpace(rom.CloneOf) ? WebUtility.HtmlEncode(rom.CloneOf) : "") + - "¬" + (!ExcludeFields[(int)Field.MachineName] ? WebUtility.HtmlEncode(rom.MachineName) : "") + - "¬" + (!ExcludeFields[(int)Field.Description] ? WebUtility.HtmlEncode((String.IsNullOrWhiteSpace(rom.MachineDescription) ? rom.MachineName : rom.MachineDescription)) : "") + - "¬" + (!ExcludeFields[(int)Field.Name] ? WebUtility.HtmlEncode(rom.Name) : "") + - "¬" + (!ExcludeFields[(int)Field.CRC] ? ((Rom)rom).CRC.ToLowerInvariant() : "") + - "¬" + (!ExcludeFields[(int)Field.Size] && ((Rom)rom).Size != -1 ? ((Rom)rom).Size.ToString() : "") + "¬¬¬\n"; - } - else if (rom.ItemType == ItemType.Disk) - { - state += "¬" + (!ExcludeFields[(int)Field.CloneOf] && String.IsNullOrWhiteSpace(rom.CloneOf) ? WebUtility.HtmlEncode(rom.CloneOf) : "") + - "¬" + (!ExcludeFields[(int)Field.CloneOf] && String.IsNullOrWhiteSpace(rom.CloneOf) ? WebUtility.HtmlEncode(rom.CloneOf) : "") + - "¬" + (!ExcludeFields[(int)Field.MachineName] ? WebUtility.HtmlEncode(rom.MachineName) : "") + - "¬" + (!ExcludeFields[(int)Field.Description] ? WebUtility.HtmlEncode((String.IsNullOrWhiteSpace(rom.MachineDescription) ? rom.MachineName : rom.MachineDescription)) : "") + - "¬" + (!ExcludeFields[(int)Field.Name] ? WebUtility.HtmlEncode(rom.Name) : "") + - "¬¬¬¬¬\n"; + case ItemType.Disk: + state += "¬"; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CloneOf, ExcludeFields))) + state += datItem.CloneOf; + state += "¬"; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CloneOf, ExcludeFields))) + state += datItem.CloneOf; + state += $"¬{datItem.GetField(Field.MachineName, ExcludeFields)}"; + if (string.IsNullOrWhiteSpace(datItem.MachineDescription)) + state += $"¬{datItem.GetField(Field.MachineName, ExcludeFields)}"; + else + state += $"¬{datItem.GetField(Field.Description, ExcludeFields)}"; + state += $"¬{datItem.GetField(Field.Name, ExcludeFields)}"; + state += "¬¬¬¬¬\n"; + break; + + case ItemType.Rom: + var rom = datItem as Rom; + state += "¬"; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CloneOf, ExcludeFields))) + state += datItem.CloneOf; + state += "¬"; + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CloneOf, ExcludeFields))) + state += datItem.CloneOf; + state += $"¬{datItem.GetField(Field.MachineName, ExcludeFields)}"; + if (string.IsNullOrWhiteSpace(datItem.MachineDescription)) + state += $"¬{datItem.GetField(Field.MachineName, ExcludeFields)}"; + else + state += $"¬{datItem.GetField(Field.Description, ExcludeFields)}"; + state += $"¬{datItem.GetField(Field.Name, ExcludeFields)}"; + state += $"¬{datItem.GetField(Field.CRC, ExcludeFields)}"; + state += $"¬{datItem.GetField(Field.Size, ExcludeFields)}"; + state += "¬¬¬\n"; + break; } sw.Write(state); diff --git a/SabreTools.Library/DatFiles/SabreDat.cs b/SabreTools.Library/DatFiles/SabreDat.cs index bcb818a9..5fcb4e4f 100644 --- a/SabreTools.Library/DatFiles/SabreDat.cs +++ b/SabreTools.Library/DatFiles/SabreDat.cs @@ -1,21 +1,13 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; -using System.Net; using System.Text; using System.Xml; + using SabreTools.Library.Data; using SabreTools.Library.DatItems; using SabreTools.Library.Tools; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using FileStream = System.IO.FileStream; -using StreamWriter = System.IO.StreamWriter; -#endif using NaturalSort; namespace SabreTools.Library.DatFiles @@ -57,7 +49,7 @@ namespace SabreTools.Library.DatFiles { // Prepare all internal variables bool empty = true; - string key = ""; + string key = string.Empty; List parent = new List(); Encoding enc = Utilities.GetEncoding(filename); @@ -65,9 +57,7 @@ namespace SabreTools.Library.DatFiles // If we got a null reader, just return if (xtr == null) - { return; - } // Otherwise, read the file to the end try @@ -81,7 +71,7 @@ namespace SabreTools.Library.DatFiles // If we didn't find any items in the folder, make sure to add the blank rom if (empty) { - string tempgame = String.Join("\\", parent); + string tempgame = string.Join("\\", parent); Rom rom = new Rom("null", tempgame, omitFromScan: Hash.DeepHashes); // TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually // Now process and add the rom @@ -92,7 +82,7 @@ namespace SabreTools.Library.DatFiles int parentcount = parent.Count; if (parentcount == 0) { - Globals.Logger.Verbose("Empty parent '{0}' found in '{1}'", String.Join("\\", parent), filename); + Globals.Logger.Verbose($"Empty parent '{string.Join("\\", parent)}' found in '{filename}'"); empty = true; } @@ -101,9 +91,7 @@ namespace SabreTools.Library.DatFiles { parent.RemoveAt(parent.Count - 1); if (keep && parentcount > 1) - { - Type = (String.IsNullOrWhiteSpace(Type) ? "SuperDAT" : Type); - } + Type = (string.IsNullOrWhiteSpace(Type) ? "SuperDAT" : Type); } } @@ -123,6 +111,7 @@ namespace SabreTools.Library.DatFiles // Skip the header node now that we've processed it xtr.Skip(); break; + case "dir": case "directory": empty = ReadDirectory(xtr.ReadSubtree(), parent, filename, sysid, srcid, keep, clean, remUnicode); @@ -138,7 +127,7 @@ namespace SabreTools.Library.DatFiles } catch (Exception ex) { - Globals.Logger.Warning("Exception found while parsing '{0}': {1}", filename, ex); + Globals.Logger.Warning($"Exception found while parsing '{filename}': {ex}"); // For XML errors, just skip the affected node xtr?.Read(); @@ -158,9 +147,7 @@ namespace SabreTools.Library.DatFiles // If there's no subtree to the header, skip it if (reader == null) - { return; - } // Otherwise, read what we can from the header while (!reader.EOF) @@ -173,55 +160,64 @@ namespace SabreTools.Library.DatFiles } // Get all header items (ONLY OVERWRITE IF THERE'S NO DATA) - string content = ""; + string content = string.Empty; switch (reader.Name) { case "name": content = reader.ReadElementContentAsString(); ; - Name = (String.IsNullOrWhiteSpace(Name) ? content : Name); + Name = (string.IsNullOrWhiteSpace(Name) ? content : Name); superdat = superdat || content.Contains(" - SuperDAT"); if (keep && superdat) { - Type = (String.IsNullOrWhiteSpace(Type) ? "SuperDAT" : Type); + Type = (string.IsNullOrWhiteSpace(Type) ? "SuperDAT" : Type); } break; + case "description": content = reader.ReadElementContentAsString(); - Description = (String.IsNullOrWhiteSpace(Description) ? content : Description); + Description = (string.IsNullOrWhiteSpace(Description) ? content : Description); break; + case "rootdir": content = reader.ReadElementContentAsString(); - RootDir = (String.IsNullOrWhiteSpace(RootDir) ? content : RootDir); + RootDir = (string.IsNullOrWhiteSpace(RootDir) ? content : RootDir); break; + case "category": content = reader.ReadElementContentAsString(); - Category = (String.IsNullOrWhiteSpace(Category) ? content : Category); + Category = (string.IsNullOrWhiteSpace(Category) ? content : Category); break; + case "version": content = reader.ReadElementContentAsString(); - Version = (String.IsNullOrWhiteSpace(Version) ? content : Version); + Version = (string.IsNullOrWhiteSpace(Version) ? content : Version); break; + case "date": content = reader.ReadElementContentAsString(); - Date = (String.IsNullOrWhiteSpace(Date) ? content.Replace(".", "/") : Date); + Date = (string.IsNullOrWhiteSpace(Date) ? content.Replace(".", "/") : Date); break; + case "author": content = reader.ReadElementContentAsString(); - Author = (String.IsNullOrWhiteSpace(Author) ? content : Author); - Email = (String.IsNullOrWhiteSpace(Email) ? reader.GetAttribute("email") : Email); - Homepage = (String.IsNullOrWhiteSpace(Homepage) ? reader.GetAttribute("homepage") : Homepage); - Url = (String.IsNullOrWhiteSpace(Url) ? reader.GetAttribute("url") : Url); + Author = (string.IsNullOrWhiteSpace(Author) ? content : Author); + Email = (string.IsNullOrWhiteSpace(Email) ? reader.GetAttribute("email") : Email); + Homepage = (string.IsNullOrWhiteSpace(Homepage) ? reader.GetAttribute("homepage") : Homepage); + Url = (string.IsNullOrWhiteSpace(Url) ? reader.GetAttribute("url") : Url); break; + case "comment": content = reader.ReadElementContentAsString(); - Comment = (String.IsNullOrWhiteSpace(Comment) ? content : Comment); + Comment = (string.IsNullOrWhiteSpace(Comment) ? content : Comment); break; + case "flags": ReadFlags(reader.ReadSubtree(), superdat); // Skip the flags node now that we've processed it reader.Skip(); break; + default: reader.Read(); break; @@ -255,21 +251,17 @@ namespace SabreTools.Library.DatFiles // Prepare all internal variables XmlReader flagreader; bool empty = true; - string key = "", date = ""; + string key = string.Empty, date = string.Empty; long size = -1; ItemStatus its = ItemStatus.None; // If there's no subtree to the header, skip it if (reader == null) - { return empty; - } - string foldername = (reader.GetAttribute("name") ?? ""); - if (!String.IsNullOrWhiteSpace(foldername)) - { + string foldername = (reader.GetAttribute("name") ?? string.Empty); + if (!string.IsNullOrWhiteSpace(foldername)) parent.Add(foldername); - } // Otherwise, read what we can from the directory while (!reader.EOF) @@ -280,7 +272,7 @@ namespace SabreTools.Library.DatFiles // If we didn't find any items in the folder, make sure to add the blank rom if (empty) { - string tempgame = String.Join("\\", parent); + string tempgame = string.Join("\\", parent); Rom rom = new Rom("null", tempgame, omitFromScan: Hash.DeepHashes); // TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually // Now process and add the rom @@ -291,7 +283,7 @@ namespace SabreTools.Library.DatFiles int parentcount = parent.Count; if (parentcount == 0) { - Globals.Logger.Verbose("Empty parent '{0}' found in '{1}'", String.Join("\\", parent), filename); + Globals.Logger.Verbose($"Empty parent '{string.Join("\\", parent)}' found in '{filename}'"); empty = true; } @@ -300,9 +292,7 @@ namespace SabreTools.Library.DatFiles { parent.RemoveAt(parent.Count - 1); if (keep && parentcount > 1) - { - Type = (String.IsNullOrWhiteSpace(Type) ? "SuperDAT" : Type); - } + Type = (string.IsNullOrWhiteSpace(Type) ? "SuperDAT" : Type); } } @@ -314,7 +304,7 @@ namespace SabreTools.Library.DatFiles } // Get all directory items - string content = ""; + string content = string.Empty; switch (reader.Name) { // Directories can contain directories @@ -325,6 +315,7 @@ namespace SabreTools.Library.DatFiles // Skip the directory node now that we've processed it reader.Read(); break; + case "file": empty = false; @@ -371,7 +362,7 @@ namespace SabreTools.Library.DatFiles Machine dir = new Machine(); // Get the name of the game from the parent - dir.Name = String.Join("\\", parent); + dir.Name = string.Join("\\", parent); dir.Description = dir.Name; DatItem datItem; @@ -387,6 +378,7 @@ namespace SabreTools.Library.DatFiles SourceID = srcid, }; break; + case "biosset": datItem = new BiosSet { @@ -399,6 +391,7 @@ namespace SabreTools.Library.DatFiles SourceID = srcid, }; break; + case "disk": datItem = new Disk { @@ -416,6 +409,7 @@ namespace SabreTools.Library.DatFiles SourceID = srcid, }; break; + case "release": datItem = new Release { @@ -430,6 +424,7 @@ namespace SabreTools.Library.DatFiles SourceID = srcid, }; break; + case "rom": datItem = new Rom { @@ -450,6 +445,7 @@ namespace SabreTools.Library.DatFiles SourceID = srcid, }; break; + case "sample": datItem = new Sample { @@ -460,6 +456,7 @@ namespace SabreTools.Library.DatFiles SourceID = srcid, }; break; + default: // By default, create a new Blank, just in case datItem = new Blank(); @@ -487,13 +484,11 @@ namespace SabreTools.Library.DatFiles private void ReadFlags(XmlReader reader, bool superdat) { // Prepare all internal variables - string content = ""; + string content = string.Empty; // If we somehow have a null flag section, skip it if (reader == null) - { return; - } while (!reader.EOF) { @@ -513,21 +508,24 @@ namespace SabreTools.Library.DatFiles switch (reader.GetAttribute("name").ToLowerInvariant()) { case "type": - Type = (String.IsNullOrWhiteSpace(Type) ? content : Type); + Type = (string.IsNullOrWhiteSpace(Type) ? content : Type); superdat = superdat || content.Contains("SuperDAT"); break; + case "forcemerging": if (ForceMerging == ForceMerging.None) { ForceMerging = Utilities.GetForceMerging(content); } break; + case "forcenodump": if (ForceNodump == ForceNodump.None) { ForceNodump = Utilities.GetForceNodump(content); } break; + case "forcepacking": if (ForcePacking == ForcePacking.None) { @@ -536,8 +534,10 @@ namespace SabreTools.Library.DatFiles break; } } + reader.Read(); break; + default: reader.Read(); break; @@ -556,20 +556,21 @@ namespace SabreTools.Library.DatFiles { try { - Globals.Logger.User("Opening file for writing: {0}", outfile); + Globals.Logger.User($"Opening file for writing: {outfile}"); FileStream fs = Utilities.TryCreate(outfile); // If we get back null for some reason, just log and return if (fs == null) { - Globals.Logger.Warning("File '{0}' could not be created for writing! Please check to see if the file is writable", outfile); + Globals.Logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable"); return false; } - StreamWriter sw = new StreamWriter(fs, new UTF8Encoding(false)); + XmlTextWriter xtw = new XmlTextWriter(fs, new UTF8Encoding(false)); + xtw.Formatting = Formatting.Indented; // Write out the header - WriteHeader(sw); + WriteHeader(xtw); // Write out each of the machines and roms int depth = 2, last = -1; @@ -602,22 +603,18 @@ namespace SabreTools.Library.DatFiles // If we have a different game and we're not at the start of the list, output the end of last item if (lastgame != null && lastgame.ToLowerInvariant() != rom.MachineName.ToLowerInvariant()) - { - depth = WriteEndGame(sw, splitpath, newsplit, depth, out last); - } + depth = WriteEndGame(xtw, splitpath, newsplit, depth, out last); // If we have a new game, output the beginning of the new item if (lastgame == null || lastgame.ToLowerInvariant() != rom.MachineName.ToLowerInvariant()) - { - depth = WriteStartGame(sw, rom, newsplit, lastgame, depth, last); - } + depth = WriteStartGame(xtw, rom, newsplit, lastgame, depth, last); // If we have a "null" game (created by DATFromDir or something similar), log it to file if (rom.ItemType == ItemType.Rom && ((Rom)rom).Size == -1 && ((Rom)rom).CRC == "null") { - Globals.Logger.Verbose("Empty folder found: {0}", rom.MachineName); + Globals.Logger.Verbose($"Empty folder found: {rom.MachineName}"); splitpath = newsplit; lastgame = rom.MachineName; @@ -625,7 +622,7 @@ namespace SabreTools.Library.DatFiles } // Now, output the rom data - WriteDatItem(sw, rom, depth, ignoreblanks); + WriteDatItem(xtw, rom, depth, ignoreblanks); // Set the new data to compare against splitpath = newsplit; @@ -634,10 +631,10 @@ namespace SabreTools.Library.DatFiles } // Write the file footer out - WriteFooter(sw, depth); + WriteFooter(xtw, depth); Globals.Logger.Verbose("File written!" + Environment.NewLine); - sw.Dispose(); + xtw.Dispose(); fs.Dispose(); } catch (Exception ex) @@ -652,44 +649,119 @@ namespace SabreTools.Library.DatFiles /// /// Write out DAT header using the supplied StreamWriter /// - /// StreamWriter to output to + /// XmlTextWriter to output to /// True if the data was written, false on error - private bool WriteHeader(StreamWriter sw) + private bool WriteHeader(XmlTextWriter xtw) { try { - string header = "\n" + - "\n\n" + - "\n" + - "\t
\n" + - "\t\t" + WebUtility.HtmlEncode(Name) + "\n" + - "\t\t" + WebUtility.HtmlEncode(Description) + "\n" + - (!String.IsNullOrWhiteSpace(RootDir) ? "\t\t" + WebUtility.HtmlEncode(RootDir) + "\n" : "") + - (!String.IsNullOrWhiteSpace(Category) ? "\t\t" + WebUtility.HtmlEncode(Category) + "\n" : "") + - "\t\t" + WebUtility.HtmlEncode(Version) + "\n" + - (!String.IsNullOrWhiteSpace(Date) ? "\t\t" + WebUtility.HtmlEncode(Date) + "\n" : "") + - "\t\t" + WebUtility.HtmlEncode(Author) + "\n" + - (!String.IsNullOrWhiteSpace(Comment) ? "\t\t" + WebUtility.HtmlEncode(Comment) + "\n" : "") + - (!String.IsNullOrWhiteSpace(Type) || ForcePacking != ForcePacking.None || ForceMerging != ForceMerging.None || ForceNodump != ForceNodump.None ? - "\t\t\n" + - (!String.IsNullOrWhiteSpace(Type) ? "\t\t\t\n" : "") + - (ForcePacking == ForcePacking.Unzip ? "\t\t\t\n" : "") + - (ForcePacking == ForcePacking.Zip ? "\t\t\t\n" : "") + - (ForceMerging == ForceMerging.Full ? "\t\t\t\n" : "") + - (ForceMerging == ForceMerging.Split ? "\t\t\t\n" : "") + - (ForceMerging == ForceMerging.Merged ? "\t\t\t\n" : "") + - (ForceMerging == ForceMerging.NonMerged ? "\t\t\t\n" : "") + - (ForceNodump == ForceNodump.Ignore ? "\t\t\t\n" : "") + - (ForceNodump == ForceNodump.Obsolete ? "\t\t\t\n" : "") + - (ForceNodump == ForceNodump.Required ? "\t\t\t\n" : "") + - "\t\t\n" - : "") + - "\t
\n" + - "\t\n"; + xtw.WriteStartDocument(); + xtw.WriteDocType("sabredat", null, "newdat.xsd", null); - // Write the header out - sw.Write(header); - sw.Flush(); + xtw.WriteStartElement("datafile"); + + xtw.WriteStartElement("header"); + + xtw.WriteElementString("name", Name); + xtw.WriteElementString("description", Description); + if (!string.IsNullOrWhiteSpace(RootDir)) + xtw.WriteElementString("rootdir", RootDir); + if (!string.IsNullOrWhiteSpace(Category)) + xtw.WriteElementString("category", Category); + xtw.WriteElementString("version", Version); + if (!string.IsNullOrWhiteSpace(Date)) + xtw.WriteElementString("date", Date); + xtw.WriteElementString("author", Author); + if (!string.IsNullOrWhiteSpace(Comment)) + xtw.WriteElementString("comment", Comment); + if (!string.IsNullOrWhiteSpace(Type) || ForcePacking != ForcePacking.None || ForceMerging != ForceMerging.None || ForceNodump != ForceNodump.None) + { + xtw.WriteStartElement("flags"); + + if (!string.IsNullOrWhiteSpace(Type)) + { + xtw.WriteStartElement("flag"); + xtw.WriteAttributeString("name", "type"); + xtw.WriteAttributeString("value", Type); + xtw.WriteEndElement(); + } + + switch (ForcePacking) + { + case ForcePacking.Unzip: + xtw.WriteStartElement("flag"); + xtw.WriteAttributeString("name", "forcepacking"); + xtw.WriteAttributeString("value", "unzip"); + xtw.WriteEndElement(); + break; + case ForcePacking.Zip: + xtw.WriteStartElement("flag"); + xtw.WriteAttributeString("name", "forcepacking"); + xtw.WriteAttributeString("value", "zip"); + xtw.WriteEndElement(); + break; + } + + switch (ForceMerging) + { + case ForceMerging.Full: + xtw.WriteStartElement("flag"); + xtw.WriteAttributeString("name", "forcemerging"); + xtw.WriteAttributeString("value", "full"); + xtw.WriteEndElement(); + break; + case ForceMerging.Split: + xtw.WriteStartElement("flag"); + xtw.WriteAttributeString("name", "forcemerging"); + xtw.WriteAttributeString("value", "split"); + xtw.WriteEndElement(); + break; + case ForceMerging.Merged: + xtw.WriteStartElement("flag"); + xtw.WriteAttributeString("name", "forcemerging"); + xtw.WriteAttributeString("value", "merged"); + xtw.WriteEndElement(); + break; + case ForceMerging.NonMerged: + xtw.WriteStartElement("flag"); + xtw.WriteAttributeString("name", "forcemerging"); + xtw.WriteAttributeString("value", "nonmerged"); + xtw.WriteEndElement(); + break; + } + + switch (ForceNodump) + { + case ForceNodump.Ignore: + xtw.WriteStartElement("flag"); + xtw.WriteAttributeString("name", "forcenodump"); + xtw.WriteAttributeString("value", "ignore"); + xtw.WriteEndElement(); + break; + case ForceNodump.Obsolete: + xtw.WriteStartElement("flag"); + xtw.WriteAttributeString("name", "forcenodump"); + xtw.WriteAttributeString("value", "obsolete"); + xtw.WriteEndElement(); + break; + case ForceNodump.Required: + xtw.WriteStartElement("flag"); + xtw.WriteAttributeString("name", "forcenodump"); + xtw.WriteAttributeString("value", "required"); + xtw.WriteEndElement(); + break; + } + + // End flags + xtw.WriteEndElement(); + } + + // End header + xtw.WriteEndElement(); + + xtw.WriteStartElement("data"); + + xtw.Flush(); } catch (Exception ex) { @@ -703,37 +775,31 @@ namespace SabreTools.Library.DatFiles /// /// Write out Game start using the supplied StreamWriter /// - /// StreamWriter to output to - /// DatItem object to be output + /// XmlTextWriter to output to + /// DatItem object to be output /// Split path representing the parent game (SabreDAT only) /// The name of the last game to be output /// Current depth to output file at (SabreDAT only) /// Last known depth to cycle back from (SabreDAT only) /// The new depth of the tag - private int WriteStartGame(StreamWriter sw, DatItem rom, List newsplit, string lastgame, int depth, int last) + private int WriteStartGame(XmlTextWriter xtw, DatItem datItem, List newsplit, string lastgame, int depth, int last) { try { // No game should start with a path separator - if (rom.MachineName.StartsWith(Path.DirectorySeparatorChar.ToString())) - { - rom.MachineName = rom.MachineName.Substring(1); - } + datItem.MachineName = datItem.MachineName.TrimStart(Path.DirectorySeparatorChar); - string state = ""; + // Build the state based on excluded fields for (int i = (last == -1 ? 0 : last); i < newsplit.Count; i++) { - for (int j = 0; j < depth - last + i - (lastgame == null ? 1 : 0); j++) - { - state += "\t"; - } - state += "\n"; + xtw.WriteStartElement("directory"); + xtw.WriteAttributeString("name", !ExcludeFields[(int)Field.MachineName] ? newsplit[i] : string.Empty); + xtw.WriteAttributeString("description", !ExcludeFields[(int)Field.MachineName] ? newsplit[i] : string.Empty); } + depth = depth - (last == -1 ? 0 : last) + newsplit.Count; - sw.Write(state); - sw.Flush(); + xtw.Flush(); } catch (Exception ex) { @@ -747,19 +813,18 @@ namespace SabreTools.Library.DatFiles /// /// Write out Game start using the supplied StreamWriter /// - /// StreamWriter to output to + /// XmlTextWriter to output to /// Split path representing last kwown parent game (SabreDAT only) /// Split path representing the parent game (SabreDAT only) /// Current depth to output file at (SabreDAT only) /// Last known depth to cycle back from (SabreDAT only) /// The new depth of the tag - private int WriteEndGame(StreamWriter sw, List splitpath, List newsplit, int depth, out int last) + private int WriteEndGame(XmlTextWriter xtw, List splitpath, List newsplit, int depth, out int last) { last = 0; try { - string state = ""; if (splitpath != null) { for (int i = 0; i < newsplit.Count && i < splitpath.Count; i++) @@ -769,28 +834,21 @@ namespace SabreTools.Library.DatFiles // If we find a difference, break if (newsplit[i] != splitpath[i]) - { break; - } } // Now that we have the last known position, take down all open folders for (int i = depth - 1; i > last + 1; i--) { - // Print out the number of tabs and the end folder - for (int j = 0; j < i; j++) - { - state += "\t"; - } - state += "\n"; + // End directory + xtw.WriteEndElement(); } // Reset the current depth depth = 2 + last; } - sw.Write(state); - sw.Flush(); + xtw.Flush(); } catch (Exception ex) { @@ -804,95 +862,139 @@ namespace SabreTools.Library.DatFiles /// /// Write out DatItem using the supplied StreamWriter /// - /// StreamWriter to output to - /// DatItem object to be output + /// XmlTextWriter to output to + /// DatItem object to be output /// Current depth to output file at (SabreDAT only) /// True if blank roms should be skipped on output, false otherwise (default) /// True if the data was written, false on error - private bool WriteDatItem(StreamWriter sw, DatItem rom, int depth, bool ignoreblanks = false) + private bool WriteDatItem(XmlTextWriter xtw, DatItem datItem, int depth, bool ignoreblanks = false) { // If we are in ignore blanks mode AND we have a blank (0-size) rom, skip - if (ignoreblanks - && (rom.ItemType == ItemType.Rom - && (((Rom)rom).Size == 0 || ((Rom)rom).Size == -1))) - { + if (ignoreblanks && (datItem.ItemType == ItemType.Rom && ((datItem as Rom).Size == 0 || (datItem as Rom).Size == -1))) return true; - } try { - string state = "", prefix = ""; - // Pre-process the item name - ProcessItemName(rom, true); + ProcessItemName(datItem, true); - for (int i = 0; i < depth; i++) - { - prefix += "\t"; - } - state += prefix; - - switch (rom.ItemType) + // Build the state based on excluded fields + switch (datItem.ItemType) { case ItemType.Archive: - state += "\n"; + xtw.WriteStartElement("file"); + xtw.WriteAttributeString("type", "archive"); + xtw.WriteAttributeString("name", datItem.GetField(Field.Name, ExcludeFields)); + xtw.WriteEndElement(); break; + case ItemType.BiosSet: - state += "\n"; + var biosSet = datItem as BiosSet; + xtw.WriteStartElement("file"); + xtw.WriteAttributeString("type", "biosset"); + xtw.WriteAttributeString("name", biosSet.GetField(Field.Name, ExcludeFields)); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.BiosDescription, ExcludeFields))) + xtw.WriteAttributeString("description", biosSet.Description); + if (!ExcludeFields[(int)Field.Default] && biosSet.Default != null) + xtw.WriteAttributeString("default", biosSet.Default.ToString().ToLowerInvariant()); + xtw.WriteEndElement(); break; + case ItemType.Disk: - state += "\n" + prefix + "\t\n" + - prefix + "\t\t\n" + - prefix + "\t\n" + - prefix + "\n" : "/>\n"); + var disk = datItem as Disk; + xtw.WriteStartElement("file"); + xtw.WriteAttributeString("type", "disk"); + xtw.WriteAttributeString("name", disk.GetField(Field.Name, ExcludeFields)); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields))) + xtw.WriteAttributeString("md5", disk.MD5.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, ExcludeFields))) + xtw.WriteAttributeString("ripemd160", disk.RIPEMD160.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields))) + xtw.WriteAttributeString("sha1", disk.SHA1.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, ExcludeFields))) + xtw.WriteAttributeString("sha256", disk.SHA256.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, ExcludeFields))) + xtw.WriteAttributeString("sha384", disk.SHA384.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, ExcludeFields))) + xtw.WriteAttributeString("sha512", disk.SHA512.ToLowerInvariant()); + if (!ExcludeFields[(int)Field.Status] && disk.ItemStatus != ItemStatus.None) + { + xtw.WriteStartElement("flags"); + + xtw.WriteStartElement("flag"); + xtw.WriteAttributeString("name", "status"); + xtw.WriteAttributeString("value", disk.ItemStatus.ToString().ToLowerInvariant()); + xtw.WriteEndElement(); + + // End flags + xtw.WriteEndElement(); + } + xtw.WriteEndElement(); break; + case ItemType.Release: - state += "\n"; + var release = datItem as Release; + xtw.WriteStartElement("file"); + xtw.WriteAttributeString("type", "release"); + xtw.WriteAttributeString("name", release.GetField(Field.Name, ExcludeFields)); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Region, ExcludeFields))) + xtw.WriteAttributeString("region", release.Region); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Language, ExcludeFields))) + xtw.WriteAttributeString("language", release.Language); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Date, ExcludeFields))) + xtw.WriteAttributeString("date", release.Date); + if (!ExcludeFields[(int)Field.Default] && release.Default != null) + xtw.WriteAttributeString("default", release.Default.ToString().ToLowerInvariant()); + xtw.WriteEndElement(); break; + case ItemType.Rom: - state += "\n" + prefix + "\t\n" + - prefix + "\t\t\n" + - prefix + "\t\n" + - prefix + "\n" : "/>\n"); + var rom = datItem as Rom; + xtw.WriteStartElement("file"); + xtw.WriteAttributeString("type", "rom"); + xtw.WriteAttributeString("name", rom.GetField(Field.Name, ExcludeFields)); + if (!ExcludeFields[(int)Field.Size] && rom.Size != -1) + xtw.WriteAttributeString("size", rom.Size.ToString()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CRC, ExcludeFields))) + xtw.WriteAttributeString("crc", rom.CRC.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields))) + xtw.WriteAttributeString("md5", rom.MD5.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, ExcludeFields))) + xtw.WriteAttributeString("ripemd160", rom.RIPEMD160.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields))) + xtw.WriteAttributeString("sha1", rom.SHA1.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, ExcludeFields))) + xtw.WriteAttributeString("sha256", rom.SHA256.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, ExcludeFields))) + xtw.WriteAttributeString("sha384", rom.SHA384.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, ExcludeFields))) + xtw.WriteAttributeString("sha512", rom.SHA512.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Date, ExcludeFields))) + xtw.WriteAttributeString("date", rom.Date); + if (!ExcludeFields[(int)Field.Status] && rom.ItemStatus != ItemStatus.None) + { + xtw.WriteStartElement("flags"); + + xtw.WriteStartElement("flag"); + xtw.WriteAttributeString("name", "status"); + xtw.WriteAttributeString("value", rom.ItemStatus.ToString().ToLowerInvariant()); + xtw.WriteEndElement(); + + // End flags + xtw.WriteEndElement(); + } + xtw.WriteEndElement(); break; + case ItemType.Sample: - state += "\n"; + xtw.WriteStartElement("file"); + xtw.WriteAttributeString("type", "sample"); + xtw.WriteAttributeString("name", datItem.GetField(Field.Name, ExcludeFields)); + xtw.WriteEndElement(); break; } - sw.Write(state); - sw.Flush(); + xtw.Flush(); } catch (Exception ex) { @@ -906,28 +1008,26 @@ namespace SabreTools.Library.DatFiles /// /// Write out DAT footer using the supplied StreamWriter /// - /// StreamWriter to output to + /// XmlTextWriter to output to /// Current depth to output file at (SabreDAT only) /// True if the data was written, false on error - private bool WriteFooter(StreamWriter sw, int depth) + private bool WriteFooter(XmlTextWriter xtw, int depth) { try { - string footer = ""; for (int i = depth - 1; i >= 2; i--) { - // Print out the number of tabs and the end folder - for (int j = 0; j < i; j++) - { - footer += "\t"; - } - footer += "\n"; + // End directory + xtw.WriteEndElement(); } - footer += "\t\n
\n"; - // Write the footer out - sw.Write(footer); - sw.Flush(); + // End data + xtw.WriteEndElement(); + + // End datafile + xtw.WriteEndElement(); + + xtw.Flush(); } catch (Exception ex) { diff --git a/SabreTools.Library/DatFiles/SeparatedValue.cs b/SabreTools.Library/DatFiles/SeparatedValue.cs index b305949d..471067ad 100644 --- a/SabreTools.Library/DatFiles/SeparatedValue.cs +++ b/SabreTools.Library/DatFiles/SeparatedValue.cs @@ -22,7 +22,7 @@ namespace SabreTools.Library.DatFiles internal class SeparatedValue : DatFile { // Private instance variables specific to Separated Value DATs - char _delim; + private readonly char _delim; /// /// Constructor designed for casting a base DatFile @@ -81,85 +81,103 @@ namespace SabreTools.Library.DatFiles case "file name": columns.Add("DatFile.FileName"); break; + case "internal name": columns.Add("DatFile.Name"); break; + case "description": case "dat description": columns.Add("DatFile.Description"); break; + case "game name": case "game": case "machine": columns.Add("Machine.Name"); break; + case "game description": columns.Add("Description"); break; + case "type": columns.Add("DatItem.Type"); break; + case "rom": case "romname": case "rom name": case "name": columns.Add("Rom.Name"); break; + case "disk": case "diskname": case "disk name": columns.Add("Disk.Name"); break; + case "size": columns.Add("DatItem.Size"); break; + case "crc": case "crc hash": columns.Add("DatItem.CRC"); break; + case "md5": case "md5 hash": columns.Add("DatItem.MD5"); break; + case "ripemd": case "ripemd160": case "ripemd hash": case "ripemd160 hash": columns.Add("DatItem.RIPEMD160"); break; + case "sha1": case "sha-1": case "sha1 hash": case "sha-1 hash": columns.Add("DatItem.SHA1"); break; + case "sha256": case "sha-256": case "sha256 hash": case "sha-256 hash": columns.Add("DatItem.SHA256"); break; + case "sha384": case "sha-384": case "sha384 hash": case "sha-384 hash": columns.Add("DatItem.SHA384"); break; + case "sha512": case "sha-512": case "sha512 hash": case "sha-512 hash": columns.Add("DatItem.SHA512"); break; + case "nodump": case "no dump": case "status": case "item status": columns.Add("DatItem.Nodump"); break; + case "date": columns.Add("DatItem.Date"); break; + default: columns.Add("INVALID"); break; @@ -175,7 +193,7 @@ namespace SabreTools.Library.DatFiles // If the line doesn't have the correct number of columns, we log and skip if (parsedLine.Length != columns.Count) { - Globals.Logger.Warning("Malformed line found in '{0}' at line {1}", filename, linenum); + Globals.Logger.Warning($"Malformed line found in '{filename}' at line {linenum}"); continue; } @@ -193,57 +211,72 @@ namespace SabreTools.Library.DatFiles switch (columns[i]) { case "DatFile.FileName": - FileName = (String.IsNullOrWhiteSpace(FileName) ? value : FileName); + FileName = (string.IsNullOrWhiteSpace(FileName) ? value : FileName); break; + case "DatFile.Name": - Name = (String.IsNullOrWhiteSpace(Name) ? value : Name); + Name = (string.IsNullOrWhiteSpace(Name) ? value : Name); break; + case "DatFile.Description": - Description = (String.IsNullOrWhiteSpace(Description) ? value : Description); + Description = (string.IsNullOrWhiteSpace(Description) ? value : Description); break; + case "Machine.Name": machineName = value; break; + case "Description": machineDesc = value; break; + case "DatItem.Type": itemType = Utilities.GetItemType(value) ?? ItemType.Rom; break; + case "Rom.Name": case "Disk.Name": - name = String.IsNullOrWhiteSpace(value) ? name : value; + name = string.IsNullOrWhiteSpace(value) ? name : value; break; + case "DatItem.Size": if (!Int64.TryParse(value, out size)) - { size = -1; - } + break; + case "DatItem.CRC": crc = Utilities.CleanHashData(value, Constants.CRCLength); break; + case "DatItem.MD5": md5 = Utilities.CleanHashData(value, Constants.MD5Length); break; + case "DatItem.RIPEMD160": ripemd160 = Utilities.CleanHashData(value, Constants.RIPEMD160Length); break; + case "DatItem.SHA1": sha1 = Utilities.CleanHashData(value, Constants.SHA1Length); break; + case "DatItem.SHA256": sha256 = Utilities.CleanHashData(value, Constants.SHA256Length); break; + case "DatItem.SHA384": sha384 = Utilities.CleanHashData(value, Constants.SHA384Length); break; + case "DatItem.SHA512": sha512 = Utilities.CleanHashData(value, Constants.SHA512Length); break; + case "DatItem.Nodump": status = Utilities.GetItemStatus(value); break; + case "DatItem.Date": date = value; break; @@ -264,6 +297,7 @@ namespace SabreTools.Library.DatFiles ParseAddHelper(archive, clean, remUnicode); break; + case ItemType.BiosSet: BiosSet biosset = new BiosSet() { @@ -275,6 +309,7 @@ namespace SabreTools.Library.DatFiles ParseAddHelper(biosset, clean, remUnicode); break; + case ItemType.Disk: Disk disk = new Disk() { @@ -294,6 +329,7 @@ namespace SabreTools.Library.DatFiles ParseAddHelper(disk, clean, remUnicode); break; + case ItemType.Release: Release release = new Release() { @@ -305,6 +341,7 @@ namespace SabreTools.Library.DatFiles ParseAddHelper(release, clean, remUnicode); break; + case ItemType.Rom: Rom rom = new Rom() { @@ -327,6 +364,7 @@ namespace SabreTools.Library.DatFiles ParseAddHelper(rom, clean, remUnicode); break; + case ItemType.Sample: Sample sample = new Sample() { @@ -352,13 +390,13 @@ namespace SabreTools.Library.DatFiles { try { - Globals.Logger.User("Opening file for writing: {0}", outfile); + Globals.Logger.User($"Opening file for writing: {outfile}"); FileStream fs = Utilities.TryCreate(outfile); // If we get back null for some reason, just log and return if (fs == null) { - Globals.Logger.Warning("File '{0}' could not be created for writing! Please check to see if the file is writable", outfile); + Globals.Logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable"); return false; } @@ -394,7 +432,7 @@ namespace SabreTools.Library.DatFiles && ((Rom)rom).Size == -1 && ((Rom)rom).CRC == "null") { - Globals.Logger.Verbose("Empty folder found: {0}", rom.MachineName); + Globals.Logger.Verbose($"Empty folder found: {rom.MachineName}"); } // Now, output the rom data @@ -444,91 +482,91 @@ namespace SabreTools.Library.DatFiles /// Write out DatItem using the supplied StreamWriter /// /// StreamWriter to output to - /// DatItem object to be output + /// DatItem object to be output /// True if blank roms should be skipped on output, false otherwise (default) /// True if the data was written, false on error - private bool WriteDatItem(StreamWriter sw, DatItem rom, bool ignoreblanks = false) + private bool WriteDatItem(StreamWriter sw, DatItem datItem, bool ignoreblanks = false) { // If we are in ignore blanks mode AND we have a blank (0-size) rom, skip - if (ignoreblanks - && (rom.ItemType == ItemType.Rom - && (((Rom)rom).Size == 0 || ((Rom)rom).Size == -1))) - { + if (ignoreblanks && (datItem.ItemType == ItemType.Rom && ((datItem as Rom).Size == 0 || (datItem as Rom).Size == -1))) return true; - } + // TODO: Clean up this mess and make it more like the other DatFile types + // TODO: Specifically, make it so that each ItemType has its own block, if possible try { // Initialize all strings - string state = "", - pre = "", - post = "", - type = "", - romname = "", - diskname = "", - size = "", - crc = "", - md5 = "", - ripemd160 = "", - sha1 = "", - sha256 = "", - sha384 = "", - sha512 = "", - status = ""; + string state = string.Empty, + pre = string.Empty, + post = string.Empty, + type = string.Empty, + romname = string.Empty, + diskname = string.Empty, + size = string.Empty, + crc = string.Empty, + md5 = string.Empty, + ripemd160 = string.Empty, + sha1 = string.Empty, + sha256 = string.Empty, + sha384 = string.Empty, + sha512 = string.Empty, + status = string.Empty; // Separated values should only output Rom and Disk - if (rom.ItemType != ItemType.Disk && rom.ItemType != ItemType.Rom) - { + if (datItem.ItemType != ItemType.Disk && datItem.ItemType != ItemType.Rom) return true; - } - if (rom.ItemType == ItemType.Rom) - { - type = "rom"; - romname = rom.Name; - size = ((Rom)rom).Size.ToString(); - crc = ((Rom)rom).CRC; - md5 = ((Rom)rom).MD5; - ripemd160 = ((Rom)rom).RIPEMD160; - sha1 = ((Rom)rom).SHA1; - sha256 = ((Rom)rom).SHA256; - sha384 = ((Rom)rom).SHA384; - sha512 = ((Rom)rom).SHA512; - status = (((Rom)rom).ItemStatus != ItemStatus.None ? "\"" + ((Rom)rom).ItemStatus.ToString() + "\"" : "\"\""); - } - else if (rom.ItemType == ItemType.Disk) + if (datItem.ItemType == ItemType.Disk) { + var disk = datItem as Disk; type = "disk"; - diskname = rom.Name; - md5 = ((Disk)rom).MD5; - ripemd160 = ((Disk)rom).RIPEMD160; - sha1 = ((Disk)rom).SHA1; - sha256 = ((Disk)rom).SHA256; - sha384 = ((Disk)rom).SHA384; - sha512 = ((Disk)rom).SHA512; - status = (((Disk)rom).ItemStatus != ItemStatus.None ? "\"" + ((Disk)rom).ItemStatus.ToString() + "\"" : "\"\""); + diskname = datItem.Name; + md5 = disk.MD5; + ripemd160 = disk.RIPEMD160; + sha1 = disk.SHA1; + sha256 = disk.SHA256; + sha384 = disk.SHA384; + sha512 = disk.SHA512; + status = (disk.ItemStatus != ItemStatus.None ? $"\"{disk.ItemStatus}\"" : "\"\""); + } + else if (datItem.ItemType == ItemType.Rom) + { + var rom = datItem as Rom; + type = "rom"; + romname = datItem.Name; + size = rom.Size.ToString(); + crc = rom.CRC; + md5 = rom.MD5; + ripemd160 = rom.RIPEMD160; + sha1 = rom.SHA1; + sha256 = rom.SHA256; + sha384 = rom.SHA384; + sha512 = rom.SHA512; + status = (rom.ItemStatus != ItemStatus.None ? $"\"{rom.ItemStatus}\"" : "\"\""); } - pre = CreatePrefixPostfix(rom, true); - post = CreatePrefixPostfix(rom, false); - string inline = string.Format("\"" + FileName + "\"" - + "{0}\"" + Name + "\"" - + "{0}\"" + Description + "\"" - + "{0}\"" + (!ExcludeFields[(int)Field.MachineName] ? rom.MachineName : "") + "\"" - + "{0}\"" + (!ExcludeFields[(int)Field.Description] ? rom.MachineDescription : "") + "\"" - + "{0}\"" + type + "\"" - + "{0}\"" + (!ExcludeFields[(int)Field.Name] ? romname : "") + "\"" - + "{0}\"" + (!ExcludeFields[(int)Field.Name] ? diskname : "") + "\"" - + "{0}\"" + (!ExcludeFields[(int)Field.Size] ? size : "") + "\"" - + "{0}\"" + (!ExcludeFields[(int)Field.CRC] ? crc : "") + "\"" - + "{0}\"" + (!ExcludeFields[(int)Field.MD5] ? md5 : "") + "\"" - // + "{0}\"" + (!ExcludeFields[(int)Field.RIPEMD160] ? ripemd160 : "") + "\"" - + "{0}\"" + (!ExcludeFields[(int)Field.SHA1] ? sha1 : "") + "\"" - + "{0}\"" + (!ExcludeFields[(int)Field.SHA256] ? sha256 : "") + "\"" - // + "{0}\"" + (!ExcludeFields[(int)Field.SHA384] ? sha384 : "") + "\"" - // + "{0}\"" + (!ExcludeFields[(int)Field.SHA512] ? sha512 : "") + "\"" - + "{0}" + status, _delim); - state += pre + inline + post + "\n"; + pre = CreatePrefixPostfix(datItem, true); + post = CreatePrefixPostfix(datItem, false); + string inline = string.Format($"\"{FileName}\"" + + $"{0}\"{Name}\"" + + $"{0}\"{Description}\"" + + $"{0}\"{(!ExcludeFields[(int)Field.MachineName] ? datItem.MachineName : string.Empty)}\"" + + $"{0}\"{(!ExcludeFields[(int)Field.Description] ? datItem.MachineDescription : string.Empty)}\"" + + $"{0}\"{type}\"" + + $"{0}\"{(!ExcludeFields[(int)Field.Name] ? romname : string.Empty)}\"" + + $"{0}\"{(!ExcludeFields[(int)Field.Name] ? diskname : string.Empty)}\"" + + $"{0}\"{(!ExcludeFields[(int)Field.Size] ? size : string.Empty)}\"" + + $"{0}\"{(!ExcludeFields[(int)Field.CRC] ? crc : string.Empty)}\"" + + $"{0}\"{(!ExcludeFields[(int)Field.MD5] ? md5 : string.Empty)}\"" + // + $"{0}\"{(!ExcludeFields[(int)Field.RIPEMD160] ? ripemd160 : string.Empty)}\"" + + $"{0}\"{(!ExcludeFields[(int)Field.SHA1] ? sha1 : string.Empty)}\"" + + $"{0}\"{(!ExcludeFields[(int)Field.SHA256] ? sha256 : string.Empty)}\"" + // + $"{0}\"{(!ExcludeFields[(int)Field.SHA384] ? sha384 : string.Empty)}\"" + // + $"{0}\"{(!ExcludeFields[(int)Field.SHA512] ? sha512 : string.Empty)}\"" + + $"{0}\"{(!ExcludeFields[(int)Field.Status] ? status : string.Empty)}\"", + _delim); + + state += $"{pre}{inline}{post}\n"; sw.Write(state); sw.Flush(); diff --git a/SabreTools.Library/DatFiles/SoftwareList.cs b/SabreTools.Library/DatFiles/SoftwareList.cs index 706f5e9f..f4ea6c49 100644 --- a/SabreTools.Library/DatFiles/SoftwareList.cs +++ b/SabreTools.Library/DatFiles/SoftwareList.cs @@ -1,20 +1,12 @@ using System; using System.Collections.Generic; -using System.Net; +using System.IO; using System.Text; using System.Xml; + using SabreTools.Library.Data; using SabreTools.Library.DatItems; using SabreTools.Library.Tools; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using FileStream = System.IO.FileStream; -using StreamWriter = System.IO.StreamWriter; -#endif using NaturalSort; namespace SabreTools.Library.DatFiles @@ -60,9 +52,7 @@ namespace SabreTools.Library.DatFiles // If we got a null reader, just return if (xtr == null) - { return; - } // Otherwise, read the file to the end try @@ -80,22 +70,20 @@ namespace SabreTools.Library.DatFiles switch (xtr.Name) { case "softwarelist": - Name = (String.IsNullOrWhiteSpace(Name) ? xtr.GetAttribute("name") ?? "" : Name); - Description = (String.IsNullOrWhiteSpace(Description) ? xtr.GetAttribute("description") ?? "" : Description); + Name = (string.IsNullOrWhiteSpace(Name) ? xtr.GetAttribute("name") ?? string.Empty : Name); + Description = (string.IsNullOrWhiteSpace(Description) ? xtr.GetAttribute("description") ?? string.Empty : Description); if (ForceMerging == ForceMerging.None) - { ForceMerging = Utilities.GetForceMerging(xtr.GetAttribute("forcemerging")); - } + if (ForceNodump == ForceNodump.None) - { ForceNodump = Utilities.GetForceNodump(xtr.GetAttribute("forcenodump")); - } + if (ForcePacking == ForcePacking.None) - { ForcePacking = Utilities.GetForcePacking(xtr.GetAttribute("forcepacking")); - } + xtr.Read(); break; + // We want to process the entire subtree of the machine case "software": ReadSoftware(xtr.ReadSubtree(), filename, sysid, srcid, keep, clean, remUnicode); @@ -103,6 +91,7 @@ namespace SabreTools.Library.DatFiles // Skip the software now that we've processed it xtr.Skip(); break; + default: xtr.Read(); break; @@ -111,7 +100,7 @@ namespace SabreTools.Library.DatFiles } catch (Exception ex) { - Globals.Logger.Warning("Exception found while parsing '{0}': {1}", filename, ex); + Globals.Logger.Warning($"Exception found while parsing '{filename}': {ex}"); // For XML errors, just skip the affected node xtr?.Read(); @@ -145,31 +134,25 @@ namespace SabreTools.Library.DatFiles { // If we have an empty software, skip it if (reader == null) - { return; - } // Otherwise, add what is possible reader.MoveToContent(); - string key = ""; + string key = string.Empty; string temptype = reader.Name; bool containsItems = false; // Create a new machine MachineType machineType = MachineType.NULL; if (Utilities.GetYesNo(reader.GetAttribute("isbios")) == true) - { machineType |= MachineType.Bios; - } + if (Utilities.GetYesNo(reader.GetAttribute("isdevice")) == true) - { machineType |= MachineType.Device; - } + if (Utilities.GetYesNo(reader.GetAttribute("ismechanical")) == true) - { machineType |= MachineType.Mechanical; - } Machine machine = new Machine { @@ -177,7 +160,7 @@ namespace SabreTools.Library.DatFiles Description = reader.GetAttribute("name"), Supported = Utilities.GetYesNo(reader.GetAttribute("supported")), // (yes|partial|no) "yes" - CloneOf = reader.GetAttribute("cloneof") ?? "", + CloneOf = reader.GetAttribute("cloneof") ?? string.Empty, Infos = new List>(), MachineType = (machineType == MachineType.NULL ? MachineType.None : machineType), @@ -198,29 +181,33 @@ namespace SabreTools.Library.DatFiles case "description": machine.Description = reader.ReadElementContentAsString(); break; + case "year": machine.Year = reader.ReadElementContentAsString(); break; + case "publisher": machine.Publisher = reader.ReadElementContentAsString(); break; + case "info": machine.Infos.Add(new Tuple(reader.GetAttribute("name"), reader.GetAttribute("value"))); - reader.Read(); break; + case "sharedfeat": // string sharedfeat_name = reader.GetAttribute("name"); // string sharedfeat_value = reader.GetAttribute("value"); - reader.Read(); break; + case "part": // Contains all rom and disk information containsItems = ReadPart(reader.ReadSubtree(), machine, filename, sysid, srcid, keep, clean, remUnicode); // Skip the part now that we've processed it reader.Skip(); break; + default: reader.Read(); break; @@ -268,7 +255,7 @@ namespace SabreTools.Library.DatFiles bool clean, bool remUnicode) { - string key = "", areaname = "", partname = "", partinterface = ""; + string key = string.Empty, areaname = string.Empty, partname = string.Empty, partinterface = string.Empty; string temptype = reader.Name; long? areasize = null; List> features = new List>(); @@ -281,13 +268,14 @@ namespace SabreTools.Library.DatFiles { if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "part") { - partname = ""; - partinterface = ""; + partname = string.Empty; + partinterface = string.Empty; features = new List>(); } + if (reader.NodeType == XmlNodeType.EndElement && (reader.Name == "dataarea" || reader.Name == "diskarea")) { - areaname = ""; + areaname = string.Empty; areasize = null; } @@ -301,23 +289,22 @@ namespace SabreTools.Library.DatFiles case "part": partname = reader.GetAttribute("name"); partinterface = reader.GetAttribute("interface"); - reader.Read(); break; + case "feature": features.Add(new Tuple(reader.GetAttribute("name"), reader.GetAttribute("feature"))); - reader.Read(); break; + case "dataarea": areaname = reader.GetAttribute("name"); if (reader.GetAttribute("size") != null) { if (Int64.TryParse(reader.GetAttribute("size"), out long tempas)) - { areasize = tempas; - } } + // string dataarea_width = reader.GetAttribute("width"); // (8|16|32|64) "8" // string dataarea_endianness = reader.GetAttribute("endianness"); // endianness (big|little) "little" @@ -327,6 +314,7 @@ namespace SabreTools.Library.DatFiles // Skip the dataarea now that we've processed it reader.Skip(); break; + case "diskarea": areaname = reader.GetAttribute("name"); @@ -336,6 +324,7 @@ namespace SabreTools.Library.DatFiles // Skip the diskarea now that we've processed it reader.Skip(); break; + case "dipswitch": // string dipswitch_name = reader.GetAttribute("name"); // string dipswitch_tag = reader.GetAttribute("tag"); @@ -348,6 +337,7 @@ namespace SabreTools.Library.DatFiles reader.Skip(); break; + default: reader.Read(); break; @@ -392,7 +382,7 @@ namespace SabreTools.Library.DatFiles bool clean, bool remUnicode) { - string key = ""; + string key = string.Empty; string temptype = reader.Name; bool containsItems = false; @@ -460,6 +450,7 @@ namespace SabreTools.Library.DatFiles reader.Read(); break; + default: reader.Read(); break; @@ -504,7 +495,7 @@ namespace SabreTools.Library.DatFiles bool clean, bool remUnicode) { - string key = ""; + string key = string.Empty; string temptype = reader.Name; bool containsItems = false; @@ -553,6 +544,7 @@ namespace SabreTools.Library.DatFiles reader.Read(); break; + default: reader.Read(); break; @@ -572,20 +564,21 @@ namespace SabreTools.Library.DatFiles { try { - Globals.Logger.User("Opening file for writing: {0}", outfile); + Globals.Logger.User($"Opening file for writing: {outfile}"); FileStream fs = Utilities.TryCreate(outfile); // If we get back null for some reason, just log and return if (fs == null) { - Globals.Logger.Warning("File '{0}' could not be created for writing! Please check to see if the file is writable", outfile); + Globals.Logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable"); return false; } - StreamWriter sw = new StreamWriter(fs, new UTF8Encoding(false)); + XmlTextWriter xtw = new XmlTextWriter(fs, new UTF8Encoding(false)); + xtw.Formatting = Formatting.Indented; // Write out the header - WriteHeader(sw); + WriteHeader(xtw); // Write out each of the machines and roms string lastgame = null; @@ -614,29 +607,25 @@ namespace SabreTools.Library.DatFiles // If we have a different game and we're not at the start of the list, output the end of last item if (lastgame != null && lastgame.ToLowerInvariant() != rom.MachineName.ToLowerInvariant()) - { - WriteEndGame(sw); - } + WriteEndGame(xtw); // If we have a new game, output the beginning of the new item if (lastgame == null || lastgame.ToLowerInvariant() != rom.MachineName.ToLowerInvariant()) - { - WriteStartGame(sw, rom); - } + WriteStartGame(xtw, rom); // If we have a "null" game (created by DATFromDir or something similar), log it to file if (rom.ItemType == ItemType.Rom && ((Rom)rom).Size == -1 && ((Rom)rom).CRC == "null") { - Globals.Logger.Verbose("Empty folder found: {0}", rom.MachineName); + Globals.Logger.Verbose($"Empty folder found: {rom.MachineName}"); lastgame = rom.MachineName; continue; } // Now, output the rom data - WriteDatItem(sw, rom, ignoreblanks); + WriteDatItem(xtw, rom, ignoreblanks); // Set the new data to compare against lastgame = rom.MachineName; @@ -644,10 +633,10 @@ namespace SabreTools.Library.DatFiles } // Write the file footer out - WriteFooter(sw); + WriteFooter(xtw); Globals.Logger.Verbose("File written!" + Environment.NewLine); - sw.Dispose(); + xtw.Dispose(); fs.Dispose(); } catch (Exception ex) @@ -662,30 +651,59 @@ namespace SabreTools.Library.DatFiles /// /// Write out DAT header using the supplied StreamWriter /// - /// StreamWriter to output to + /// XmlTextWriter to output to /// True if the data was written, false on error - private bool WriteHeader(StreamWriter sw) + private bool WriteHeader(XmlTextWriter xtw) { try { - string header = "\n" + - "\n\n" + - "\n\n"; + xtw.WriteStartDocument(); + xtw.WriteDocType("softwarelist", null, "softwarelist.dtd", null); - // Write the header out - sw.Write(header); - sw.Flush(); + xtw.WriteStartElement("softwarelist"); + xtw.WriteAttributeString("name", Name); + xtw.WriteAttributeString("description", Description); + + switch (ForcePacking) + { + case ForcePacking.Unzip: + xtw.WriteAttributeString("forcepacking", "unzip"); + break; + case ForcePacking.Zip: + xtw.WriteAttributeString("forcepacking", "zip"); + break; + } + + switch (ForceMerging) + { + case ForceMerging.Full: + xtw.WriteAttributeString("forcemerging", "full"); + break; + case ForceMerging.Split: + xtw.WriteAttributeString("forcemerging", "split"); + break; + case ForceMerging.Merged: + xtw.WriteAttributeString("forcemerging", "merged"); + break; + case ForceMerging.NonMerged: + xtw.WriteAttributeString("forcemerging", "nonmerged"); + break; + } + + switch (ForceNodump) + { + case ForceNodump.Ignore: + xtw.WriteAttributeString("forcenodump", "ignore"); + break; + case ForceNodump.Obsolete: + xtw.WriteAttributeString("forcenodump", "obsolete"); + break; + case ForceNodump.Required: + xtw.WriteAttributeString("forcenodump", "required"); + break; + } + + xtw.Flush(); } catch (Exception ex) { @@ -699,37 +717,52 @@ namespace SabreTools.Library.DatFiles /// /// Write out Game start using the supplied StreamWriter /// - /// StreamWriter to output to - /// DatItem object to be output + /// XmlTextWriter to output to + /// DatItem object to be output /// True if the data was written, false on error - private bool WriteStartGame(StreamWriter sw, DatItem rom) + private bool WriteStartGame(XmlTextWriter xtw, DatItem datItem) { try { // No game should start with a path separator - if (rom.MachineName.StartsWith(Path.DirectorySeparatorChar.ToString())) + datItem.MachineName = datItem.MachineName.TrimStart(Path.DirectorySeparatorChar); + + // Build the state based on excluded fields + xtw.WriteStartElement("software"); + xtw.WriteAttributeString("name", datItem.GetField(Field.MachineName, ExcludeFields)); + + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CloneOf, ExcludeFields)) && !string.Equals(datItem.MachineName, datItem.CloneOf, StringComparison.OrdinalIgnoreCase)) + xtw.WriteAttributeString("cloneof", datItem.CloneOf); + + if (!ExcludeFields[(int)Field.Supported]) { - rom.MachineName = rom.MachineName.Substring(1); + if (datItem.Supported == true) + xtw.WriteAttributeString("supported", "yes"); + else if (datItem.Supported == false) + xtw.WriteAttributeString("supported", "no"); + else + xtw.WriteAttributeString("supported", "partial"); } - string state = "\t\n" : "") - + (!ExcludeFields[(int)Field.Description] ? "\t\t" + WebUtility.HtmlEncode(rom.MachineDescription) + "\n" : "") - + (!ExcludeFields[(int)Field.Year] && rom.Year != null ? "\t\t" + WebUtility.HtmlEncode(rom.Year) + "\n" : "") - + (!ExcludeFields[(int)Field.Publisher] && rom.Publisher != null ? "\t\t" + WebUtility.HtmlEncode(rom.Publisher) + "\n" : ""); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Description, ExcludeFields))) + xtw.WriteElementString("description", datItem.MachineDescription); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Year, ExcludeFields))) + xtw.WriteElementString("year", datItem.Year); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Publisher, ExcludeFields))) + xtw.WriteElementString("publisher", datItem.Publisher); - if (!ExcludeFields[(int)Field.Infos]) + if (!ExcludeFields[(int)Field.Infos] && datItem.Infos != null && datItem.Infos.Count > 0) { - foreach (Tuple kvp in rom.Infos) + foreach (Tuple kvp in datItem.Infos) { - state += "\t\t\n"; + xtw.WriteStartElement("info"); + xtw.WriteAttributeString("name", kvp.Item1); + xtw.WriteAttributeString("value", kvp.Item2); + xtw.WriteEndElement(); } } - sw.Write(state); - sw.Flush(); + xtw.Flush(); } catch (Exception ex) { @@ -743,16 +776,16 @@ namespace SabreTools.Library.DatFiles /// /// Write out Game start using the supplied StreamWriter /// - /// StreamWriter to output to + /// XmlTextWriter to output to /// True if the data was written, false on error - private bool WriteEndGame(StreamWriter sw) + private bool WriteEndGame(XmlTextWriter xtw) { try { - string state = "\t\n\n"; + // End software + xtw.WriteEndElement(); - sw.Write(state); - sw.Flush(); + xtw.Flush(); } catch (Exception ex) { @@ -766,80 +799,121 @@ namespace SabreTools.Library.DatFiles /// /// Write out DatItem using the supplied StreamWriter /// - /// StreamWriter to output to - /// DatItem object to be output + /// XmlTextWriter to output to + /// DatItem object to be output /// True if blank roms should be skipped on output, false otherwise (default) /// True if the data was written, false on error - private bool WriteDatItem(StreamWriter sw, DatItem rom, bool ignoreblanks = false) + private bool WriteDatItem(XmlTextWriter xtw, DatItem datItem, bool ignoreblanks = false) { // If we are in ignore blanks mode AND we have a blank (0-size) rom, skip - if (ignoreblanks - && (rom.ItemType == ItemType.Rom - && (((Rom)rom).Size == 0 || ((Rom)rom).Size == -1))) - { + if (ignoreblanks && (datItem.ItemType == ItemType.Rom && ((datItem as Rom).Size == 0 || (datItem as Rom).Size == -1))) return true; - } try { - string state = ""; - // Pre-process the item name - ProcessItemName(rom, true); + ProcessItemName(datItem, true); - state += "\t\t\n"; + // Build the state based on excluded fields + xtw.WriteStartElement("part"); + xtw.WriteAttributeString("name", datItem.GetField(Field.PartName, ExcludeFields)); + xtw.WriteAttributeString("interface", datItem.GetField(Field.PartInterface, ExcludeFields)); - if (!ExcludeFields[(int)Field.Features]) + if (!ExcludeFields[(int)Field.Features] && datItem.Features != null && datItem.Features.Count > 0) { - foreach (Tuple kvp in rom.Features) + foreach (Tuple kvp in datItem.Features) { - state += "\t\t\t\n"; + xtw.WriteStartElement("feature"); + xtw.WriteAttributeString("name", kvp.Item1); + xtw.WriteAttributeString("value", kvp.Item2); + xtw.WriteEndElement(); } } - switch (rom.ItemType) + string areaName = datItem.GetField(Field.AreaName, ExcludeFields); + switch (datItem.ItemType) { case ItemType.Disk: - state += "\t\t\t\n" - + "\t\t\t\t\n" - + "\t\t\t\n"; + var disk = datItem as Disk; + if (!ExcludeFields[(int)Field.AreaName] && string.IsNullOrWhiteSpace(areaName)) + areaName = "cdrom"; + + xtw.WriteStartElement("diskarea"); + xtw.WriteAttributeString("name", areaName); + if (!ExcludeFields[(int)Field.AreaSize] && disk.AreaSize != null) + xtw.WriteAttributeString("size", disk.AreaSize.ToString()); + + xtw.WriteStartElement("disk"); + xtw.WriteAttributeString("name", disk.GetField(Field.Name, ExcludeFields)); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields))) + xtw.WriteAttributeString("md5", disk.MD5.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, ExcludeFields))) + xtw.WriteAttributeString("ripemd160", disk.RIPEMD160.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields))) + xtw.WriteAttributeString("sha1", disk.SHA1.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, ExcludeFields))) + xtw.WriteAttributeString("sha256", disk.SHA256.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, ExcludeFields))) + xtw.WriteAttributeString("sha384", disk.SHA384.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, ExcludeFields))) + xtw.WriteAttributeString("sha512", disk.SHA512.ToLowerInvariant()); + if (!ExcludeFields[(int)Field.Status] && disk.ItemStatus != ItemStatus.None) + xtw.WriteAttributeString("status", disk.ItemStatus.ToString().ToLowerInvariant()); + if (!ExcludeFields[(int)Field.Writable] && disk.Writable != null) + xtw.WriteAttributeString("writable", disk.Writable == true ? "yes" : "no"); + xtw.WriteEndElement(); + + // End diskarea + xtw.WriteEndElement(); break; + case ItemType.Rom: - state += "\t\t\t\n" - + "\t\t\t\t\n" - + "\t\t\t\n"; + var rom = datItem as Rom; + if (!ExcludeFields[(int)Field.AreaName] && string.IsNullOrWhiteSpace(areaName)) + areaName = "rom"; + + xtw.WriteStartElement("dataarea"); + xtw.WriteAttributeString("name", areaName); + if (!ExcludeFields[(int)Field.AreaSize] && rom.AreaSize != null) + xtw.WriteAttributeString("size", rom.AreaSize.ToString()); + + xtw.WriteStartElement("rom"); + xtw.WriteAttributeString("name", rom.GetField(Field.Name, ExcludeFields)); + if (!ExcludeFields[(int)Field.Size] && rom.Size != -1) + xtw.WriteAttributeString("size", rom.Size.ToString()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.CRC, ExcludeFields))) + xtw.WriteAttributeString("crc", rom.CRC.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.MD5, ExcludeFields))) + xtw.WriteAttributeString("md5", rom.MD5.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.RIPEMD160, ExcludeFields))) + xtw.WriteAttributeString("ripemd160", rom.RIPEMD160.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA1, ExcludeFields))) + xtw.WriteAttributeString("sha1", rom.SHA1.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA256, ExcludeFields))) + xtw.WriteAttributeString("sha256", rom.SHA256.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA384, ExcludeFields))) + xtw.WriteAttributeString("sha384", rom.SHA384.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.SHA512, ExcludeFields))) + xtw.WriteAttributeString("sha512", rom.SHA512.ToLowerInvariant()); + if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Offset, ExcludeFields))) + xtw.WriteAttributeString("offset", rom.Offset); + //if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Value, ExcludeFields))) + // xtw.WriteAttributeString("value", rom.Value); + if (!ExcludeFields[(int)Field.Status] && rom.ItemStatus != ItemStatus.None) + xtw.WriteAttributeString("status", rom.ItemStatus.ToString().ToLowerInvariant()); + //if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Loadflag, ExcludeFields))) + // xtw.WriteAttributeString("loadflag", rom.Loadflag); + xtw.WriteEndElement(); + + // End dataarea + xtw.WriteEndElement(); break; } - state += "\t\t\n"; + // End part + xtw.WriteEndElement(); - sw.Write(state); - sw.Flush(); + xtw.Flush(); } catch (Exception ex) { @@ -853,17 +927,19 @@ namespace SabreTools.Library.DatFiles /// /// Write out DAT footer using the supplied StreamWriter /// - /// StreamWriter to output to + /// XmlTextWriter to output to /// True if the data was written, false on error - private bool WriteFooter(StreamWriter sw) + private bool WriteFooter(XmlTextWriter xtw) { try { - string footer = "\t\n\n\n"; + // End software + xtw.WriteEndElement(); - // Write the footer out - sw.Write(footer); - sw.Flush(); + // End softwarelist + xtw.WriteEndElement(); + + xtw.Flush(); } catch (Exception ex) { diff --git a/SabreTools.Library/DatItems/Archive.cs b/SabreTools.Library/DatItems/Archive.cs index 282fac00..44087abf 100644 --- a/SabreTools.Library/DatItems/Archive.cs +++ b/SabreTools.Library/DatItems/Archive.cs @@ -14,7 +14,7 @@ namespace SabreTools.Library.DatItems /// public Archive() { - this.Name = ""; + this.Name = string.Empty; this.ItemType = ItemType.Archive; } @@ -69,12 +69,10 @@ namespace SabreTools.Library.DatItems { // If we don't have an archive, return false if (this.ItemType != other.ItemType) - { return false; - } // Otherwise, treat it as an archive - Archive newOther = (Archive)other; + Archive newOther = other as Archive; // If the archive information matches return (this.Name == newOther.Name); diff --git a/SabreTools.Library/DatItems/BiosSet.cs b/SabreTools.Library/DatItems/BiosSet.cs index 0d1bf0a8..2a102c03 100644 --- a/SabreTools.Library/DatItems/BiosSet.cs +++ b/SabreTools.Library/DatItems/BiosSet.cs @@ -28,7 +28,7 @@ namespace SabreTools.Library.DatItems /// public BiosSet() { - this.Name = ""; + this.Name = string.Empty; this.ItemType = ItemType.BiosSet; } @@ -86,12 +86,10 @@ namespace SabreTools.Library.DatItems { // If we don't have a biosset, return false if (this.ItemType != other.ItemType) - { return false; - } // Otherwise, treat it as a biosset - BiosSet newOther = (BiosSet)other; + BiosSet newOther = other as BiosSet; // If the archive information matches return (this.Name == newOther.Name && this.Description == newOther.Description && this.Default == newOther.Default); diff --git a/SabreTools.Library/DatItems/Blank.cs b/SabreTools.Library/DatItems/Blank.cs index f36eff4d..b9ebb566 100644 --- a/SabreTools.Library/DatItems/Blank.cs +++ b/SabreTools.Library/DatItems/Blank.cs @@ -14,7 +14,7 @@ namespace SabreTools.Library.DatItems /// public Blank() { - this.Name = ""; + this.Name = string.Empty; this.ItemType = ItemType.Blank; } @@ -69,12 +69,10 @@ namespace SabreTools.Library.DatItems { // If we don't have a blank, return false if (this.ItemType != other.ItemType) - { return false; - } - // Otherwise, treat it as a - Blank newOther = (Blank)other; + // Otherwise, treat it as a Blank + Blank newOther = other as Blank; // If the archive information matches return (_machine == newOther._machine); diff --git a/SabreTools.Library/DatItems/DatItem.cs b/SabreTools.Library/DatItems/DatItem.cs index 7c968ed3..ca43625c 100644 --- a/SabreTools.Library/DatItems/DatItem.cs +++ b/SabreTools.Library/DatItems/DatItem.cs @@ -1,15 +1,13 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Linq; + using SabreTools.Library.Data; using SabreTools.Library.DatFiles; using SabreTools.Library.Tools; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; -#endif using NaturalSort; +using System.Runtime.CompilerServices; namespace SabreTools.Library.DatItems { @@ -564,6 +562,189 @@ namespace SabreTools.Library.DatItems #region Instance Methods + #region Accessors + + /// + /// Get the value of that field as a string, if possible + /// + public string GetField(Field field, bool[] excludeFields) + { + // If the field is to be excluded, return empty string + if (excludeFields[(int)field]) + return string.Empty; + + switch (field) + { + case Field.Name: + return this.Name; + case Field.PartName: + return this.PartName; + case Field.PartInterface: + return this.PartInterface; + case Field.Features: + return string.Join(", ", this.Features.Select(f => $"{f.Item1}={f.Item2}")); + case Field.AreaName: + return this.AreaName; + case Field.AreaSize: + return this.AreaSize?.ToString() ?? string.Empty; + + case Field.MachineName: + return this.MachineName; + case Field.Comment: + return this.Comment; + case Field.Description: + return this.MachineDescription; + case Field.Year: + return this.Year; + case Field.Manufacturer: + return this.Manufacturer; + case Field.Publisher: + return this.Publisher; + case Field.RomOf: + return this.RomOf; + case Field.CloneOf: + return this.CloneOf; + case Field.SampleOf: + return this.SampleOf; + case Field.Supported: + return this.Supported?.ToString() ?? string.Empty; + case Field.SourceFile: + return this.SourceFile; + case Field.Runnable: + return this.Runnable?.ToString() ?? string.Empty; + case Field.Board: + return this.Board; + case Field.RebuildTo: + return this.RebuildTo; + case Field.Devices: + return string.Join(", ", this.Devices); + case Field.SlotOptions: + return string.Join(", ", this.SlotOptions); + case Field.Infos: + return string.Join(", ", this.Infos.Select(i => $"{i.Item1}={i.Item2}")); + case Field.MachineType: + return this.MachineType.ToString(); + + case Field.Default: + if (ItemType == ItemType.BiosSet) + return (this as BiosSet).Default?.ToString() ?? string.Empty; + else if (ItemType == ItemType.Release) + return (this as Release).Default?.ToString() ?? string.Empty; + break; + case Field.BiosDescription: + if (ItemType == ItemType.BiosSet) + return (this as BiosSet).Description; + break; + + case Field.MD5: + if (ItemType == ItemType.Disk) + return (this as Disk).MD5; + else if (ItemType == ItemType.Rom) + return (this as Rom).MD5; + break; + case Field.RIPEMD160: + if (ItemType == ItemType.Disk) + return (this as Disk).RIPEMD160; + else if (ItemType == ItemType.Rom) + return (this as Rom).RIPEMD160; + break; + case Field.SHA1: + if (ItemType == ItemType.Disk) + return (this as Disk).SHA1; + else if (ItemType == ItemType.Rom) + return (this as Rom).SHA1; + break; + case Field.SHA256: + if (ItemType == ItemType.Disk) + return (this as Disk).SHA256; + else if (ItemType == ItemType.Rom) + return (this as Rom).SHA256; + break; + case Field.SHA384: + if (ItemType == ItemType.Disk) + return (this as Disk).SHA384; + else if (ItemType == ItemType.Rom) + return (this as Rom).SHA384; + break; + case Field.SHA512: + if (ItemType == ItemType.Disk) + return (this as Disk).SHA512; + else if (ItemType == ItemType.Rom) + return (this as Rom).SHA512; + break; + case Field.Merge: + if (ItemType == ItemType.Disk) + return (this as Disk).MergeTag; + else if (ItemType == ItemType.Rom) + return (this as Rom).MergeTag; + break; + case Field.Region: + if (ItemType == ItemType.Disk) + return (this as Disk).Region; + else if (ItemType == ItemType.Release) + return (this as Release).Region; + else if (ItemType == ItemType.Rom) + return (this as Rom).Region; + break; + case Field.Index: + if (ItemType == ItemType.Disk) + return (this as Disk).Index; + break; + case Field.Writable: + if (ItemType == ItemType.Disk) + return (this as Disk).Writable?.ToString() ?? string.Empty; + break; + case Field.Optional: + if (ItemType == ItemType.Disk) + return (this as Disk).Optional?.ToString() ?? string.Empty; + else if (ItemType == ItemType.Rom) + return (this as Rom).Optional?.ToString() ?? string.Empty; + break; + case Field.Status: + if (ItemType == ItemType.Disk) + return (this as Disk).ItemStatus.ToString(); + else if (ItemType == ItemType.Rom) + return (this as Rom).ItemStatus.ToString(); + break; + + case Field.Language: + if (ItemType == ItemType.Release) + return (this as Release).Language; + break; + case Field.Date: + if (ItemType == ItemType.Release) + return (this as Release).Date; + else if (ItemType == ItemType.Rom) + return (this as Rom).Date; + break; + + case Field.Bios: + if (ItemType == ItemType.Rom) + return (this as Rom).Bios; + break; + case Field.Size: + if (ItemType == ItemType.Rom) + return (this as Rom).Size.ToString(); + break; + case Field.CRC: + if (ItemType == ItemType.Rom) + return (this as Rom).CRC; + break; + case Field.Offset: + if (ItemType == ItemType.Rom) + return (this as Rom).Offset; + break; + + case Field.NULL: + default: + return string.Empty; + } + + return string.Empty; + } + + #endregion + #region Cloning Methods /// @@ -601,9 +782,8 @@ namespace SabreTools.Library.DatItems try { if (this.Name == other.Name) - { ret = (this.Equals(other) ? 0 : 1); - } + ret = String.Compare(this.Name, other.Name); } catch @@ -632,34 +812,24 @@ namespace SabreTools.Library.DatItems // If we don't have a duplicate at all, return none if (!this.Equals(lastItem)) - { return output; - } // If the duplicate is external already or should be, set it if ((lastItem.DupeType & DupeType.External) != 0 || lastItem.SystemID != this.SystemID || lastItem.SourceID != this.SourceID) { if (lastItem.MachineName == this.MachineName && lastItem.Name == this.Name) - { output = DupeType.External | DupeType.All; - } else - { output = DupeType.External | DupeType.Hash; - } } // Otherwise, it's considered an internal dupe else { if (lastItem.MachineName == this.MachineName && lastItem.Name == this.Name) - { output = DupeType.Internal | DupeType.All; - } else - { output = DupeType.Internal | DupeType.Hash; - } } return output; @@ -679,31 +849,18 @@ namespace SabreTools.Library.DatItems { // Check for an empty rom list first if (datdata.Count == 0) - { return false; - } // We want to get the proper key for the DatItem string key = SortAndGetKey(datdata, sorted); // If the key doesn't exist, return the empty list if (!datdata.Contains(key)) - { return false; - } // Try to find duplicates List roms = datdata[key]; - - foreach (DatItem rom in roms) - { - if (this.Equals(rom)) - { - return true; - } - } - - return false; + return roms.Any(r => this.Equals(r)); } /// @@ -767,9 +924,7 @@ namespace SabreTools.Library.DatItems { // If we're not already sorted, take care of it if (!sorted) - { datdata.BucketByBestAvailable(); - } // Now that we have the sorted type, we get the proper key return Utilities.GetKeyFromDatItem(this, datdata.SortedBy); @@ -792,9 +947,7 @@ namespace SabreTools.Library.DatItems { // Check for null or blank roms first if (infiles == null || infiles.Count == 0) - { return new List(); - } // Create output list List outfiles = new List(); @@ -805,9 +958,7 @@ namespace SabreTools.Library.DatItems { // If we don't have a Rom or a Disk, we skip checking for duplicates if (file.ItemType != ItemType.Rom && file.ItemType != ItemType.Disk) - { continue; - } // If it's a nodump, add and skip if (file.ItemType == ItemType.Rom && ((Rom)file).ItemStatus == ItemStatus.Nodump) @@ -852,46 +1003,46 @@ namespace SabreTools.Library.DatItems ((Rom)saveditem).Size = (((Rom)saveditem).Size == -1 && ((Rom)file).Size != -1 ? ((Rom)file).Size : ((Rom)saveditem).Size); - ((Rom)saveditem).CRC = (String.IsNullOrWhiteSpace(((Rom)saveditem).CRC) && !String.IsNullOrWhiteSpace(((Rom)file).CRC) + ((Rom)saveditem).CRC = (string.IsNullOrWhiteSpace(((Rom)saveditem).CRC) && !string.IsNullOrWhiteSpace(((Rom)file).CRC) ? ((Rom)file).CRC : ((Rom)saveditem).CRC); - ((Rom)saveditem).MD5 = (String.IsNullOrWhiteSpace(((Rom)saveditem).MD5) && !String.IsNullOrWhiteSpace(((Rom)file).MD5) + ((Rom)saveditem).MD5 = (string.IsNullOrWhiteSpace(((Rom)saveditem).MD5) && !string.IsNullOrWhiteSpace(((Rom)file).MD5) ? ((Rom)file).MD5 : ((Rom)saveditem).MD5); - ((Rom)saveditem).RIPEMD160 = (String.IsNullOrWhiteSpace(((Rom)saveditem).RIPEMD160) && !String.IsNullOrWhiteSpace(((Rom)file).RIPEMD160) + ((Rom)saveditem).RIPEMD160 = (string.IsNullOrWhiteSpace(((Rom)saveditem).RIPEMD160) && !string.IsNullOrWhiteSpace(((Rom)file).RIPEMD160) ? ((Rom)file).RIPEMD160 : ((Rom)saveditem).RIPEMD160); - ((Rom)saveditem).SHA1 = (String.IsNullOrWhiteSpace(((Rom)saveditem).SHA1) && !String.IsNullOrWhiteSpace(((Rom)file).SHA1) + ((Rom)saveditem).SHA1 = (string.IsNullOrWhiteSpace(((Rom)saveditem).SHA1) && !string.IsNullOrWhiteSpace(((Rom)file).SHA1) ? ((Rom)file).SHA1 : ((Rom)saveditem).SHA1); - ((Rom)saveditem).SHA256 = (String.IsNullOrWhiteSpace(((Rom)saveditem).SHA256) && !String.IsNullOrWhiteSpace(((Rom)file).SHA256) + ((Rom)saveditem).SHA256 = (string.IsNullOrWhiteSpace(((Rom)saveditem).SHA256) && !string.IsNullOrWhiteSpace(((Rom)file).SHA256) ? ((Rom)file).SHA256 : ((Rom)saveditem).SHA256); - ((Rom)saveditem).SHA384 = (String.IsNullOrWhiteSpace(((Rom)saveditem).SHA384) && !String.IsNullOrWhiteSpace(((Rom)file).SHA384) + ((Rom)saveditem).SHA384 = (string.IsNullOrWhiteSpace(((Rom)saveditem).SHA384) && !string.IsNullOrWhiteSpace(((Rom)file).SHA384) ? ((Rom)file).SHA384 : ((Rom)saveditem).SHA384); - ((Rom)saveditem).SHA512 = (String.IsNullOrWhiteSpace(((Rom)saveditem).SHA512) && !String.IsNullOrWhiteSpace(((Rom)file).SHA512) + ((Rom)saveditem).SHA512 = (string.IsNullOrWhiteSpace(((Rom)saveditem).SHA512) && !string.IsNullOrWhiteSpace(((Rom)file).SHA512) ? ((Rom)file).SHA512 : ((Rom)saveditem).SHA512); } else if (file.ItemType == ItemType.Disk) { - ((Disk)saveditem).MD5 = (String.IsNullOrWhiteSpace(((Disk)saveditem).MD5) && !String.IsNullOrWhiteSpace(((Disk)file).MD5) + ((Disk)saveditem).MD5 = (string.IsNullOrWhiteSpace(((Disk)saveditem).MD5) && !string.IsNullOrWhiteSpace(((Disk)file).MD5) ? ((Disk)file).MD5 : ((Disk)saveditem).MD5); - ((Disk)saveditem).RIPEMD160 = (String.IsNullOrWhiteSpace(((Disk)saveditem).RIPEMD160) && !String.IsNullOrWhiteSpace(((Disk)file).RIPEMD160) + ((Disk)saveditem).RIPEMD160 = (string.IsNullOrWhiteSpace(((Disk)saveditem).RIPEMD160) && !string.IsNullOrWhiteSpace(((Disk)file).RIPEMD160) ? ((Disk)file).RIPEMD160 : ((Disk)saveditem).RIPEMD160); - ((Disk)saveditem).SHA1 = (String.IsNullOrWhiteSpace(((Disk)saveditem).SHA1) && !String.IsNullOrWhiteSpace(((Disk)file).SHA1) + ((Disk)saveditem).SHA1 = (string.IsNullOrWhiteSpace(((Disk)saveditem).SHA1) && !string.IsNullOrWhiteSpace(((Disk)file).SHA1) ? ((Disk)file).SHA1 : ((Disk)saveditem).SHA1); - ((Disk)saveditem).SHA256 = (String.IsNullOrWhiteSpace(((Disk)saveditem).SHA256) && !String.IsNullOrWhiteSpace(((Disk)file).SHA256) + ((Disk)saveditem).SHA256 = (string.IsNullOrWhiteSpace(((Disk)saveditem).SHA256) && !string.IsNullOrWhiteSpace(((Disk)file).SHA256) ? ((Disk)file).SHA256 : ((Disk)saveditem).SHA256); - ((Disk)saveditem).SHA384 = (String.IsNullOrWhiteSpace(((Disk)saveditem).SHA384) && !String.IsNullOrWhiteSpace(((Disk)file).SHA384) + ((Disk)saveditem).SHA384 = (string.IsNullOrWhiteSpace(((Disk)saveditem).SHA384) && !string.IsNullOrWhiteSpace(((Disk)file).SHA384) ? ((Disk)file).SHA384 : ((Disk)saveditem).SHA384); - ((Disk)saveditem).SHA512 = (String.IsNullOrWhiteSpace(((Disk)saveditem).SHA512) && !String.IsNullOrWhiteSpace(((Disk)file).SHA512) + ((Disk)saveditem).SHA512 = (string.IsNullOrWhiteSpace(((Disk)saveditem).SHA512) && !string.IsNullOrWhiteSpace(((Disk)file).SHA512) ? ((Disk)file).SHA512 : ((Disk)saveditem).SHA512); } @@ -976,37 +1127,18 @@ namespace SabreTools.Library.DatItems // If the current item exactly matches the last item, then we don't add it if ((datItem.GetDuplicateStatus(lastItem) & DupeType.All) != 0) { - Globals.Logger.Verbose("Exact duplicate found for '{0}'", datItem.Name); + Globals.Logger.Verbose($"Exact duplicate found for '{datItem.Name}'"); continue; } // If the current name matches the previous name, rename the current item else if (datItem.Name == lastItem.Name) { - Globals.Logger.Verbose("Name duplicate found for '{0}'", datItem.Name); + Globals.Logger.Verbose($"Name duplicate found for '{datItem.Name}'"); - if (datItem.ItemType == ItemType.Disk) + if (datItem.ItemType == ItemType.Disk || datItem.ItemType == ItemType.Rom) { - Disk disk = (Disk)datItem; - disk.Name += "_" + (!String.IsNullOrWhiteSpace(disk.MD5) - ? disk.MD5 - : !String.IsNullOrWhiteSpace(disk.SHA1) - ? disk.SHA1 - : "1"); - datItem = disk; - lastrenamed = lastrenamed ?? datItem.Name; - } - else if (datItem.ItemType == ItemType.Rom) - { - Rom rom = (Rom)datItem; - rom.Name += "_" + (!String.IsNullOrWhiteSpace(rom.CRC) - ? rom.CRC - : !String.IsNullOrWhiteSpace(rom.MD5) - ? rom.MD5 - : !String.IsNullOrWhiteSpace(rom.SHA1) - ? rom.SHA1 - : "1"); - datItem = rom; + datItem.Name += GetDuplicateSuffix(datItem); lastrenamed = lastrenamed ?? datItem.Name; } @@ -1014,7 +1146,7 @@ namespace SabreTools.Library.DatItems if (datItem.Name == lastrenamed) { lastrenamed = datItem.Name; - datItem.Name += (lastid == 0 ? "" : "_" + lastid); + datItem.Name += (lastid == 0 ? string.Empty : "_" + lastid); lastid++; } // If we have no conflict, then we want to reset the lastrenamed and id @@ -1043,6 +1175,39 @@ namespace SabreTools.Library.DatItems return output; } + /// + /// Get duplicate suffix based on the item type + /// + private static string GetDuplicateSuffix(DatItem datItem) + { + if (datItem.ItemType == ItemType.Disk) + { + Disk disk = datItem as Disk; + + if (string.IsNullOrWhiteSpace(disk.MD5)) + return $"_{disk.MD5}"; + else if (string.IsNullOrWhiteSpace(disk.SHA1)) + return $"_{disk.SHA1}"; + else + return "_1"; + } + else if (datItem.ItemType == ItemType.Rom) + { + Rom rom = datItem as Rom; + + if (string.IsNullOrWhiteSpace(rom.CRC)) + return $"_{rom.CRC}"; + else if (string.IsNullOrWhiteSpace(rom.MD5)) + return $"_{rom.MD5}"; + else if (string.IsNullOrWhiteSpace(rom.SHA1)) + return $"_{rom.SHA1}"; + else + return "_1"; + } + + return "_1"; + } + /// /// Sort a list of File objects by SystemID, SourceID, Game, and Name (in order) /// @@ -1068,6 +1233,7 @@ namespace SabreTools.Library.DatItems { return nc.Compare(Path.GetFileName(Utilities.RemovePathUnsafeCharacters(x.Name)), Path.GetFileName(Utilities.RemovePathUnsafeCharacters(y.Name))); } + return nc.Compare(Path.GetDirectoryName(Utilities.RemovePathUnsafeCharacters(x.Name)), Path.GetDirectoryName(Utilities.RemovePathUnsafeCharacters(y.Name))); } else if ((x.ItemType == ItemType.Rom || x.ItemType == ItemType.Disk) && (y.ItemType != ItemType.Rom && y.ItemType != ItemType.Disk)) @@ -1084,13 +1250,17 @@ namespace SabreTools.Library.DatItems { return nc.Compare(Path.GetFileName(x.Name), Path.GetFileName(y.Name)); } + return nc.Compare(Path.GetDirectoryName(x.Name), Path.GetDirectoryName(y.Name)); } } + return nc.Compare(x.MachineName, y.MachineName); } + return (norename ? nc.Compare(x.MachineName, y.MachineName) : x.SourceID - y.SourceID); } + return (norename ? nc.Compare(x.MachineName, y.MachineName) : x.SystemID - y.SystemID); } catch (Exception) diff --git a/SabreTools.Library/DatItems/Disk.cs b/SabreTools.Library/DatItems/Disk.cs index 6fe85b47..550587ef 100644 --- a/SabreTools.Library/DatItems/Disk.cs +++ b/SabreTools.Library/DatItems/Disk.cs @@ -116,7 +116,7 @@ namespace SabreTools.Library.DatItems /// public Disk() { - this.Name = ""; + this.Name = string.Empty; this.ItemType = ItemType.Disk; this.DupeType = 0x00; this.ItemStatus = ItemStatus.None; @@ -264,8 +264,8 @@ namespace SabreTools.Library.DatItems if (this.ItemType != other.ItemType) return dupefound; - // Otherwise, treat it as a rom - Disk newOther = (Disk)other; + // Otherwise, treat it as a Disk + Disk newOther = other as Disk; // If all hashes are empty but they're both nodump and the names match, then they're dupes if ((this.ItemStatus == ItemStatus.Nodump && newOther.ItemStatus == ItemStatus.Nodump) diff --git a/SabreTools.Library/DatItems/Release.cs b/SabreTools.Library/DatItems/Release.cs index 7b947eb4..a3b6349b 100644 --- a/SabreTools.Library/DatItems/Release.cs +++ b/SabreTools.Library/DatItems/Release.cs @@ -38,11 +38,11 @@ namespace SabreTools.Library.DatItems /// public Release() { - this.Name = ""; + this.Name = string.Empty; this.ItemType = ItemType.Release; - this.Region = ""; - this.Language = ""; - this.Date = ""; + this.Region = string.Empty; + this.Language = string.Empty; + this.Date = string.Empty; this.Default = null; } @@ -102,12 +102,10 @@ namespace SabreTools.Library.DatItems { // If we don't have a release return false if (this.ItemType != other.ItemType) - { return false; - } - // Otherwise, treat it as a reease - Release newOther = (Release)other; + // Otherwise, treat it as a Release + Release newOther = other as Release; // If the archive information matches return (this.Name == newOther.Name diff --git a/SabreTools.Library/DatItems/Rom.cs b/SabreTools.Library/DatItems/Rom.cs index 87169b7c..cf20c1b0 100644 --- a/SabreTools.Library/DatItems/Rom.cs +++ b/SabreTools.Library/DatItems/Rom.cs @@ -136,11 +136,11 @@ namespace SabreTools.Library.DatItems /// public Rom() { - this.Name = ""; + this.Name = string.Empty; this.ItemType = ItemType.Rom; this.DupeType = 0x00; this.ItemStatus = ItemStatus.None; - this.Date = ""; + this.Date = string.Empty; } /// @@ -155,34 +155,28 @@ namespace SabreTools.Library.DatItems this.Name = name; this.ItemType = ItemType.Rom; this.Size = -1; + if ((omitFromScan & Hash.CRC) == 0) - { _crc = null; - } + if ((omitFromScan & Hash.MD5) == 0) - { _md5 = null; - } + if ((omitFromScan & Hash.RIPEMD160) == 0) - { _ripemd160 = null; - } + if ((omitFromScan & Hash.SHA1) == 0) - { _sha1 = null; - } + if ((omitFromScan & Hash.SHA256) == 0) - { _sha256 = null; - } + if ((omitFromScan & Hash.SHA384) == 0) - { _sha384 = null; - } + if ((omitFromScan & Hash.SHA512) == 0) - { _sha512 = null; - } + this.ItemStatus = ItemStatus.None; _machine = new Machine @@ -278,12 +272,10 @@ namespace SabreTools.Library.DatItems // If we don't have a rom, return false if (this.ItemType != other.ItemType) - { return dupefound; - } - // Otherwise, treat it as a rom - Rom newOther = (Rom)other; + // Otherwise, treat it as a Rom + Rom newOther = other as Rom; // If all hashes are empty but they're both nodump and the names match, then they're dupes if ((this.ItemStatus == ItemStatus.Nodump && newOther.ItemStatus == ItemStatus.Nodump) diff --git a/SabreTools.Library/DatItems/Sample.cs b/SabreTools.Library/DatItems/Sample.cs index 0d2c68a5..7764a07a 100644 --- a/SabreTools.Library/DatItems/Sample.cs +++ b/SabreTools.Library/DatItems/Sample.cs @@ -14,7 +14,7 @@ namespace SabreTools.Library.DatItems /// public Sample() { - this.Name = ""; + this.Name = string.Empty; this.ItemType = ItemType.Sample; } @@ -69,12 +69,10 @@ namespace SabreTools.Library.DatItems { // If we don't have a sample, return false if (this.ItemType != other.ItemType) - { return false; - } - // Otherwise, treat it as a sample - Sample newOther = (Sample)other; + // Otherwise, treat it as a Sample + Sample newOther = other as Sample; // If the archive information matches return (this.Name == newOther.Name); diff --git a/SabreTools.Library/Data/Build.cs b/SabreTools.Library/Data/Build.cs index fe3bbdbe..bf2e9822 100644 --- a/SabreTools.Library/Data/Build.cs +++ b/SabreTools.Library/Data/Build.cs @@ -18,14 +18,14 @@ namespace SabreTools.Library.Data /// /// Readies the console and outputs the header /// - /// The name to be displayed as the programB + /// The name to be displayed as the program public static void PrepareConsole(string name) { // Dynamically create the header string, adapted from http://stackoverflow.com/questions/8200661/how-to-align-string-in-fixed-length-string int width = Console.WindowWidth - 3; - string border = "+" + new string('-', width) + "+"; - string mid = name + " " + Constants.Version; - mid = "|" + mid.PadLeft(((width - mid.Length) / 2) + mid.Length).PadRight(width) + "|"; + string border = $"+{new string('-', width)}+"; + string mid = $"{name} {Constants.Version}"; + mid = $"|{mid.PadLeft(((width - mid.Length) / 2) + mid.Length).PadRight(width)}|"; // If we're outputting to console, do fancy things if (!Console.IsOutputRedirected) @@ -42,7 +42,7 @@ namespace SabreTools.Library.Data Console.BackgroundColor = ConsoleColor.Blue; } - Console.Title = name + " " + Constants.Version; + Console.Title = $"{name} {Constants.Version}"; // Output the header Console.WriteLine(border); diff --git a/SabreTools.Library/Data/Constants.cs b/SabreTools.Library/Data/Constants.cs index e7f04aba..cda3e605 100644 --- a/SabreTools.Library/Data/Constants.cs +++ b/SabreTools.Library/Data/Constants.cs @@ -1,13 +1,6 @@ using System; -using System.Reflection; - -using SabreTools.Library.Tools; - -#if MONO using System.IO; -#else -using Alphaleonis.Win32.Filesystem; -#endif +using System.Reflection; namespace SabreTools.Library.Data { @@ -19,7 +12,7 @@ namespace SabreTools.Library.Data /// /// The current toolset version to be used by all child applications /// - public readonly static string Version = "v1.0.0-" + File.GetCreationTime(Assembly.GetExecutingAssembly().Location).ToString("yyyy-MM-dd HH:mm:ss"); + public readonly static string Version = $"v1.0.0-{File.GetCreationTime(Assembly.GetExecutingAssembly().Location).ToString("yyyy-MM-dd HH:mm:ss")}"; public const int HeaderHeight = 3; #region 0-byte file constants @@ -164,7 +157,7 @@ namespace SabreTools.Library.Data public const string HeadererDbSchema = "Headerer"; public static string HeadererFileName = Path.Combine(Globals.ExeDir, "Headerer.sqlite"); - public static string HeadererConnectionString = "Data Source=" + HeadererFileName + ";Version = 3;"; + public static string HeadererConnectionString = $"Data Source={HeadererFileName};Version = 3;"; #endregion @@ -177,8 +170,8 @@ namespace SabreTools.Library.Data This DTD module is identified by the PUBLIC and SYSTEM identifiers: - PUBLIC "" -//Logiqx//DTD ROM Management Datafile//EN"" - SYSTEM ""http://www.logiqx.com/Dats/datafile.dtd"" + PUBLIC string.Empty -//Logiqx//DTD ROM Management Datafile//ENstring.Empty + SYSTEM string.Emptyhttp://www.logiqx.com/Dats/datafile.dtdstring.Empty $Revision: 1.5 $ $Date: 2008/10/28 21:39:16 $ @@ -187,7 +180,7 @@ namespace SabreTools.Library.Data - + @@ -201,21 +194,21 @@ namespace SabreTools.Library.Data - - - + + + - - - - - - + + + + + + - + @@ -228,11 +221,11 @@ namespace SabreTools.Library.Data - + - + @@ -244,7 +237,7 @@ namespace SabreTools.Library.Data - + @@ -255,7 +248,7 @@ namespace SabreTools.Library.Data - + @@ -263,7 +256,7 @@ namespace SabreTools.Library.Data - + @@ -272,15 +265,15 @@ namespace SabreTools.Library.Data "; public const string MAMEDTD = @" - + - - - - + + + + @@ -290,7 +283,7 @@ namespace SabreTools.Library.Data - + @@ -305,8 +298,8 @@ namespace SabreTools.Library.Data - - + + @@ -318,9 +311,9 @@ namespace SabreTools.Library.Data - - - + + + @@ -334,7 +327,7 @@ namespace SabreTools.Library.Data - + @@ -353,8 +346,8 @@ namespace SabreTools.Library.Data - - + + @@ -366,7 +359,7 @@ namespace SabreTools.Library.Data - + @@ -377,11 +370,11 @@ namespace SabreTools.Library.Data - + - + @@ -389,11 +382,11 @@ namespace SabreTools.Library.Data - + - + @@ -426,7 +419,7 @@ namespace SabreTools.Library.Data - + @@ -450,7 +443,7 @@ namespace SabreTools.Library.Data - + @@ -470,8 +463,8 @@ namespace SabreTools.Library.Data - - + + @@ -484,7 +477,7 @@ namespace SabreTools.Library.Data - + @@ -496,8 +489,8 @@ namespace SabreTools.Library.Data - - + + @@ -505,7 +498,7 @@ namespace SabreTools.Library.Data - + "; #endregion diff --git a/SabreTools.Library/Data/Enums.cs b/SabreTools.Library/Data/Enums.cs index 0fa477af..677e503a 100644 --- a/SabreTools.Library/Data/Enums.cs +++ b/SabreTools.Library/Data/Enums.cs @@ -128,19 +128,20 @@ public enum OutputFormat { // Currently implemented - Folder = 0, - TorrentZip = 1, - TorrentGzip = 2, - TapeArchive = 5, + Folder, + TorrentZip, + TorrentGzip, + TorrentGzipRomba, + TapeArchive, // Currently unimplemented fully - Torrent7Zip = 3, - TorrentRar = 4, - TorrentXZ = 6, - TorrentLRZip = 7, - TorrentLZ4 = 8, - TorrentZstd = 9, - TorrentZPAQ = 10, + Torrent7Zip, + TorrentRar, + TorrentXZ, + TorrentLRZip, + TorrentLZ4, + TorrentZstd, + TorrentZPAQ, } /// diff --git a/SabreTools.Library/Data/Flags.cs b/SabreTools.Library/Data/Flags.cs index 61f6bb37..d6e25fe7 100644 --- a/SabreTools.Library/Data/Flags.cs +++ b/SabreTools.Library/Data/Flags.cs @@ -249,9 +249,9 @@ namespace SabreTools.Library.Data Logiqx = 1 << 0, /// - /// Logiqx XML (using gmae) + /// Logiqx XML (using game) /// - LogiqxDepreciated = 1 << 1, + LogiqxDeprecated = 1 << 1, /// /// MAME Softare List XML @@ -377,6 +377,8 @@ namespace SabreTools.Library.Data #endregion + // TODO: Add JSON output. Just for kicks + // Specialty combinations ALL = Int32.MaxValue, } diff --git a/SabreTools.Library/Data/Globals.cs b/SabreTools.Library/Data/Globals.cs index 00245f65..21ade87a 100644 --- a/SabreTools.Library/Data/Globals.cs +++ b/SabreTools.Library/Data/Globals.cs @@ -1,15 +1,10 @@ using System; +using System.IO; using System.Reflection; using System.Threading.Tasks; using SabreTools.Library.Tools; -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; -#endif - namespace SabreTools.Library.Data { /// @@ -31,9 +26,8 @@ namespace SabreTools.Library.Data get { if (_logger == null) - { _logger = new Logger(); - } + return _logger; } set { _logger = value; } diff --git a/SabreTools.Library/External/Compress/File/File.cs b/SabreTools.Library/External/Compress/File/File.cs index fabf7a93..3456bcbf 100644 --- a/SabreTools.Library/External/Compress/File/File.cs +++ b/SabreTools.Library/External/Compress/File/File.cs @@ -13,7 +13,7 @@ namespace Compress.File private Stream _inStream; private byte[] _crc; - public string ZipFilename => _fileInfo?.FullName ?? ""; + public string ZipFilename => _fileInfo?.FullName ?? string.Empty; public long TimeStamp => _fileInfo?.LastWriteTime ?? 0; diff --git a/SabreTools.Library/External/Compress/SevenZip/SevenZip.cs b/SabreTools.Library/External/Compress/SevenZip/SevenZip.cs index a0c9a230..8eccdfa4 100644 --- a/SabreTools.Library/External/Compress/SevenZip/SevenZip.cs +++ b/SabreTools.Library/External/Compress/SevenZip/SevenZip.cs @@ -46,7 +46,7 @@ namespace Compress.SevenZip private long _baseOffset; - public string ZipFilename => _zipFileInfo != null ? _zipFileInfo.FullName : ""; + public string ZipFilename => _zipFileInfo != null ? _zipFileInfo.FullName : string.Empty; public long TimeStamp => _zipFileInfo?.LastWriteTime ?? 0; diff --git a/SabreTools.Library/External/Compress/ZipFile/ZLib/Deflate.cs b/SabreTools.Library/External/Compress/ZipFile/ZLib/Deflate.cs index 0b7b3460..ad956c1d 100644 --- a/SabreTools.Library/External/Compress/ZipFile/ZLib/Deflate.cs +++ b/SabreTools.Library/External/Compress/ZipFile/ZLib/Deflate.cs @@ -157,14 +157,14 @@ namespace Compress.ZipFile.ZLib { "need dictionary", "stream end", - "", + string.Empty, "file error", "stream error", "data error", "insufficient memory", "buffer error", "incompatible version", - "" + string.Empty }; // preset dictionary flag in zlib header diff --git a/SabreTools.Library/External/Compress/ZipFile/zipFile.cs b/SabreTools.Library/External/Compress/ZipFile/zipFile.cs index 8694ff06..e6616d6d 100644 --- a/SabreTools.Library/External/Compress/ZipFile/zipFile.cs +++ b/SabreTools.Library/External/Compress/ZipFile/zipFile.cs @@ -39,7 +39,7 @@ namespace Compress.ZipFile private bool _zip64; - public string ZipFilename => _zipFileInfo != null ? _zipFileInfo.FullName : ""; + public string ZipFilename => _zipFileInfo != null ? _zipFileInfo.FullName : string.Empty; public long TimeStamp => _zipFileInfo?.LastWriteTime ?? 0; @@ -923,7 +923,7 @@ namespace Compress.ZipFile switch (zS) { case ZipReturn.ZipGood: - ret = ""; + ret = string.Empty; break; case ZipReturn.ZipFileCountError: ret = "The number of file in the Zip does not mach the number of files in the Zips Centeral Directory"; @@ -990,7 +990,7 @@ namespace Compress.ZipFile private static string GetString(byte[] byteArr) { - string s = ""; + string s = string.Empty; foreach (byte by in byteArr) { s += (char)by; diff --git a/SabreTools.Library/External/Compress/gZip/gZip.cs b/SabreTools.Library/External/Compress/gZip/gZip.cs index 6d9a1eba..dfa99d85 100644 --- a/SabreTools.Library/External/Compress/gZip/gZip.cs +++ b/SabreTools.Library/External/Compress/gZip/gZip.cs @@ -322,7 +322,7 @@ namespace Compress.gZip public ZipStatus ZipStatus { get; private set; } - public string ZipFilename => _zipFileInfo != null ? _zipFileInfo.FullName : ""; + public string ZipFilename => _zipFileInfo != null ? _zipFileInfo.FullName : string.Empty; public long TimeStamp => _zipFileInfo?.LastWriteTime ?? 0; diff --git a/SabreTools.Library/External/NaturalSort/NaturalComparer.cs b/SabreTools.Library/External/NaturalSort/NaturalComparer.cs index 447783dd..f6c74f11 100644 --- a/SabreTools.Library/External/NaturalSort/NaturalComparer.cs +++ b/SabreTools.Library/External/NaturalSort/NaturalComparer.cs @@ -41,14 +41,14 @@ namespace NaturalSort } if (!table.TryGetValue(x, out string[] x1)) { - //x1 = Regex.Split(x.Replace(" ", ""), "([0-9]+)"); - x1 = Regex.Split(x.ToLowerInvariant(), "([0-9]+)").Where(s => !String.IsNullOrWhiteSpace(s)).ToArray(); + //x1 = Regex.Split(x.Replace(" ", string.Empty), "([0-9]+)"); + x1 = Regex.Split(x.ToLowerInvariant(), "([0-9]+)").Where(s => !string.IsNullOrWhiteSpace(s)).ToArray(); table.Add(x, x1); } if (!table.TryGetValue(y, out string[] y1)) { - //y1 = Regex.Split(y.Replace(" ", ""), "([0-9]+)"); - y1 = Regex.Split(y.ToLowerInvariant(), "([0-9]+)").Where(s => !String.IsNullOrWhiteSpace(s)).ToArray(); + //y1 = Regex.Split(y.Replace(" ", string.Empty), "([0-9]+)"); + y1 = Regex.Split(y.ToLowerInvariant(), "([0-9]+)").Where(s => !string.IsNullOrWhiteSpace(s)).ToArray(); table.Add(y, y1); } diff --git a/SabreTools.Library/External/NaturalSort/NaturalReversedComparer.cs b/SabreTools.Library/External/NaturalSort/NaturalReversedComparer.cs index 3c640791..5552b707 100644 --- a/SabreTools.Library/External/NaturalSort/NaturalReversedComparer.cs +++ b/SabreTools.Library/External/NaturalSort/NaturalReversedComparer.cs @@ -41,14 +41,14 @@ namespace NaturalSort } if (!table.TryGetValue(x, out string[] x1)) { - //x1 = Regex.Split(x.Replace(" ", ""), "([0-9]+)"); - x1 = Regex.Split(x.ToLowerInvariant(), "([0-9]+)").Where(s => !String.IsNullOrWhiteSpace(s)).ToArray(); + //x1 = Regex.Split(x.Replace(" ", string.Empty), "([0-9]+)"); + x1 = Regex.Split(x.ToLowerInvariant(), "([0-9]+)").Where(s => !string.IsNullOrWhiteSpace(s)).ToArray(); table.Add(x, x1); } if (!table.TryGetValue(y, out string[] y1)) { - //y1 = Regex.Split(y.Replace(" ", ""), "([0-9]+)"); - y1 = Regex.Split(y.ToLowerInvariant(), "([0-9]+)").Where(s => !String.IsNullOrWhiteSpace(s)).ToArray(); + //y1 = Regex.Split(y.Replace(" ", string.Empty), "([0-9]+)"); + y1 = Regex.Split(y.ToLowerInvariant(), "([0-9]+)").Where(s => !string.IsNullOrWhiteSpace(s)).ToArray(); table.Add(y, y1); } diff --git a/SabreTools.Library/FileTypes/Folder.cs b/SabreTools.Library/FileTypes/Folder.cs index ba0f0cd3..9c67b307 100644 --- a/SabreTools.Library/FileTypes/Folder.cs +++ b/SabreTools.Library/FileTypes/Folder.cs @@ -1,26 +1,12 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using SabreTools.Library.Data; using SabreTools.Library.DatItems; using SabreTools.Library.Tools; -#if MONO -using System.IO; -using Directory = Alphaleonis.Win32.Filesystem.Directory; -using PathFormat = Alphaleonis.Win32.Filesystem.PathFormat; -#else -using Alphaleonis.Win32.Filesystem; - -using BinaryReader = System.IO.BinaryReader; -using FileStream = System.IO.FileStream; -using MemoryStream = System.IO.MemoryStream; -using SearchOption = System.IO.SearchOption; -using SeekOrigin = System.IO.SeekOrigin; -using Stream = System.IO.Stream; -#endif - namespace SabreTools.Library.FileTypes { /// @@ -75,7 +61,7 @@ namespace SabreTools.Library.FileTypes Directory.CreateDirectory(this.Filename); Directory.CreateDirectory(outDir); - Directory.Copy(this.Filename, outDir, true, PathFormat.FullPath); + DirectoryCopy(this.Filename, outDir, true); } catch (Exception ex) { @@ -86,6 +72,45 @@ namespace SabreTools.Library.FileTypes return true; } + // https://docs.microsoft.com/en-us/dotnet/standard/io/how-to-copy-directories + private static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs) + { + // Get the subdirectories for the specified directory. + DirectoryInfo dir = new DirectoryInfo(sourceDirName); + + if (!dir.Exists) + { + throw new DirectoryNotFoundException( + "Source directory does not exist or could not be found: " + + sourceDirName); + } + + DirectoryInfo[] dirs = dir.GetDirectories(); + // If the destination directory doesn't exist, create it. + if (!Directory.Exists(destDirName)) + { + Directory.CreateDirectory(destDirName); + } + + // Get the files in the directory and copy them to the new location. + FileInfo[] files = dir.GetFiles(); + foreach (FileInfo file in files) + { + string temppath = Path.Combine(destDirName, file.Name); + file.CopyTo(temppath, false); + } + + // If copying subdirectories, copy them and their contents to new location. + if (copySubDirs) + { + foreach (DirectoryInfo subdir in dirs) + { + string temppath = Path.Combine(destDirName, subdir.Name); + DirectoryCopy(subdir.FullName, temppath, copySubDirs); + } + } + } + /// /// Attempt to extract a file from an archive /// @@ -110,7 +135,7 @@ namespace SabreTools.Library.FileTypes string match = files.Where(s => s.EndsWith(entryName)).FirstOrDefault(); // If we had a file, copy that over to the new name - if (!String.IsNullOrWhiteSpace(match)) + if (!string.IsNullOrWhiteSpace(match)) { realentry = match; File.Copy(match, Path.Combine(outDir, entryName)); @@ -149,7 +174,7 @@ namespace SabreTools.Library.FileTypes string match = files.Where(s => s.EndsWith(entryName)).FirstOrDefault(); // If we had a file, copy that over to the new name - if (!String.IsNullOrWhiteSpace(match)) + if (!string.IsNullOrWhiteSpace(match)) { Utilities.TryOpenRead(match).CopyTo(ms); realentry = match; @@ -185,6 +210,7 @@ namespace SabreTools.Library.FileTypes BaseFile nf = Utilities.GetFileInfo(file, omitFromScan: omitFromScan, date: date); _children.Add(nf); } + foreach (string dir in Directory.EnumerateDirectories(this.Filename, "*", SearchOption.TopDirectoryOnly)) { Folder fl = new Folder(dir); @@ -285,7 +311,7 @@ namespace SabreTools.Library.FileTypes if (rom.ItemType == ItemType.Rom) { - if (date && !String.IsNullOrWhiteSpace(((Rom)rom).Date)) + if (date && !string.IsNullOrWhiteSpace(((Rom)rom).Date)) { File.SetCreationTime(fileName, DateTime.Parse(((Rom)rom).Date)); } diff --git a/SabreTools.Library/FileTypes/GZipArchive.cs b/SabreTools.Library/FileTypes/GZipArchive.cs index e8c94289..7d12157b 100644 --- a/SabreTools.Library/FileTypes/GZipArchive.cs +++ b/SabreTools.Library/FileTypes/GZipArchive.cs @@ -1,25 +1,12 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text.RegularExpressions; using SabreTools.Library.Data; using SabreTools.Library.DatItems; using SabreTools.Library.Tools; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using BinaryReader = System.IO.BinaryReader; -using BinaryWriter = System.IO.BinaryWriter; -using EndOfStreamException = System.IO.EndOfStreamException; -using FileStream = System.IO.FileStream; -using MemoryStream = System.IO.MemoryStream; -using SeekOrigin = System.IO.SeekOrigin; -using Stream = System.IO.Stream; -#endif using Compress; using Compress.gZip; using Compress.ZipFile.ZLib; @@ -290,21 +277,21 @@ namespace SabreTools.Library.FileTypes // If we have the romba depot files, just skip them gracefully if (datum == ".romba_size" || datum == ".romba_size.backup") { - Globals.Logger.Verbose("Romba depot file found, skipping: {0}", this.Filename); + Globals.Logger.Verbose($"Romba depot file found, skipping: {this.Filename}"); return false; } // Check if the name is the right length if (!Regex.IsMatch(datum, @"^[0-9a-f]{" + Constants.SHA1Length + @"}\.gz")) // TODO: When updating to SHA-256, this needs to update to Constants.SHA256Length { - Globals.Logger.Warning("Non SHA-1 filename found, skipping: '{0}'", Path.GetFullPath(this.Filename)); + Globals.Logger.Warning($"Non SHA-1 filename found, skipping: '{Path.GetFullPath(this.Filename)}'"); return false; } // Check if the file is at least the minimum length if (filesize < 40 /* bytes */) { - Globals.Logger.Warning("Possibly corrupt file '{0}' with size {1}", Path.GetFullPath(this.Filename), Utilities.GetBytesReadable(filesize)); + Globals.Logger.Warning($"Possibly corrupt file '{Path.GetFullPath(this.Filename)}' with size {Utilities.GetBytesReadable(filesize)}"); return false; } @@ -357,21 +344,21 @@ namespace SabreTools.Library.FileTypes // If we have the romba depot files, just skip them gracefully if (datum == ".romba_size" || datum == ".romba_size.backup") { - Globals.Logger.Verbose("Romba depot file found, skipping: {0}", this.Filename); + Globals.Logger.Verbose($"Romba depot file found, skipping: {this.Filename}"); return null; } // Check if the name is the right length if (!Regex.IsMatch(datum, @"^[0-9a-f]{" + Constants.SHA1Length + @"}\.gz")) // TODO: When updating to SHA-256, this needs to update to Constants.SHA256Length { - Globals.Logger.Warning("Non SHA-1 filename found, skipping: '{0}'", Path.GetFullPath(this.Filename)); + Globals.Logger.Warning($"Non SHA-1 filename found, skipping: '{Path.GetFullPath(this.Filename)}'"); return null; } // Check if the file is at least the minimum length if (filesize < 40 /* bytes */) { - Globals.Logger.Warning("Possibly corrupt file '{0}' with size {1}", Path.GetFullPath(this.Filename), Utilities.GetBytesReadable(filesize)); + Globals.Logger.Warning($"Possibly corrupt file '{Path.GetFullPath(this.Filename)}' with size {Utilities.GetBytesReadable(filesize)}"); return null; } @@ -439,7 +426,7 @@ namespace SabreTools.Library.FileTypes // Check that the input file exists if (!File.Exists(inputFile)) { - Globals.Logger.Warning("File '{0}' does not exist!", inputFile); + Globals.Logger.Warning($"File '{inputFile}' does not exist!"); return false; } inputFile = Path.GetFullPath(inputFile); diff --git a/SabreTools.Library/FileTypes/RarArchive.cs b/SabreTools.Library/FileTypes/RarArchive.cs index bdac57c7..cfcd7d7c 100644 --- a/SabreTools.Library/FileTypes/RarArchive.cs +++ b/SabreTools.Library/FileTypes/RarArchive.cs @@ -1,23 +1,11 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using SabreTools.Library.Data; using SabreTools.Library.DatItems; using SabreTools.Library.Tools; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using BinaryReader = System.IO.BinaryReader; -using EndOfStreamException = System.IO.EndOfStreamException; -using FileStream = System.IO.FileStream; -using MemoryStream = System.IO.MemoryStream; -using SeekOrigin = System.IO.SeekOrigin; -using Stream = System.IO.Stream; -#endif using SharpCompress.Archives; using SharpCompress.Archives.Rar; using SharpCompress.Readers; diff --git a/SabreTools.Library/FileTypes/SevenZipArchive.cs b/SabreTools.Library/FileTypes/SevenZipArchive.cs index 8c2718c5..439eccdb 100644 --- a/SabreTools.Library/FileTypes/SevenZipArchive.cs +++ b/SabreTools.Library/FileTypes/SevenZipArchive.cs @@ -1,23 +1,11 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using SabreTools.Library.Data; using SabreTools.Library.DatItems; using SabreTools.Library.Tools; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using BinaryWriter = System.IO.BinaryWriter; -using EndOfStreamException = System.IO.EndOfStreamException; -using FileStream = System.IO.FileStream; -using MemoryStream = System.IO.MemoryStream; -using SeekOrigin = System.IO.SeekOrigin; -using Stream = System.IO.Stream; -#endif using Compress; using Compress.SevenZip; using Compress.ZipFile; @@ -85,10 +73,8 @@ namespace SabreTools.Library.FileTypes zr = zf.ZipFileOpenReadStream(i, out Stream readStream, out ulong streamsize); // Create the rest of the path, if needed - if (!String.IsNullOrWhiteSpace(Path.GetDirectoryName(zf.Filename(i)))) - { + if (!string.IsNullOrWhiteSpace(Path.GetDirectoryName(zf.Filename(i)))) Directory.CreateDirectory(Path.Combine(outDir, Path.GetDirectoryName(zf.Filename(i)))); - } // If the entry ends with a directory separator, continue to the next item, if any if (zf.Filename(i).EndsWith(Path.DirectorySeparatorChar.ToString()) @@ -304,7 +290,7 @@ namespace SabreTools.Library.FileTypes // If we get a read error, log it and continue if (zr != ZipReturn.ZipGood) { - Globals.Logger.Warning("An error occurred while reading archive {0}: Zip Error - {1}", this.Filename, zr); + Globals.Logger.Warning($"An error occurred while reading archive {this.Filename}: Zip Error - {zr}"); zr = zf.ZipFileCloseReadStream(); continue; } @@ -446,7 +432,7 @@ namespace SabreTools.Library.FileTypes public override bool Write(Stream inputStream, string outDir, Rom rom, bool date = false, bool romba = false) { bool success = false; - string tempFile = Path.Combine(outDir, "tmp" + Guid.NewGuid().ToString()); + string tempFile = Path.Combine(outDir, $"tmp{Guid.NewGuid()}"); // If either input is null or empty, return if (inputStream == null || rom == null || rom.Name == null) @@ -464,7 +450,7 @@ namespace SabreTools.Library.FileTypes inputStream.Seek(0, SeekOrigin.Begin); // Get the output archive name from the first rebuild rom - string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(rom.MachineName) + (rom.MachineName.EndsWith(".zip") ? "" : ".zip")); + string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(rom.MachineName) + (rom.MachineName.EndsWith(".zip") ? string.Empty : ".zip")); // Set internal variables Stream writeStream = null; @@ -490,7 +476,7 @@ namespace SabreTools.Library.FileTypes ulong istreamSize = (ulong)(inputStream.Length); DateTime dt = DateTime.Now; - if (date && !String.IsNullOrWhiteSpace(rom.Date) && DateTime.TryParse(rom.Date.Replace('\\', '/'), out dt)) + if (date && !string.IsNullOrWhiteSpace(rom.Date) && DateTime.TryParse(rom.Date.Replace('\\', '/'), out dt)) { uint msDosDateTime = Utilities.ConvertDateTimeToMsDosTimeFormat(dt); zipFile.ZipFileOpenWriteStream(false, false, rom.Name.Replace('\\', '/'), istreamSize, 0, msDosDateTime, out writeStream); @@ -565,7 +551,7 @@ namespace SabreTools.Library.FileTypes ulong istreamSize = (ulong)(inputStream.Length); DateTime dt = DateTime.Now; - if (date && !String.IsNullOrWhiteSpace(rom.Date) && DateTime.TryParse(rom.Date.Replace('\\', '/'), out dt)) + if (date && !string.IsNullOrWhiteSpace(rom.Date) && DateTime.TryParse(rom.Date.Replace('\\', '/'), out dt)) { uint msDosDateTime = Utilities.ConvertDateTimeToMsDosTimeFormat(dt); zipFile.ZipFileOpenWriteStream(false, false, rom.Name.Replace('\\', '/'), istreamSize, 0, msDosDateTime, out writeStream); @@ -648,7 +634,7 @@ namespace SabreTools.Library.FileTypes public override bool Write(List inputFiles, string outDir, List roms, bool date = false, bool romba = false) { bool success = false; - string tempFile = Path.Combine(outDir, "tmp" + Guid.NewGuid().ToString()); + string tempFile = Path.Combine(outDir, $"tmp{Guid.NewGuid()}"); // If either list of roms is null or empty, return if (inputFiles == null || roms == null || inputFiles.Count == 0 || roms.Count == 0) @@ -672,7 +658,7 @@ namespace SabreTools.Library.FileTypes } // Get the output archive name from the first rebuild rom - string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(roms[0].MachineName) + (roms[0].MachineName.EndsWith(".zip") ? "" : ".zip")); + string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(roms[0].MachineName) + (roms[0].MachineName.EndsWith(".zip") ? string.Empty : ".zip")); // Set internal variables Stream writeStream = null; @@ -715,7 +701,7 @@ namespace SabreTools.Library.FileTypes ulong istreamSize = (ulong)(new FileInfo(inputFiles[index]).Length); DateTime dt = DateTime.Now; - if (date && !String.IsNullOrWhiteSpace(roms[index].Date) && DateTime.TryParse(roms[index].Date.Replace('\\', '/'), out dt)) + if (date && !string.IsNullOrWhiteSpace(roms[index].Date) && DateTime.TryParse(roms[index].Date.Replace('\\', '/'), out dt)) { uint msDosDateTime = Utilities.ConvertDateTimeToMsDosTimeFormat(dt); zipFile.ZipFileOpenWriteStream(false, false, roms[index].Name.Replace('\\', '/'), istreamSize, 0, msDosDateTime, out writeStream); @@ -798,7 +784,7 @@ namespace SabreTools.Library.FileTypes ulong istreamSize = (ulong)(new FileInfo(inputFiles[-index - 1]).Length); DateTime dt = DateTime.Now; - if (date && !String.IsNullOrWhiteSpace(roms[-index - 1].Date) && DateTime.TryParse(roms[-index - 1].Date.Replace('\\', '/'), out dt)) + if (date && !string.IsNullOrWhiteSpace(roms[-index - 1].Date) && DateTime.TryParse(roms[-index - 1].Date.Replace('\\', '/'), out dt)) { uint msDosDateTime = Utilities.ConvertDateTimeToMsDosTimeFormat(dt); zipFile.ZipFileOpenWriteStream(false, false, roms[-index - 1].Name.Replace('\\', '/'), istreamSize, 0, msDosDateTime, out writeStream); diff --git a/SabreTools.Library/FileTypes/TapeArchive.cs b/SabreTools.Library/FileTypes/TapeArchive.cs index 25a009cb..b238b7df 100644 --- a/SabreTools.Library/FileTypes/TapeArchive.cs +++ b/SabreTools.Library/FileTypes/TapeArchive.cs @@ -1,22 +1,11 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using SabreTools.Library.Data; using SabreTools.Library.DatItems; using SabreTools.Library.Tools; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using EndOfStreamException = System.IO.EndOfStreamException; -using FileStream = System.IO.FileStream; -using MemoryStream = System.IO.MemoryStream; -using SeekOrigin = System.IO.SeekOrigin; -using Stream = System.IO.Stream; -#endif using Compress.ZipFile; using SharpCompress.Archives; using SharpCompress.Archives.Tar; @@ -318,7 +307,7 @@ namespace SabreTools.Library.FileTypes public override bool Write(Stream inputStream, string outDir, Rom rom, bool date = false, bool romba = false) { bool success = false; - string tempFile = Path.Combine(outDir, "tmp" + Guid.NewGuid().ToString()); + string tempFile = Path.Combine(outDir, $"tmp{Guid.NewGuid()}"); // If either input is null or empty, return if (inputStream == null || rom == null || rom.Name == null) @@ -333,7 +322,7 @@ namespace SabreTools.Library.FileTypes } // Get the output archive name from the first rebuild rom - string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(rom.MachineName) + (rom.MachineName.EndsWith(".tar") ? "" : ".tar")); + string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(rom.MachineName) + (rom.MachineName.EndsWith(".tar") ? string.Empty : ".tar")); // Set internal variables TarArchive oldTarFile = TarArchive.Create(); @@ -352,7 +341,7 @@ namespace SabreTools.Library.FileTypes { // Get temporary date-time if possible DateTime? usableDate = null; - if (date && !String.IsNullOrWhiteSpace(rom.Date) && DateTime.TryParse(rom.Date.Replace('\\', '/'), out DateTime dt)) + if (date && !string.IsNullOrWhiteSpace(rom.Date) && DateTime.TryParse(rom.Date.Replace('\\', '/'), out DateTime dt)) { usableDate = dt; } @@ -405,7 +394,7 @@ namespace SabreTools.Library.FileTypes // Get temporary date-time if possible DateTime? usableDate = null; - if (date && !String.IsNullOrWhiteSpace(rom.Date) && DateTime.TryParse(rom.Date.Replace('\\', '/'), out DateTime dt)) + if (date && !string.IsNullOrWhiteSpace(rom.Date) && DateTime.TryParse(rom.Date.Replace('\\', '/'), out DateTime dt)) { usableDate = dt; } @@ -471,7 +460,7 @@ namespace SabreTools.Library.FileTypes public override bool Write(List inputFiles, string outDir, List roms, bool date = false, bool romba = false) { bool success = false; - string tempFile = Path.Combine(outDir, "tmp" + Guid.NewGuid().ToString()); + string tempFile = Path.Combine(outDir, $"tmp{Guid.NewGuid()}"); // If either list of roms is null or empty, return if (inputFiles == null || roms == null || inputFiles.Count == 0 || roms.Count == 0) @@ -495,7 +484,7 @@ namespace SabreTools.Library.FileTypes } // Get the output archive name from the first rebuild rom - string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(roms[0].MachineName) + (roms[0].MachineName.EndsWith(".tar") ? "" : ".tar")); + string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(roms[0].MachineName) + (roms[0].MachineName.EndsWith(".tar") ? string.Empty : ".tar")); // Set internal variables TarArchive oldTarFile = TarArchive.Create(); @@ -531,7 +520,7 @@ namespace SabreTools.Library.FileTypes // Get temporary date-time if possible DateTime? usableDate = null; - if (date && !String.IsNullOrWhiteSpace(roms[index].Date) && DateTime.TryParse(roms[index].Date.Replace('\\', '/'), out DateTime dt)) + if (date && !string.IsNullOrWhiteSpace(roms[index].Date) && DateTime.TryParse(roms[index].Date.Replace('\\', '/'), out DateTime dt)) { usableDate = dt; } @@ -591,7 +580,7 @@ namespace SabreTools.Library.FileTypes { // Get temporary date-time if possible DateTime? usableDate = null; - if (date && !String.IsNullOrWhiteSpace(roms[-index - 1].Date) && DateTime.TryParse(roms[-index - 1].Date.Replace('\\', '/'), out DateTime dt)) + if (date && !string.IsNullOrWhiteSpace(roms[-index - 1].Date) && DateTime.TryParse(roms[-index - 1].Date.Replace('\\', '/'), out DateTime dt)) { usableDate = dt; } diff --git a/SabreTools.Library/FileTypes/XZArchive.cs b/SabreTools.Library/FileTypes/XZArchive.cs index 57eb293b..76832387 100644 --- a/SabreTools.Library/FileTypes/XZArchive.cs +++ b/SabreTools.Library/FileTypes/XZArchive.cs @@ -1,23 +1,11 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using SabreTools.Library.Data; using SabreTools.Library.DatItems; using SabreTools.Library.Tools; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using BinaryWriter = System.IO.BinaryWriter; -using EndOfStreamException = System.IO.EndOfStreamException; -using FileStream = System.IO.FileStream; -using MemoryStream = System.IO.MemoryStream; -using SeekOrigin = System.IO.SeekOrigin; -using Stream = System.IO.Stream; -#endif using Compress.ZipFile; using SevenZip; using SharpCompress.Archives; @@ -247,7 +235,7 @@ namespace SabreTools.Library.FileTypes public override bool Write(Stream inputStream, string outDir, Rom rom, bool date = false, bool romba = false) { bool success = false; - string tempFile = Path.Combine(outDir, "tmp" + Guid.NewGuid().ToString()); + string tempFile = Path.Combine(outDir, $"tmp{Guid.NewGuid()}"); // If either input is null or empty, return if (inputStream == null || rom == null || rom.Name == null) @@ -265,7 +253,7 @@ namespace SabreTools.Library.FileTypes inputStream.Seek(0, SeekOrigin.Begin); // Get the output archive name from the first rebuild rom - string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(rom.MachineName) + (rom.MachineName.EndsWith(".xz") ? "" : ".xz")); + string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(rom.MachineName) + (rom.MachineName.EndsWith(".xz") ? string.Empty : ".xz")); // Set internal variables SevenZipBase.SetLibraryPath("7za.dll"); @@ -443,7 +431,7 @@ namespace SabreTools.Library.FileTypes public override bool Write(List inputFiles, string outDir, List roms, bool date = false, bool romba = false) { bool success = false; - string tempFile = Path.Combine(outDir, "tmp" + Guid.NewGuid().ToString()); + string tempFile = Path.Combine(outDir, $"tmp{Guid.NewGuid()}"); // If either list of roms is null or empty, return if (inputFiles == null || roms == null || inputFiles.Count == 0 || roms.Count == 0) @@ -467,7 +455,7 @@ namespace SabreTools.Library.FileTypes } // Get the output archive name from the first rebuild rom - string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(roms[0].MachineName) + (roms[0].MachineName.EndsWith(".xz") ? "" : ".xz")); + string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(roms[0].MachineName) + (roms[0].MachineName.EndsWith(".xz") ? string.Empty : ".xz")); // Set internal variables SevenZipBase.SetLibraryPath("7za.dll"); diff --git a/SabreTools.Library/FileTypes/ZipArchive.cs b/SabreTools.Library/FileTypes/ZipArchive.cs index 210ccb72..8b21c696 100644 --- a/SabreTools.Library/FileTypes/ZipArchive.cs +++ b/SabreTools.Library/FileTypes/ZipArchive.cs @@ -1,22 +1,11 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using SabreTools.Library.Data; using SabreTools.Library.DatItems; using SabreTools.Library.Tools; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using EndOfStreamException = System.IO.EndOfStreamException; -using FileStream = System.IO.FileStream; -using MemoryStream = System.IO.MemoryStream; -using SeekOrigin = System.IO.SeekOrigin; -using Stream = System.IO.Stream; -#endif using Compress; using Compress.ZipFile; using NaturalSort; @@ -83,7 +72,7 @@ namespace SabreTools.Library.FileTypes zr = zf.ZipFileOpenReadStream(i, false, out Stream readStream, out ulong streamsize, out ushort cm); // Create the rest of the path, if needed - if (!String.IsNullOrWhiteSpace(Path.GetDirectoryName(zf.Filename(i)))) + if (!string.IsNullOrWhiteSpace(Path.GetDirectoryName(zf.Filename(i)))) { Directory.CreateDirectory(Path.Combine(outDir, Path.GetDirectoryName(zf.Filename(i)))); } @@ -302,7 +291,7 @@ namespace SabreTools.Library.FileTypes // If we get a read error, log it and continue if (zr != ZipReturn.ZipGood) { - Globals.Logger.Warning("An error occurred while reading archive {0}: Zip Error - {1}", this.Filename, zr); + Globals.Logger.Warning($"An error occurred while reading archive {this.Filename}: Zip Error - {zr}"); zr = zf.ZipFileCloseReadStream(); continue; } @@ -448,7 +437,7 @@ namespace SabreTools.Library.FileTypes public override bool Write(Stream inputStream, string outDir, Rom rom, bool date = false, bool romba = false) { bool success = false; - string tempFile = Path.Combine(outDir, "tmp" + Guid.NewGuid().ToString()); + string tempFile = Path.Combine(outDir, $"tmp{Guid.NewGuid()}"); // If either input is null or empty, return if (inputStream == null || rom == null || rom.Name == null) @@ -466,7 +455,7 @@ namespace SabreTools.Library.FileTypes inputStream.Seek(0, SeekOrigin.Begin); // Get the output archive name from the first rebuild rom - string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(rom.MachineName) + (rom.MachineName.EndsWith(".zip") ? "" : ".zip")); + string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(rom.MachineName) + (rom.MachineName.EndsWith(".zip") ? string.Empty : ".zip")); // Set internal variables Stream writeStream = null; @@ -492,7 +481,7 @@ namespace SabreTools.Library.FileTypes ulong istreamSize = (ulong)(inputStream.Length); DateTime dt = DateTime.Now; - if (date && !String.IsNullOrWhiteSpace(rom.Date) && DateTime.TryParse(rom.Date.Replace('\\', '/'), out dt)) + if (date && !string.IsNullOrWhiteSpace(rom.Date) && DateTime.TryParse(rom.Date.Replace('\\', '/'), out dt)) { uint msDosDateTime = Utilities.ConvertDateTimeToMsDosTimeFormat(dt); zipFile.ZipFileOpenWriteStream(false, false, rom.Name.Replace('\\', '/'), istreamSize, (ushort)CompressionMethod.Deflated, msDosDateTime, out writeStream); @@ -567,7 +556,7 @@ namespace SabreTools.Library.FileTypes ulong istreamSize = (ulong)(inputStream.Length); DateTime dt = DateTime.Now; - if (date && !String.IsNullOrWhiteSpace(rom.Date) && DateTime.TryParse(rom.Date.Replace('\\', '/'), out dt)) + if (date && !string.IsNullOrWhiteSpace(rom.Date) && DateTime.TryParse(rom.Date.Replace('\\', '/'), out dt)) { uint msDosDateTime = Utilities.ConvertDateTimeToMsDosTimeFormat(dt); zipFile.ZipFileOpenWriteStream(false, false, rom.Name.Replace('\\', '/'), istreamSize, (ushort)CompressionMethod.Deflated, msDosDateTime, out writeStream); @@ -651,7 +640,7 @@ namespace SabreTools.Library.FileTypes public override bool Write(List inputFiles, string outDir, List roms, bool date = false, bool romba = false) { bool success = false; - string tempFile = Path.Combine(outDir, "tmp" + Guid.NewGuid().ToString()); + string tempFile = Path.Combine(outDir, $"tmp{Guid.NewGuid()}"); // If either list of roms is null or empty, return if (inputFiles == null || roms == null || inputFiles.Count == 0 || roms.Count == 0) @@ -675,7 +664,7 @@ namespace SabreTools.Library.FileTypes } // Get the output archive name from the first rebuild rom - string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(roms[0].MachineName) + (roms[0].MachineName.EndsWith(".zip") ? "" : ".zip")); + string archiveFileName = Path.Combine(outDir, Utilities.RemovePathUnsafeCharacters(roms[0].MachineName) + (roms[0].MachineName.EndsWith(".zip") ? string.Empty : ".zip")); // Set internal variables Stream writeStream = null; @@ -718,7 +707,7 @@ namespace SabreTools.Library.FileTypes ulong istreamSize = (ulong)(new FileInfo(inputFiles[index]).Length); DateTime dt = DateTime.Now; - if (date && !String.IsNullOrWhiteSpace(roms[index].Date) && DateTime.TryParse(roms[index].Date.Replace('\\', '/'), out dt)) + if (date && !string.IsNullOrWhiteSpace(roms[index].Date) && DateTime.TryParse(roms[index].Date.Replace('\\', '/'), out dt)) { uint msDosDateTime = Utilities.ConvertDateTimeToMsDosTimeFormat(dt); zipFile.ZipFileOpenWriteStream(false, false, roms[index].Name.Replace('\\', '/'), istreamSize, (ushort)CompressionMethod.Deflated, msDosDateTime, out writeStream); @@ -801,7 +790,7 @@ namespace SabreTools.Library.FileTypes ulong istreamSize = (ulong)(new FileInfo(inputFiles[-index - 1]).Length); DateTime dt = DateTime.Now; - if (date && !String.IsNullOrWhiteSpace(roms[-index - 1].Date) && DateTime.TryParse(roms[-index - 1].Date.Replace('\\', '/'), out dt)) + if (date && !string.IsNullOrWhiteSpace(roms[-index - 1].Date) && DateTime.TryParse(roms[-index - 1].Date.Replace('\\', '/'), out dt)) { uint msDosDateTime = Utilities.ConvertDateTimeToMsDosTimeFormat(dt); zipFile.ZipFileOpenWriteStream(false, false, roms[-index - 1].Name.Replace('\\', '/'), istreamSize, (ushort)CompressionMethod.Deflated, msDosDateTime, out writeStream); diff --git a/SabreTools.Library/Help/Feature.cs b/SabreTools.Library/Help/Feature.cs index 78360792..4ed87367 100644 --- a/SabreTools.Library/Help/Feature.cs +++ b/SabreTools.Library/Help/Feature.cs @@ -1,34 +1,28 @@ using System; using System.Collections.Generic; using System.IO; - +using System.Linq; using SabreTools.Library.Data; namespace SabreTools.Library.Help { public class Feature { - #region Private instance variables + #region Protected instance variables - private FeatureType _featureType; - private bool _foundOnce = false; - - // Specific value types - private bool _valueBool = false; - private int _valueInt32 = Int32.MinValue; - private long _valueInt64 = Int64.MinValue; - private string _valueString = null; - private List _valueList = null; + protected FeatureType _featureType; + protected bool _foundOnce = false; + protected object _value = null; #endregion #region Publicly facing variables - public string Name { get; private set; } - public List Flags { get; private set; } - public string Description { get; private set; } - public string LongDescription { get; private set; } // TODO: Use this to generate README.1ST? - public Dictionary Features { get; private set; } + public string Name { get; protected set; } + public List Flags { get; protected set; } + public string Description { get; protected set; } + public string LongDescription { get; protected set; } // TODO: Use this to generate README.1ST? + public Dictionary Features { get; protected set; } #endregion @@ -74,7 +68,7 @@ namespace SabreTools.Library.Help /// public Feature this[string name] { - get { return this.Features[name]; } + get { return this.Features.ContainsKey(name) ? this.Features[name] : null; } set { this.Features[name] = value; } } @@ -83,7 +77,7 @@ namespace SabreTools.Library.Help /// public Feature this[Feature subfeature] { - get { return this.Features[subfeature.Name]; } + get { return this.Features.ContainsKey(subfeature.Name) ? this.Features[subfeature.Name] : null; } set { this.Features[subfeature.Name] = value; } } @@ -139,24 +133,7 @@ namespace SabreTools.Library.Help /// True if the flag was found, false otherwise public bool ContainsFlag(string name) { - bool success = false; - - // Loop through the flags - foreach (string flag in this.Flags) - { - if (flag == name) - { - success = true; - break; - } - else if (flag.TrimStart('-') == name) - { - success = true; - break; - } - } - - return success; + return this.Flags.Any(f => f == name || f.TrimStart('-') == name); } /// @@ -166,19 +143,7 @@ namespace SabreTools.Library.Help /// True if the flag was found, false otherwise public bool StartsWith(char c) { - bool success = false; - - // Loop through the flags - foreach (string flag in this.Flags) - { - if (flag.TrimStart('-').ToLowerInvariant()[0] == c) - { - success = true; - break; - } - } - - return success; + return this.Flags.Any(f => f.TrimStart('-').ToLowerInvariant()[0] == c); } #endregion @@ -197,7 +162,7 @@ namespace SabreTools.Library.Help List outputList = new List(); // Build the output string first - string output = ""; + string output = string.Empty; // Add the pre-space first output += CreatePadding(pre); @@ -223,17 +188,13 @@ namespace SabreTools.Library.Help } // Now add all flags - output += String.Join(", ", newflags); + output += string.Join(", ", newflags); // If we have a midpoint set, check to see if the string needs padding if (midpoint > 0 && output.Length < midpoint) - { output += CreatePadding(midpoint - output.Length); - } else - { output += " "; - } // Append the description output += this.Description; @@ -257,20 +218,20 @@ namespace SabreTools.Library.Help // If we have a newline character, reset the line and continue if (split[i].Contains("\n")) { - string[] subsplit = split[i].Replace("\r", "").Split('\n'); + string[] subsplit = split[i].Replace("\r", string.Empty).Split('\n'); for (int j = 0; j < subsplit.Length - 1; j++) { // Add the next word only if the total length doesn't go above the width of the screen if (output.Length + subsplit[j].Length < width) { - output += (output.Length == pre + 4 ? "" : " ") + subsplit[j]; + output += (output.Length == pre + 4 ? string.Empty : " ") + subsplit[j]; } // Otherwise, we want to cache the line to output and create a new blank string else { outputList.Add(output); output = CreatePadding(pre + 4); - output += (output.Length == pre + 4 ? "" : " ") + subsplit[j]; + output += (output.Length == pre + 4 ? string.Empty : " ") + subsplit[j]; } outputList.Add(output); @@ -284,20 +245,20 @@ namespace SabreTools.Library.Help // Add the next word only if the total length doesn't go above the width of the screen if (output.Length + split[i].Length < width) { - output += (output.Length == pre + 4 ? "" : " ") + split[i]; + output += (output.Length == pre + 4 ? string.Empty : " ") + split[i]; } // Otherwise, we want to cache the line to output and create a new blank string else { outputList.Add(output); output = CreatePadding(pre + 4); - output += (output.Length == pre + 4 ? "" : " ") + split[i]; + output += (output.Length == pre + 4 ? string.Empty : " ") + split[i]; } } // Add the last created output and a blank line for clarity outputList.Add(output); - outputList.Add(""); + outputList.Add(string.Empty); } return outputList; @@ -310,11 +271,12 @@ namespace SabreTools.Library.Help /// String with requested number of blank spaces private string CreatePadding(int spaces) { - string blank = ""; + string blank = string.Empty; for (int i = 0; i < spaces; i++) { blank += " "; } + return blank; } @@ -331,7 +293,7 @@ namespace SabreTools.Library.Help List outputList = new List(); // Build the output string first - string output = ""; + string output = string.Empty; // Normalize based on the tab level int preAdjusted = pre; @@ -366,17 +328,13 @@ namespace SabreTools.Library.Help } // Now add all flags - output += String.Join(", ", newflags); + output += string.Join(", ", newflags); // If we have a midpoint set, check to see if the string needs padding if (midpoint > 0 && output.Length < midpointAdjusted) - { output += CreatePadding(midpointAdjusted - output.Length); - } else - { output += " "; - } // Append the description output += this.Description; @@ -400,20 +358,20 @@ namespace SabreTools.Library.Help // If we have a newline character, reset the line and continue if (split[i].Contains("\n")) { - string[] subsplit = split[i].Replace("\r", "").Split('\n'); + string[] subsplit = split[i].Replace("\r", string.Empty).Split('\n'); for (int j = 0; j < subsplit.Length - 1; j++) { // Add the next word only if the total length doesn't go above the width of the screen if (output.Length + subsplit[j].Length < width) { - output += (output.Length == preAdjusted + 4 ? "" : " ") + subsplit[j]; + output += (output.Length == preAdjusted + 4 ? string.Empty : " ") + subsplit[j]; } // Otherwise, we want to cache the line to output and create a new blank string else { outputList.Add(output); output = CreatePadding(preAdjusted + 4); - output += (output.Length == preAdjusted + 4 ? "" : " ") + subsplit[j]; + output += (output.Length == preAdjusted + 4 ? string.Empty : " ") + subsplit[j]; } outputList.Add(output); @@ -427,20 +385,20 @@ namespace SabreTools.Library.Help // Add the next word only if the total length doesn't go above the width of the screen if (output.Length + split[i].Length < width) { - output += (output.Length == preAdjusted + 4 ? "" : " ") + split[i]; + output += (output.Length == preAdjusted + 4 ? string.Empty : " ") + split[i]; } // Otherwise, we want to cache the line to output and create a new blank string else { outputList.Add(output); output = CreatePadding(preAdjusted + 4); - output += (output.Length == preAdjusted + 4 ? "" : " ") + split[i]; + output += (output.Length == preAdjusted + 4 ? string.Empty : " ") + split[i]; } } // Add the last created output and a blank line for clarity outputList.Add(output); - outputList.Add(""); + outputList.Add(string.Empty); } // Now let's append all subfeatures @@ -471,132 +429,148 @@ namespace SabreTools.Library.Help valid = !input.Contains("=") && this.Flags.Contains(input); if (valid) { - _valueBool = true; + _value = true; // If we've already found this feature before if (_foundOnce && !ignore) - { valid = false; - } _foundOnce = true; } + break; + // If we have an Int32, try to parse it if at all possible case FeatureType.Int32: valid = input.Contains("=") && this.Flags.Contains(input.Split('=')[0]); if (valid) { if (!Int32.TryParse(input.Split('=')[1], out int value)) - { value = Int32.MinValue; - } - _valueInt32 = value; + + _value = value; // If we've already found this feature before if (_foundOnce && !ignore) - { valid = false; - } _foundOnce = true; } + break; + // If we have an Int32, try to parse it if at all possible case FeatureType.Int64: valid = input.Contains("=") && this.Flags.Contains(input.Split('=')[0]); if (valid) { if (!Int64.TryParse(input.Split('=')[1], out long value)) - { value = Int64.MinValue; - } - _valueInt64 = value; + + _value = value; // If we've already found this feature before if (_foundOnce && !ignore) - { valid = false; - } _foundOnce = true; } + break; + // If we have an input, make sure it has an equals sign in it case FeatureType.List: valid = input.Contains("=") && this.Flags.Contains(input.Split('=')[0]); if (valid) { - if (_valueList == null) - { - _valueList = new List(); - } + if (_value == null) + _value = new List(); - _valueList.Add(input.Split('=')[1]); + (_value as List).Add(input.Split('=')[1]); } + break; + case FeatureType.String: valid = input.Contains("=") && this.Flags.Contains(input.Split('=')[0]); if (valid) { - _valueString = input.Split('=')[1]; + _value = input.Split('=')[1]; // If we've already found this feature before if (_foundOnce && !ignore) - { valid = false; - } _foundOnce = true; } + break; } // If we haven't found a valid flag and we're not looking for just this feature, check to see if any of the subfeatures are valid if (!valid && !exact) - { - foreach (string feature in this.Features.Keys) - { - valid = this.Features[feature].ValidateInput(input); - - // If we've found a valid feature, we break out - if (valid) - { - break; - } - } - } + valid = this.Features.Keys.Any(k => this.Features[k].ValidateInput(input)); // If we're not valid at this point, we want to check if this flag is a file or a folder if (!valid) - { valid = File.Exists(input) || Directory.Exists(input); - } return valid; } /// - /// Get the proper value associated with this feature + /// Get the boolean value associated with this feature /// - /// Value associated with this feature - public object GetValue() + public bool GetBoolValue() { - switch (_featureType) - { - case FeatureType.Flag: - return _valueBool; - case FeatureType.Int32: - return _valueInt32; - case FeatureType.Int64: - return _valueInt64; - case FeatureType.List: - return _valueList; - case FeatureType.String: - return _valueString; - } + if (_featureType != FeatureType.Flag) + throw new ArgumentException("Feature is not a flag"); - return null; + return (_value as bool?).HasValue ? (_value as bool?).Value : false; + } + + /// + /// Get the string value associated with this feature + /// + public string GetStringValue() + { + if (_featureType != FeatureType.String) + throw new ArgumentException("Feature is not a string"); + + return (_value as string); + } + + /// + /// Get the Int32 value associated with this feature + /// + public int GetInt32Value() + { + if (_featureType != FeatureType.Int32) + throw new ArgumentException("Feature is not an int"); + + return (_value as int?).HasValue ? (_value as int?).Value : int.MinValue; + } + + /// + /// Get the Int64 value associated with this feature + /// + public long GetInt64Value() + { + if (_featureType != FeatureType.Int64) + throw new ArgumentException("Feature is not a long"); + + return (_value as long?).HasValue ? (_value as long?).Value : long.MinValue; + } + + /// + /// Get the List\ value associated with this feature + /// + public List GetListValue() + { + if (_featureType != FeatureType.List) + throw new ArgumentException("Feature is not a list"); + + return (_value as List) ?? new List(); } /// @@ -605,20 +579,18 @@ namespace SabreTools.Library.Help /// True if the feature is enabled, false otherwise public bool IsEnabled() { - object obj = GetValue(); - switch (_featureType) { case FeatureType.Flag: - return (bool)obj; - case FeatureType.Int32: - return (int)obj != Int32.MinValue; - case FeatureType.Int64: - return (long)obj != Int64.MinValue; - case FeatureType.List: - return obj != null; + return (_value as bool?) == true; case FeatureType.String: - return obj != null; + return (_value as string) != null; + case FeatureType.Int32: + return (_value as int?).HasValue && (_value as int?).Value != int.MinValue; + case FeatureType.Int64: + return (_value as long?).HasValue && (_value as long?).Value != long.MinValue; + case FeatureType.List: + return (_value as List) != null; } return false; diff --git a/SabreTools.Library/Help/Help.cs b/SabreTools.Library/Help/Help.cs index 7f22bd35..498276a9 100644 --- a/SabreTools.Library/Help/Help.cs +++ b/SabreTools.Library/Help/Help.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; namespace SabreTools.Library.Help { @@ -98,19 +99,7 @@ namespace SabreTools.Library.Help /// Feature name public string GetFeatureName(string name) { - string feature = ""; - - // Loop through the features - foreach (string featureName in _features.Keys) - { - if (_features[featureName].ValidateInput(name, exact: true, ignore: true)) - { - feature = featureName; - break; - } - } - - return feature; + return _features.Keys.FirstOrDefault(f => _features[f].ValidateInput(name, exact: true, ignore: true)) ?? string.Empty; } /// @@ -132,7 +121,7 @@ namespace SabreTools.Library.Help } // And append the generic ending - output.Add(""); + output.Add(string.Empty); output.Add("For information on available flags, put the option name after help"); // Now write out everything in a staged manner @@ -170,7 +159,7 @@ namespace SabreTools.Library.Help credits.Add(_barrier); credits.Add("Credits"); credits.Add(_barrier); - credits.Add(""); + credits.Add(string.Empty); credits.Add("Programmer / Lead: Matt Nadareski (darksabre76)"); credits.Add("Additional code: emuLOAD, @tractivo, motoschifo"); credits.Add("Testing: emuLOAD, @tractivo, Kludge, Obiwantje, edc"); @@ -218,14 +207,14 @@ namespace SabreTools.Library.Help // If we have a real name found, append all available subflags recursively if (realname != null) { - output.Add("Available options for " + realname + ":"); + output.Add($"Available options for {realname}:"); output.AddRange(_features[realname].OutputRecursive(0, pre: 2, midpoint: 30, includeLongDescription: includeLongDescription)); } // If no name was found but we have possible matches, show them else if (startsWith.Count > 0) { - output.Add("\"" + featurename + "\" not found. Did you mean:"); + output.Add($"\"{featurename}\" not found. Did you mean:"); foreach (string possible in startsWith) { output.AddRange(_features[possible].Output(pre: 2, midpoint: 30, includeLongDescription: includeLongDescription)); @@ -243,19 +232,7 @@ namespace SabreTools.Library.Help /// True if the feature was found, false otherwise public bool TopLevelFlag(string flag) { - bool success = false; - - // Loop through the features and check - foreach (string feature in _features.Keys) - { - if (_features[feature].ValidateInput(flag, exact: true)) - { - success = true; - break; - } - } - - return success; + return _features.Keys.Any(f => _features[f].ValidateInput(flag, exact: true)); } /// @@ -267,7 +244,7 @@ namespace SabreTools.Library.Help Dictionary enabled = new Dictionary(); // Loop through the features - foreach(KeyValuePair feature in _features) + foreach (KeyValuePair feature in _features) { Dictionary temp = GetEnabledSubfeatures(feature.Key, feature.Value); foreach (KeyValuePair tempfeat in temp) @@ -294,9 +271,7 @@ namespace SabreTools.Library.Help // First determine if the current feature is enabled if (feature.IsEnabled()) - { enabled.Add(key, feature); - } // Now loop through the subfeatures recursively foreach (KeyValuePair sub in feature.Features) @@ -305,9 +280,8 @@ namespace SabreTools.Library.Help foreach (KeyValuePair tempfeat in temp) { if (!enabled.ContainsKey(tempfeat.Key)) - { enabled.Add(tempfeat.Key, null); - } + enabled[tempfeat.Key] = tempfeat.Value; } } @@ -337,6 +311,7 @@ namespace SabreTools.Library.Help Pause(); } } + Pause(); } diff --git a/SabreTools.Library/Help/TopLevel.cs b/SabreTools.Library/Help/TopLevel.cs new file mode 100644 index 00000000..e531162e --- /dev/null +++ b/SabreTools.Library/Help/TopLevel.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using SabreTools.Library.Data; +using SabreTools.Library.DatFiles; +using SabreTools.Library.Help; +using SabreTools.Library.Tools; + +namespace SabreTools.Library.Help +{ + /// + /// Represents an actionable top-level feature + /// + public abstract class TopLevel : Feature + { + public List Inputs = new List(); + + /// + /// Process args list based on current feature + /// + public virtual bool ProcessArgs(string[] args, Help help) + { + for (int i = 1; i < args.Length; i++) + { + // Verify that the current flag is proper for the feature + if (!ValidateInput(args[i])) + { + Globals.Logger.Error($"Invalid input detected: {args[i]}"); + help.OutputIndividualFeature(this.Name); + Globals.Logger.Close(); + return false; + } + + // Special precautions for files and directories + if (File.Exists(args[i]) || Directory.Exists(args[i])) + Inputs.Add(args[i]); + } + + return true; + } + + /// + /// Process and extract variables based on current feature + /// + public virtual void ProcessFeatures(Dictionary features) { } + + #region Generic Extraction + + /// + /// Get boolean value from nullable feature + /// + protected bool GetBoolean(Dictionary features, string key) + { + if (!features.ContainsKey(key)) + return false; + + return true; + } + + /// + /// Get int value from nullable feature + /// + protected int GetInt32(Dictionary features, string key) + { + if (!features.ContainsKey(key)) + return Int32.MinValue; + + return features[key].GetInt32Value(); + } + + /// + /// Get long value from nullable feature + /// + protected long GetInt64(Dictionary features, string key) + { + if (!features.ContainsKey(key)) + return Int64.MinValue; + + return features[key].GetInt64Value(); + } + + /// + /// Get list value from nullable feature + /// + protected List GetList(Dictionary features, string key) + { + if (!features.ContainsKey(key)) + return new List(); + + return features[key].GetListValue() ?? new List(); + } + + /// + /// Get string value from nullable feature + /// + protected string GetString(Dictionary features, string key) + { + if (!features.ContainsKey(key)) + return null; + + return features[key].GetStringValue(); + } + + #endregion + } +} diff --git a/SabreTools.Library/README.1ST b/SabreTools.Library/README.1ST index 3cbdec5b..edfbc936 100644 --- a/SabreTools.Library/README.1ST +++ b/SabreTools.Library/README.1ST @@ -239,7 +239,7 @@ Options: tsv - Standardized Tab-Separated Value xml, logiqx - Logiqx XML - -dpc, --depreciated Output 'game' instead of 'machine' + -dpc, --deprecated Output 'game' instead of 'machine' By default, Logiqx XML DATs output with the more modern "machine" tag for each set. This flag allows users to output the older "game" tag instead, for compatibility reasons. [Logiqx only] @@ -323,7 +323,7 @@ Options: -h=, --header= Set a header skipper to use, blank means all Set the header special field for the output DAT(s). In file - rebuilding, this flag allows for either all copier headers (using "") + rebuilding, this flag allows for either all copier headers (using string.Empty) or specific copier headers by name (such as "fds.xml") to determine if a file matches or not. @@ -689,7 +689,7 @@ Options: -h=, --header= Set a header skipper to use, blank means all Set the header special field for the output DAT(s). In file - rebuilding, this flag allows for either all copier headers (using "") + rebuilding, this flag allows for either all copier headers (using string.Empty) or specific copier headers by name (such as "fds.xml") to determine if a file matches or not. @@ -777,7 +777,7 @@ Options: tsv - Standardized Tab-Separated Value xml, logiqx - Logiqx XML - -dpc, --depreciated Output 'game' instead of 'machine' + -dpc, --deprecated Output 'game' instead of 'machine' By default, Logiqx XML DATs output with the more modern "machine" tag for each set. This flag allows users to output the older "game" tag instead, for compatibility reasons. [Logiqx only] @@ -999,7 +999,7 @@ Options: and output for physical files. Where appropriate, Romba depot files will be created as well. - -dpc, --depreciated Output 'game' instead of 'machine' + -dpc, --deprecated Output 'game' instead of 'machine' By default, Logiqx XML DATs output with the more modern "machine" tag for each set. This flag allows users to output the older "game" tag instead, for compatibility reasons. [Logiqx only] @@ -1042,7 +1042,7 @@ Options: -h=, --header= Set a header skipper to use, blank means all Set the header special field for the output DAT(s). In file - rebuilding, this flag allows for either all copier headers (using "") + rebuilding, this flag allows for either all copier headers (using string.Empty) or specific copier headers by name (such as "fds.xml") to determine if a file matches or not. @@ -1508,7 +1508,7 @@ Options: -h=, --header= Set a header skipper to use, blank means all Set the header special field for the output DAT(s). In file - rebuilding, this flag allows for either all copier headers (using "") + rebuilding, this flag allows for either all copier headers (using string.Empty) or specific copier headers by name (such as "fds.xml") to determine if a file matches or not. diff --git a/SabreTools.Library/Reports/BaseReport.cs b/SabreTools.Library/Reports/BaseReport.cs index 2b25007e..f145060c 100644 --- a/SabreTools.Library/Reports/BaseReport.cs +++ b/SabreTools.Library/Reports/BaseReport.cs @@ -1,17 +1,9 @@ using System; +using System.IO; using SabreTools.Library.DatFiles; using SabreTools.Library.Tools; -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using Stream = System.IO.Stream; -using StreamWriter = System.IO.StreamWriter; -#endif - namespace SabreTools.Library.Reports { /// @@ -51,9 +43,7 @@ namespace SabreTools.Library.Reports _datFile = datfile; if (!stream.CanWrite) - { throw new ArgumentException(nameof(stream)); - } _writer = new StreamWriter(stream); _baddumpCol = baddumpCol; diff --git a/SabreTools.Library/Reports/Html.cs b/SabreTools.Library/Reports/Html.cs index f9cca4e0..344d8eda 100644 --- a/SabreTools.Library/Reports/Html.cs +++ b/SabreTools.Library/Reports/Html.cs @@ -1,18 +1,11 @@ using System; +using System.IO; using System.Linq; using System.Net; using SabreTools.Library.DatFiles; using SabreTools.Library.Tools; -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using Stream = System.IO.Stream; -#endif - namespace SabreTools.Library.Reports { /// @@ -52,19 +45,19 @@ namespace SabreTools.Library.Reports public override void Write(long game = -1) { string line = "\t\t\t" + WebUtility.HtmlEncode(_datFile.FileName.Remove(0, 5)) - : ">" + WebUtility.HtmlEncode(_datFile.FileName)) + "" - + "" + Utilities.GetBytesReadable(_datFile.TotalSize) + "" - + "" + (game == -1 ? _datFile.Keys.Count() : game) + "" - + "" + _datFile.RomCount + "" - + "" + _datFile.DiskCount + "" - + "" + _datFile.CRCCount + "" - + "" + _datFile.MD5Count + "" - + "" + _datFile.RIPEMD160Count + "" - + "" + _datFile.SHA1Count + "" - + "" + _datFile.SHA256Count + "" - + (_baddumpCol ? "" + _datFile.BaddumpCount + "" : "") - + (_nodumpCol ? "" + _datFile.NodumpCount + "" : "") + ? $" class=\"dir\">{WebUtility.HtmlEncode(_datFile.FileName.Remove(0, 5))}" + : $">{WebUtility.HtmlEncode(_datFile.FileName)}") + "" + + $"{Utilities.GetBytesReadable(_datFile.TotalSize)}" + + $"{(game == -1 ? _datFile.Keys.Count() : game)}" + + $"{_datFile.RomCount}" + + $"{_datFile.DiskCount}" + + $"{_datFile.CRCCount}" + + $"{_datFile.MD5Count}" + + $"{_datFile.RIPEMD160Count}" + + $"{_datFile.SHA1Count}" + + $"{_datFile.SHA256Count}" + + (_baddumpCol ? $"{_datFile.BaddumpCount}" : string.Empty) + + (_nodumpCol ? $"{_datFile.NodumpCount}" : string.Empty) + "\n"; _writer.Write(line); _writer.Flush(); @@ -93,7 +86,7 @@ namespace SabreTools.Library.Reports

DAT Statistics Report (" + DateTime.Now.ToShortDateString() + @")

- +
"); _writer.Flush(); @@ -106,9 +99,9 @@ namespace SabreTools.Library.Reports /// public override void WriteMidHeader() { - _writer.Write(@" " -+ @"" -+ (_baddumpCol ? "" : "") + (_nodumpCol ? "" : "") + "\n"); + _writer.Write(@" " ++ @"" ++ (_baddumpCol ? "" : string.Empty) + (_nodumpCol ? "" : string.Empty) + "\n"); _writer.Flush(); } diff --git a/SabreTools.Library/Reports/SeparatedValue.cs b/SabreTools.Library/Reports/SeparatedValue.cs index f310cd4f..938c0bbd 100644 --- a/SabreTools.Library/Reports/SeparatedValue.cs +++ b/SabreTools.Library/Reports/SeparatedValue.cs @@ -1,15 +1,8 @@ -using System.Linq; +using System.IO; +using System.Linq; using SabreTools.Library.DatFiles; -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using Stream = System.IO.Stream; -#endif - namespace SabreTools.Library.Reports { /// @@ -65,8 +58,8 @@ namespace SabreTools.Library.Reports + "\"" + _datFile.SHA256Count + "\"{0}" + "\"" + _datFile.SHA384Count + "\"{0}" + "\"" + _datFile.SHA512Count + "\"" - + (_baddumpCol ? "{0}\"" + _datFile.BaddumpCount + "\"" : "") - + (_nodumpCol ? "{0}\"" + _datFile.BaddumpCount + "\"" : "") + + (_baddumpCol ? "{0}\"" + _datFile.BaddumpCount + "\"" : string.Empty) + + (_nodumpCol ? "{0}\"" + _datFile.BaddumpCount + "\"" : string.Empty) + "\n", _separator); _writer.Write(line); @@ -79,7 +72,7 @@ namespace SabreTools.Library.Reports public override void WriteHeader() { _writer.Write(string.Format("\"File Name\"{0}\"Total Size\"{0}\"Games\"{0}\"Roms\"{0}\"Disks\"{0}\"# with CRC\"{0}\"# with MD5\"{0}\"# with SHA-1\"{0}\"# with SHA-256\"" - + (_baddumpCol ? "{0}\"BadDumps\"" : "") + (_nodumpCol ? "{0}\"Nodumps\"" : "") + "\n", _separator)); + + (_baddumpCol ? "{0}\"BadDumps\"" : string.Empty) + (_nodumpCol ? "{0}\"Nodumps\"" : string.Empty) + "\n", _separator)); _writer.Flush(); } diff --git a/SabreTools.Library/Reports/Textfile.cs b/SabreTools.Library/Reports/Textfile.cs index b51cf2ee..109f64a9 100644 --- a/SabreTools.Library/Reports/Textfile.cs +++ b/SabreTools.Library/Reports/Textfile.cs @@ -1,16 +1,9 @@ -using System.Linq; +using System.IO; +using System.Linq; using SabreTools.Library.DatFiles; using SabreTools.Library.Tools; -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using Stream = System.IO.Stream; -#endif - namespace SabreTools.Library.Reports { /// @@ -63,13 +56,10 @@ namespace SabreTools.Library.Reports Roms with SHA-512: " + _datFile.SHA512Count + "\n"; if (_baddumpCol) - { line += " Roms with BadDump status: " + _datFile.BaddumpCount + "\n"; - } + if (_nodumpCol) - { line += " Roms with Nodump status: " + _datFile.NodumpCount + "\n"; - } // For spacing between DATs line += "\n\n"; diff --git a/SabreTools.Library/SabreTools.Library.csproj b/SabreTools.Library/SabreTools.Library.csproj index 03371c77..68665963 100644 --- a/SabreTools.Library/SabreTools.Library.csproj +++ b/SabreTools.Library/SabreTools.Library.csproj @@ -1,54 +1,11 @@ - net462;netstandard2.0 + net462;net472;net48;netcoreapp3.1 win10-x64;win7-x86 - Debug;Release;Mono + Debug;Release AnyCPU;x64 - - - NET_FRAMEWORK - - - - DEBUG;TRACE;MONO - false - - 3 - - - - DEBUG;TRACE;MONO - - false - - - - DEBUG;TRACE - false - - 3 - - - - DEBUG;TRACE - - false - - - - TRACE - true - - 3 - - - - TRACE - - true - @@ -97,10 +54,9 @@ - - + diff --git a/SabreTools.Library/Skippers/Skipper.cs b/SabreTools.Library/Skippers/Skipper.cs index e1c652ac..4d890977 100644 --- a/SabreTools.Library/Skippers/Skipper.cs +++ b/SabreTools.Library/Skippers/Skipper.cs @@ -1,22 +1,12 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Xml; using SabreTools.Library.Data; using SabreTools.Library.Tools; -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using BinaryReader = System.IO.BinaryReader; -using SearchOption = System.IO.SearchOption; -using SeekOrigin = System.IO.SeekOrigin; -using Stream = System.IO.Stream; -#endif - namespace SabreTools.Library.Skippers { public class Skipper @@ -74,11 +64,11 @@ namespace SabreTools.Library.Skippers /// public Skipper() { - Name = ""; - Author = ""; - Version = ""; + Name = string.Empty; + Author = string.Empty; + Version = string.Empty; Rules = new List(); - SourceFile = ""; + SourceFile = string.Empty; } /// @@ -94,18 +84,14 @@ namespace SabreTools.Library.Skippers XmlReader xtr = Utilities.GetXmlTextReader(filename); if (xtr == null) - { return; - } bool valid = false; xtr.MoveToContent(); while (!xtr.EOF) { if (xtr.NodeType != XmlNodeType.Element) - { xtr.Read(); - } switch (xtr.Name.ToLowerInvariant()) { @@ -113,15 +99,19 @@ namespace SabreTools.Library.Skippers valid = true; xtr.Read(); break; + case "name": Name = xtr.ReadElementContentAsString(); break; + case "author": Author = xtr.ReadElementContentAsString(); break; + case "version": Version = xtr.ReadElementContentAsString(); break; + case "rule": // Get the information from the rule first SkipperRule rule = new SkipperRule @@ -145,6 +135,7 @@ namespace SabreTools.Library.Skippers rule.StartOffset = Convert.ToInt64(offset, 16); } } + if (xtr.GetAttribute("end_offset") != null) { string offset = xtr.GetAttribute("end_offset"); @@ -157,6 +148,7 @@ namespace SabreTools.Library.Skippers rule.EndOffset = Convert.ToInt64(offset, 16); } } + if (xtr.GetAttribute("operation") != null) { string operation = xtr.GetAttribute("operation"); @@ -182,9 +174,7 @@ namespace SabreTools.Library.Skippers while (!subreader.EOF) { if (subreader.NodeType != XmlNodeType.Element) - { subreader.Read(); - } // Get the test type SkipperTest test = new SkipperTest @@ -201,18 +191,23 @@ namespace SabreTools.Library.Skippers case "data": test.Type = HeaderSkipTest.Data; break; + case "or": test.Type = HeaderSkipTest.Or; break; + case "xor": test.Type = HeaderSkipTest.Xor; break; + case "and": test.Type = HeaderSkipTest.And; break; + case "file": test.Type = HeaderSkipTest.File; break; + default: subreader.Read(); break; @@ -231,6 +226,7 @@ namespace SabreTools.Library.Skippers test.Offset = Convert.ToInt64(offset, 16); } } + if (subreader.GetAttribute("value") != null) { string value = subreader.GetAttribute("value"); @@ -243,6 +239,7 @@ namespace SabreTools.Library.Skippers test.Value[index] = byte.Parse(byteValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture); } } + if (subreader.GetAttribute("result") != null) { string result = subreader.GetAttribute("result"); @@ -257,6 +254,7 @@ namespace SabreTools.Library.Skippers break; } } + if (subreader.GetAttribute("mask") != null) { string mask = subreader.GetAttribute("mask"); @@ -269,6 +267,7 @@ namespace SabreTools.Library.Skippers test.Mask[index] = byte.Parse(byteValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture); } } + if (subreader.GetAttribute("size") != null) { string size = subreader.GetAttribute("size"); @@ -281,6 +280,7 @@ namespace SabreTools.Library.Skippers test.Size = Convert.ToInt64(size, 16); } } + if (subreader.GetAttribute("operator") != null) { string oper = subreader.GetAttribute("operator"); @@ -340,9 +340,7 @@ namespace SabreTools.Library.Skippers private static void PopulateSkippers() { if (_list == null) - { _list = new List(); - } foreach (string skipperFile in Directory.EnumerateFiles(LocalPath, "*", SearchOption.AllDirectories)) { @@ -362,7 +360,7 @@ namespace SabreTools.Library.Skippers // If the file doesn't exist, return a blank skipper rule if (!File.Exists(input)) { - Globals.Logger.Error("The file '{0}' does not exist so it cannot be tested", input); + Globals.Logger.Error($"The file '{input}' does not exist so it cannot be tested"); return new SkipperRule(); } @@ -383,9 +381,7 @@ namespace SabreTools.Library.Skippers // If we have a null skipper name, we return since we're not matching skippers if (skipperName == null) - { return skipperRule; - } // Loop through and find a Skipper that has the right name Globals.Logger.Verbose("Beginning search for matching header skip rules"); @@ -395,9 +391,9 @@ namespace SabreTools.Library.Skippers foreach (Skipper skipper in tempList) { // If we're searching for the skipper OR we have a match to an inputted one - if (String.IsNullOrWhiteSpace(skipperName) - || (!String.IsNullOrWhiteSpace(skipper.Name) && skipperName.ToLowerInvariant() == skipper.Name.ToLowerInvariant()) - || (!String.IsNullOrWhiteSpace(skipper.Name) && skipperName.ToLowerInvariant() == skipper.SourceFile.ToLowerInvariant())) + if (string.IsNullOrWhiteSpace(skipperName) + || (!string.IsNullOrWhiteSpace(skipper.Name) && skipperName.ToLowerInvariant() == skipper.Name.ToLowerInvariant()) + || (!string.IsNullOrWhiteSpace(skipper.Name) && skipperName.ToLowerInvariant() == skipper.SourceFile.ToLowerInvariant())) { // Loop through the rules until one is found that works BinaryReader br = new BinaryReader(input); @@ -417,17 +413,11 @@ namespace SabreTools.Library.Skippers case HeaderSkipTest.Data: // First seek to the correct position if (test.Offset == null) - { input.Seek(0, SeekOrigin.End); - } else if (test.Offset > 0 && test.Offset <= input.Length) - { input.Seek((long)test.Offset, SeekOrigin.Begin); - } else if (test.Offset < 0 && Math.Abs((long)test.Offset) <= input.Length) - { input.Seek((long)test.Offset, SeekOrigin.End); - } // Then read and compare bytewise result = true; @@ -451,22 +441,17 @@ namespace SabreTools.Library.Skippers // Return if the expected and actual results match success &= (result == test.Result); break; + case HeaderSkipTest.Or: case HeaderSkipTest.Xor: case HeaderSkipTest.And: // First seek to the correct position if (test.Offset == null) - { input.Seek(0, SeekOrigin.End); - } else if (test.Offset > 0 && test.Offset <= input.Length) - { input.Seek((long)test.Offset, SeekOrigin.Begin); - } else if (test.Offset < 0 && Math.Abs((long)test.Offset) <= input.Length) - { input.Seek((long)test.Offset, SeekOrigin.End); - } result = true; try @@ -499,6 +484,7 @@ namespace SabreTools.Library.Skippers // Return if the expected and actual results match success &= (result == test.Result); break; + case HeaderSkipTest.File: // First get the file size from stream long size = input.Length; @@ -534,9 +520,7 @@ namespace SabreTools.Library.Skippers { // If we're not keeping the stream open, dispose of the binary reader if (!keepOpen) - { input.Dispose(); - } Globals.Logger.User(" Matching rule found!"); return rule; @@ -547,15 +531,11 @@ namespace SabreTools.Library.Skippers // If we're not keeping the stream open, dispose of the binary reader if (!keepOpen) - { input.Dispose(); - } // If we have a blank rule, inform the user if (skipperRule.Tests == null) - { Globals.Logger.Verbose("No matching rule found!"); - } return skipperRule; } diff --git a/SabreTools.Library/Skippers/SkipperRule.cs b/SabreTools.Library/Skippers/SkipperRule.cs index 087e3370..8da97ac6 100644 --- a/SabreTools.Library/Skippers/SkipperRule.cs +++ b/SabreTools.Library/Skippers/SkipperRule.cs @@ -1,20 +1,10 @@ using System; using System.Collections.Generic; +using System.IO; using SabreTools.Library.Data; using SabreTools.Library.Tools; -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using BinaryReader = System.IO.BinaryReader; -using BinaryWriter = System.IO.BinaryWriter; -using SeekOrigin = System.IO.SeekOrigin; -using Stream = System.IO.Stream; -#endif - namespace SabreTools.Library.Skippers { public class SkipperRule @@ -61,14 +51,14 @@ namespace SabreTools.Library.Skippers // If the input file doesn't exist, fail if (!File.Exists(input)) { - Globals.Logger.Error("I'm sorry but '{0}' doesn't exist!", input); + Globals.Logger.Error($"I'm sorry but '{input}' doesn't exist!"); return false; } // Create the output directory if it doesn't already Utilities.EnsureOutputDirectory(Path.GetDirectoryName(output)); - Globals.Logger.User("Attempting to apply rule to '{0}'", input); + Globals.Logger.User($"Attempting to apply rule to '{input}'"); success = TransformStream(Utilities.TryOpenRead(input), Utilities.TryCreate(output)); // If the output file has size 0, delete it @@ -114,21 +104,16 @@ namespace SabreTools.Library.Skippers // Seek to the beginning offset if (StartOffset == null) - { success = false; - } + else if (Math.Abs((long)StartOffset) > input.Length) - { success = false; - } + else if (StartOffset > 0) - { input.Seek((long)StartOffset, SeekOrigin.Begin); - } + else if (StartOffset < 0) - { input.Seek((long)StartOffset, SeekOrigin.End); - } // Then read and apply the operation as you go if (success) @@ -154,6 +139,7 @@ namespace SabreTools.Library.Skippers r <<= s; buffer[pos] = (byte)r; break; + case HeaderSkipOperation.Byteswap: if (pos % 2 == 1) { @@ -164,12 +150,15 @@ namespace SabreTools.Library.Skippers buffer[pos + 1] = b; } break; + case HeaderSkipOperation.Wordswap: buffer[3 - pos] = b; break; + case HeaderSkipOperation.WordByteswap: buffer[(pos + 2) % 4] = b; break; + case HeaderSkipOperation.None: default: buffer[pos] = b; @@ -187,6 +176,7 @@ namespace SabreTools.Library.Skippers buffer = new byte[4]; } } + // If there's anything more in the buffer, write only the left bits for (int i = 0; i < pos; i++) { @@ -203,15 +193,11 @@ namespace SabreTools.Library.Skippers { // If we're not keeping the read stream open, dispose of the binary reader if (!keepReadOpen) - { br?.Dispose(); - } // If we're not keeping the write stream open, dispose of the binary reader if (!keepWriteOpen) - { bw?.Dispose(); - } } return success; diff --git a/SabreTools.Library/Tools/DatabaseTools.cs b/SabreTools.Library/Tools/DatabaseTools.cs index 7f09e40e..b950c906 100644 --- a/SabreTools.Library/Tools/DatabaseTools.cs +++ b/SabreTools.Library/Tools/DatabaseTools.cs @@ -1,14 +1,9 @@ -using Mono.Data.Sqlite; -using System; +using System; +using System.IO; using System.Collections.Generic; using SabreTools.Library.Data; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; -#endif +using Mono.Data.Sqlite; namespace SabreTools.Library.Tools { @@ -34,19 +29,16 @@ namespace SabreTools.Library.Tools SqliteConnection dbc = new SqliteConnection(Constants.HeadererConnectionString); dbc.Open(); - string query = @"SELECT * FROM data WHERE sha1='" + SHA1 + "' AND header='" + header + "'"; + string query = $"SELECT * FROM data WHERE sha1='{SHA1}' AND header='{header}'"; SqliteCommand slc = new SqliteCommand(query, dbc); SqliteDataReader sldr = slc.ExecuteReader(); exists = sldr.HasRows; if (!exists) { - query = @"INSERT INTO data (sha1, header, type) VALUES ('" + - SHA1 + "', " + - "'" + header + "', " + - "'" + source + "')"; + query = $"INSERT INTO data (sha1, header, type) VALUES ('{SHA1}', '{header}', '{source}')"; slc = new SqliteCommand(query, dbc); - Globals.Logger.Verbose("Result of inserting header: {0}", slc.ExecuteNonQuery()); + Globals.Logger.Verbose($"Result of inserting header: {slc.ExecuteNonQuery()}"); } // Dispose of database objects @@ -68,9 +60,7 @@ namespace SabreTools.Library.Tools // Make sure the file exists if (!File.Exists(db)) - { SqliteConnection.CreateFile(db); - } // Open the database connection SqliteConnection dbc = new SqliteConnection(connectionString); @@ -174,7 +164,7 @@ CREATE TABLE IF NOT EXISTS data ( // Create the output list of headers List headers = new List(); - string query = @"SELECT header, type FROM data WHERE sha1='" + SHA1 + "'"; + string query = $"SELECT header, type FROM data WHERE sha1='{SHA1}'"; SqliteCommand slc = new SqliteCommand(query, dbc); SqliteDataReader sldr = slc.ExecuteReader(); @@ -182,7 +172,7 @@ CREATE TABLE IF NOT EXISTS data ( { while (sldr.Read()) { - Globals.Logger.Verbose("Found match with rom type '{0}'", sldr.GetString(1)); + Globals.Logger.Verbose($"Found match with rom type '{sldr.GetString(1)}'"); headers.Add(sldr.GetString(0)); } } diff --git a/SabreTools.Library/Tools/Hasher.cs b/SabreTools.Library/Tools/Hasher.cs index 9b307792..f2279aba 100644 --- a/SabreTools.Library/Tools/Hasher.cs +++ b/SabreTools.Library/Tools/Hasher.cs @@ -9,6 +9,9 @@ using SabreTools.Library.Data; namespace SabreTools.Library.Tools { + /// + /// Async hashing class wraper + /// public class Hasher { public Hash HashType { get; private set; } @@ -64,6 +67,9 @@ namespace SabreTools.Library.Tools _hasher.Dispose(); } + /// + /// Process a buffer of some length with the internal hash algorithm + /// public async Task Process(byte[] buffer, int size) { switch (HashType) @@ -86,6 +92,9 @@ namespace SabreTools.Library.Tools } } + /// + /// Finalize the internal hash algorigthm + /// public async Task Finalize() { byte[] emptyBuffer = new byte[0]; @@ -108,6 +117,9 @@ namespace SabreTools.Library.Tools } } + /// + /// Get internal hash as a byte array + /// public byte[] GetHash() { switch (HashType) diff --git a/SabreTools.Library/Tools/InternalStopwatch.cs b/SabreTools.Library/Tools/InternalStopwatch.cs index 97ba3960..feb59bbb 100644 --- a/SabreTools.Library/Tools/InternalStopwatch.cs +++ b/SabreTools.Library/Tools/InternalStopwatch.cs @@ -17,7 +17,7 @@ namespace SabreTools.Library.Tools /// public InternalStopwatch() { - _subject = ""; + _subject = string.Empty; } /// @@ -30,24 +30,13 @@ namespace SabreTools.Library.Tools Start(); } - /// - /// Constructor that initalizes the stopwatch with a subject and starts immediately - /// - /// Subject of the stopwatch - /// Parameters to format the string - public InternalStopwatch(string subject, params object[] more) - { - _subject = string.Format(subject, more); - Start(); - } - /// /// Start the stopwatch and display subject text /// public void Start() { _startTime = DateTime.Now; - Globals.Logger.User("{0}...", _subject); + Globals.Logger.User($"{_subject}..."); } /// @@ -60,23 +49,12 @@ namespace SabreTools.Library.Tools Start(); } - /// - /// Start the stopwatch and display subject text - /// - /// Text to show on stopwatch start - /// Parameters to format the string - public void Start(string subject, params object[] more) - { - _subject = string.Format(subject, more); - Start(); - } - /// /// End the stopwatch and display subject text /// public void Stop() { - Globals.Logger.User("{0} completed in {1}", _subject, DateTime.Now.Subtract(_startTime).ToString(@"hh\:mm\:ss\.fffff")); + Globals.Logger.User($"{_subject} completed in {DateTime.Now.Subtract(_startTime).ToString("hh:mm:ss.fffff")}"); } } } diff --git a/SabreTools.Library/Tools/Logger.cs b/SabreTools.Library/Tools/Logger.cs index 74dbfe29..8ba68f97 100644 --- a/SabreTools.Library/Tools/Logger.cs +++ b/SabreTools.Library/Tools/Logger.cs @@ -1,18 +1,10 @@ using System; +using System.IO; using System.Text; using SabreTools.Library.Data; using SabreTools.Library.Tools; -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using FileStream = System.IO.FileStream; -using StreamWriter = System.IO.StreamWriter; -#endif - namespace SabreTools.Library.Tools { /// @@ -58,13 +50,11 @@ namespace SabreTools.Library.Tools _tofile = tofile; _warnings = false; _errors = false; - _filename = Path.GetFileNameWithoutExtension(filename) + " (" + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss") + ")." + Utilities.GetExtension(filename); + _filename = $"{Path.GetFileNameWithoutExtension(filename)} ({DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss")}).{Utilities.GetExtension(filename)}"; _filter = filter; if (!Directory.Exists(_basepath)) - { Directory.CreateDirectory(_basepath); - } Start(); } @@ -77,9 +67,7 @@ namespace SabreTools.Library.Tools { _start = DateTime.Now; if (!_tofile) - { return true; - } try { @@ -87,8 +75,8 @@ namespace SabreTools.Library.Tools _log = new StreamWriter(logfile, Encoding.UTF8, (int)(4 * Constants.KibiByte), true); _log.AutoFlush = true; - _log.WriteLine("Logging started " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); - _log.WriteLine(string.Format("Command run: {0}", Globals.CommandLineArgs)); + _log.WriteLine($"Logging started {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}"); + _log.WriteLine($"Command run: {Globals.CommandLineArgs}"); } catch { @@ -108,38 +96,31 @@ namespace SabreTools.Library.Tools if (!suppress) { if (_warnings) - { Console.WriteLine("There were warnings in the last run! Check the log for more details"); - } + if (_errors) - { Console.WriteLine("There were errors in the last run! Check the log for more details"); - } TimeSpan span = DateTime.Now.Subtract(_start); // Special case for multi-day runs - string total = ""; + string total = string.Empty; if (span >= TimeSpan.FromDays(1)) - { total = span.ToString(@"d\:hh\:mm\:ss"); - } else - { total = span.ToString(@"hh\:mm\:ss"); - } if (!_tofile) { - Console.WriteLine("Total runtime: " + total); + Console.WriteLine($"Total runtime: {total}"); return true; } try { - _log.WriteLine("Logging ended " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); - _log.WriteLine("Total runtime: " + total); - Console.WriteLine("Total runtime: " + total); + _log.WriteLine($"Logging ended {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}"); + _log.WriteLine($"Total runtime: {total}"); + Console.WriteLine($"Total runtime: {total}"); _log.Close(); } catch @@ -173,15 +154,11 @@ namespace SabreTools.Library.Tools { // If the log level is less than the filter level, we skip it but claim we didn't if (loglevel < _filter) - { return true; - } // USER and ERROR writes to console if (loglevel == LogLevel.USER || loglevel == LogLevel.ERROR) - { - Console.WriteLine((loglevel == LogLevel.ERROR && appendPrefix ? loglevel.ToString() + " " : "") + output); - } + Console.WriteLine((loglevel == LogLevel.ERROR && appendPrefix ? loglevel.ToString() + " " : string.Empty) + output); // If we're writing to file, use the existing stream if (_tofile) @@ -190,7 +167,7 @@ namespace SabreTools.Library.Tools { lock(_lock) { - _log.WriteLine((appendPrefix ? loglevel.ToString() + " - " + DateTime.Now + " - " : "") + output); + _log.WriteLine((appendPrefix ? $"{loglevel} - {DateTime.Now} - " : string.Empty) + output); } } catch (Exception ex) @@ -230,7 +207,7 @@ namespace SabreTools.Library.Tools { lock (_lock) { - _log.Write(DateTime.Now + " - " + output); + _log.Write($"{DateTime.Now} - {output}"); } } catch @@ -243,38 +220,15 @@ namespace SabreTools.Library.Tools return true; } - /// - /// Write the given string as a verbose message to the log output - /// - /// String to be written log - /// Optional arguments for string formatting - /// True if the output could be written, false otherwises - public bool Verbose(string output, params object[] args) - { - return Log(args.Length == 0 ? output: string.Format(output, args), LogLevel.VERBOSE, true); - } - /// /// Write the given string as a verbose message to the log output /// /// String to be written log /// True if the level and datetime should be prepended to each statement (default), false otherwise - /// Optional arguments for string formatting /// True if the output could be written, false otherwise - public bool Verbose(string output, bool appendPrefix = true, params object[] args) + public bool Verbose(string output, bool appendPrefix = true) { - return Log(args.Length == 0 ? output : string.Format(output, args), LogLevel.VERBOSE, appendPrefix); - } - - /// - /// Write the given string as a user message to the log output - /// - /// String to be written log - /// Optional arguments for string formatting - /// True if the output could be written, false otherwise - public bool User(string output, params object[] args) - { - return Log(args.Length == 0 ? output : string.Format(output, args), LogLevel.USER, true); + return Log(output, LogLevel.VERBOSE, appendPrefix); } /// @@ -282,23 +236,10 @@ namespace SabreTools.Library.Tools /// /// String to be written log /// True if the level and datetime should be prepended to each statement (default), false otherwise - /// Optional arguments for string formatting /// True if the output could be written, false otherwise - public bool User(string output, bool appendPrefix = true, params object[] args) + public bool User(string output, bool appendPrefix = true) { - return Log(args.Length == 0 ? output : string.Format(output, args), LogLevel.USER, appendPrefix); - } - - /// - /// Write the given string as a warning to the log output - /// - /// String to be written log - /// Optional arguments for string formatting - /// True if the output could be written, false otherwise - public bool Warning(string output, params object[] args) - { - _warnings = true; - return Log(args.Length == 0 ? output : string.Format(output, args), LogLevel.WARNING, true); + return Log(output, LogLevel.USER, appendPrefix); } /// @@ -306,24 +247,11 @@ namespace SabreTools.Library.Tools /// /// String to be written log /// True if the level and datetime should be prepended to each statement (default), false otherwise - /// Optional arguments for string formatting /// True if the output could be written, false otherwise - public bool Warning(string output, bool appendPrefix = true, params object[] args) + public bool Warning(string output, bool appendPrefix = true) { _warnings = true; - return Log(args.Length == 0 ? output : string.Format(output, args), LogLevel.WARNING, appendPrefix); - } - - /// - /// Writes the given string as an error in the log - /// - /// String to be written log - /// Optional arguments for string formatting - /// True if the output could be written, false otherwise - public bool Error(string output, params object[] args) - { - _errors = true; - return Log(args.Length == 0 ? output : string.Format(output, args), LogLevel.ERROR, true); + return Log(output, LogLevel.WARNING, appendPrefix); } /// @@ -331,12 +259,11 @@ namespace SabreTools.Library.Tools /// /// String to be written log /// True if the level and datetime should be prepended to each statement (default), false otherwise - /// Optional arguments for string formatting /// True if the output could be written, false otherwise - public bool Error(string output, bool appendPrefix = true, params object[] args) + public bool Error(string output, bool appendPrefix = true) { _errors = true; - return Log(args.Length == 0 ? output : string.Format(output, args), LogLevel.ERROR, appendPrefix); + return Log(output, LogLevel.ERROR, appendPrefix); } /// diff --git a/SabreTools.Library/Tools/Utilities.cs b/SabreTools.Library/Tools/Utilities.cs index 6adb6f7a..a9a1b91d 100644 --- a/SabreTools.Library/Tools/Utilities.cs +++ b/SabreTools.Library/Tools/Utilities.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Net; using System.Reflection; @@ -17,26 +18,6 @@ using SabreTools.Library.FileTypes; using SabreTools.Library.Reports; using SabreTools.Library.Skippers; using Compress.ThreadReaders; - -#if MONO -using System.IO; -#else -using Alphaleonis.Win32.Filesystem; - -using BinaryReader = System.IO.BinaryReader; -using BinaryWriter = System.IO.BinaryWriter; -using FileAccess = System.IO.FileAccess; -using FileMode = System.IO.FileMode; -using FileShare = System.IO.FileShare; -using FileStream = System.IO.FileStream; -using IOException = System.IO.IOException; -using MemoryStream = System.IO.MemoryStream; -using PathTooLongException = System.IO.PathTooLongException; -using SearchOption = System.IO.SearchOption; -using SeekOrigin = System.IO.SeekOrigin; -using Stream = System.IO.Stream; -using StreamReader = System.IO.StreamReader; -#endif using NaturalSort; namespace SabreTools.Library.Tools @@ -228,8 +209,8 @@ namespace SabreTools.Library.Tools /// The cleaned name public static string CleanGameName(string[] game) { - game[game.Length - 1] = CleanGameName(game[game.Length - 1]); - string outgame = String.Join(Path.DirectorySeparatorChar.ToString(), game); + game[game.Length - 1] = CleanGameName(game.Last()); + string outgame = string.Join(Path.DirectorySeparatorChar.ToString(), game); outgame = outgame.TrimStart().TrimEnd(); return outgame; } @@ -244,29 +225,22 @@ namespace SabreTools.Library.Tools { // If we have a known blank hash, return blank if (string.IsNullOrWhiteSpace(hash) || hash == "-" || hash == "_") - { - return ""; - } + return string.Empty; // Check to see if it's a "hex" hash - hash = hash.Trim().Replace("0x", ""); + hash = hash.Trim().Replace("0x", string.Empty); // If we have a blank hash now, return blank if (string.IsNullOrWhiteSpace(hash)) - { - return ""; - } + return string.Empty; // If the hash shorter than the required length, pad it if (hash.Length < padding) - { hash = hash.PadLeft(padding, '0'); - } + // If the hash is longer than the required length, it's invalid else if (hash.Length > padding) - { - return ""; - } + return string.Empty; // Now normalize the hash hash = hash.ToLowerInvariant(); @@ -276,7 +250,7 @@ namespace SabreTools.Library.Tools { if ((hash[i] < '0' || hash[i] > '9') && (hash[i] < 'a' || hash[i] > 'f')) { - hash = ""; + hash = string.Empty; break; } } @@ -292,13 +266,10 @@ namespace SabreTools.Library.Tools public static string CleanListromHashData(string hash) { if (hash.StartsWith("CRC")) - { return hash.Substring(4, 8).ToLowerInvariant(); - } + else if (hash.StartsWith("SHA1")) - { return hash.Substring(5, 40).ToLowerInvariant(); - } return hash; } @@ -369,14 +340,14 @@ namespace SabreTools.Library.Tools { "Й", "J" }, { "К", "K" }, { "Л", "L" }, { "М", "M" }, { "Н", "N" }, { "О", "O" }, { "П", "P" }, { "Р", "R" }, { "С", "S" }, { "Т", "T" }, { "У", "U" }, { "Ф", "f" }, { "Х", "Kh" }, { "Ц", "Ts" }, { "Ч", "Ch" }, - { "Ш", "Sh" }, { "Щ", "Sch" }, { "Ъ", "" }, { "Ы", "y" }, { "Ь", "" }, + { "Ш", "Sh" }, { "Щ", "Sch" }, { "Ъ", string.Empty }, { "Ы", "y" }, { "Ь", string.Empty }, { "Э", "e" }, { "Ю", "yu" }, { "Я", "ya" }, { "а", "a" }, { "б", "b" }, { "в", "v" }, { "г", "g" }, { "д", "d" }, { "е", "e" }, { "ё", "yo" }, { "ж", "zh" }, { "з", "z" }, { "и", "i" }, { "й", "j" }, { "к", "k" }, { "л", "l" }, { "м", "m" }, { "н", "n" }, { "о", "o" }, { "п", "p" }, { "р", "r" }, { "с", "s" }, { "т", "t" }, { "у", "u" }, { "ф", "f" }, { "х", "kh" }, { "ц", "ts" }, { "ч", "ch" }, { "ш", "sh" }, { "щ", "sch" }, - { "ъ", "" }, { "ы", "y" }, { "ь", "" }, { "э", "e" }, { "ю", "yu" }, + { "ъ", string.Empty }, { "ы", "y" }, { "ь", string.Empty }, { "э", "e" }, { "ю", "yu" }, { "я", "ya" }, }; @@ -450,29 +421,32 @@ namespace SabreTools.Library.Tools // If we got back null, then it's not an archive, so we we return if (at == null) - { return archive; - } // Create the archive based on the type - Globals.Logger.Verbose("Found archive of type: {0}", at); + Globals.Logger.Verbose($"Found archive of type: {at}"); switch (at) { case FileType.GZipArchive: archive = new GZipArchive(input); break; + case FileType.RarArchive: archive = new RarArchive(input); break; + case FileType.SevenZipArchive: archive = new SevenZipArchive(input); break; + case FileType.TapeArchive: archive = new TapeArchive(input); break; + case FileType.ZipArchive: archive = new ZipArchive(input); break; + default: // We ignore all other types for now break; @@ -492,14 +466,19 @@ namespace SabreTools.Library.Tools { case FileType.GZipArchive: return new GZipArchive(); + case FileType.RarArchive: return new RarArchive(); + case FileType.SevenZipArchive: return new SevenZipArchive(); + case FileType.TapeArchive: return new TapeArchive(); + case FileType.ZipArchive: return new ZipArchive(); + default: return null; } @@ -516,26 +495,38 @@ namespace SabreTools.Library.Tools { case OutputFormat.Folder: return new Folder(); + case OutputFormat.TapeArchive: return new TapeArchive(); + case OutputFormat.Torrent7Zip: return new SevenZipArchive(); + case OutputFormat.TorrentGzip: + case OutputFormat.TorrentGzipRomba: return new GZipArchive(); + case OutputFormat.TorrentLRZip: return new LRZipArchive(); + case OutputFormat.TorrentLZ4: return new LZ4Archive(); + case OutputFormat.TorrentRar: return new RarArchive(); + case OutputFormat.TorrentXZ: return new XZArchive(); + case OutputFormat.TorrentZip: return new ZipArchive(); + case OutputFormat.TorrentZPAQ: return new ZPAQArchive(); + case OutputFormat.TorrentZstd: return new ZstdArchive(); + default: return null; } @@ -555,12 +546,16 @@ namespace SabreTools.Library.Tools { case StatReportFormat.Textfile: return new Textfile(null, filename, baddumpCol, nodumpCol); + case StatReportFormat.CSV: return new Reports.SeparatedValue(null, filename, ',', baddumpCol, nodumpCol); + case StatReportFormat.HTML: return new Html(null, filename, baddumpCol, nodumpCol); + case StatReportFormat.SSV: return new Reports.SeparatedValue(null, filename, ';', baddumpCol, nodumpCol); + case StatReportFormat.TSV: return new Reports.SeparatedValue(null, filename, '\t', baddumpCol, nodumpCol); } @@ -575,18 +570,13 @@ namespace SabreTools.Library.Tools /// Date as a string, if possible public static string GetDate(string input) { - string date = ""; + string date = string.Empty; if (input != null) { - DateTime dateTime = DateTime.Now; - if (DateTime.TryParse(input, out dateTime)) - { + if (DateTime.TryParse(input, out DateTime dateTime)) date = dateTime.ToString(); - } else - { date = input; - } } return date; @@ -616,50 +606,73 @@ namespace SabreTools.Library.Tools { case DatFormat.AttractMode: return new AttractMode(baseDat); + case DatFormat.ClrMamePro: return new ClrMamePro(baseDat); + case DatFormat.CSV: return new DatFiles.SeparatedValue(baseDat, ','); + case DatFormat.DOSCenter: return new DosCenter(baseDat); + case DatFormat.EverdriveSMDB: return new EverdriveSMDB(baseDat); + case DatFormat.Listrom: return new Listrom(baseDat); + case DatFormat.Listxml: return new Listxml(baseDat); + case DatFormat.Logiqx: return new Logiqx(baseDat, false); - case DatFormat.LogiqxDepreciated: + + case DatFormat.LogiqxDeprecated: return new Logiqx(baseDat, true); + case DatFormat.MissFile: return new Missfile(baseDat); + case DatFormat.OfflineList: return new OfflineList(baseDat); + case DatFormat.OpenMSX: return new OpenMSX(baseDat); + case DatFormat.RedumpMD5: return new Hashfile(baseDat, Hash.MD5); + case DatFormat.RedumpRIPEMD160: return new Hashfile(baseDat, Hash.RIPEMD160); + case DatFormat.RedumpSFV: return new Hashfile(baseDat, Hash.CRC); + case DatFormat.RedumpSHA1: return new Hashfile(baseDat, Hash.SHA1); + case DatFormat.RedumpSHA256: return new Hashfile(baseDat, Hash.SHA256); + case DatFormat.RedumpSHA384: return new Hashfile(baseDat, Hash.SHA384); + case DatFormat.RedumpSHA512: return new Hashfile(baseDat, Hash.SHA512); + case DatFormat.RomCenter: return new RomCenter(baseDat); + case DatFormat.SabreDat: return new SabreDat(baseDat); + case DatFormat.SoftwareList: return new SoftwareList(baseDat); + case DatFormat.SSV: return new DatFiles.SeparatedValue(baseDat, ';'); + case DatFormat.TSV: return new DatFiles.SeparatedValue(baseDat, '\t'); } @@ -678,14 +691,19 @@ namespace SabreTools.Library.Tools { case ItemType.Archive: return new Archive(); + case ItemType.BiosSet: return new BiosSet(); + case ItemType.Disk: return new Disk(); + case ItemType.Release: return new Release(); + case ItemType.Sample: return new Sample(); + case ItemType.Rom: default: return new Rom(); @@ -703,6 +721,7 @@ namespace SabreTools.Library.Tools { case FileType.CHD: return new Disk(baseFile); + case FileType.GZipArchive: case FileType.LRZipArchive: case FileType.LZ4Archive: @@ -715,6 +734,7 @@ namespace SabreTools.Library.Tools case FileType.ZPAQArchive: case FileType.ZstdArchive: return new Rom(baseFile); + case FileType.Folder: default: return null; @@ -913,9 +933,6 @@ namespace SabreTools.Library.Tools { switch (forcemerge?.ToLowerInvariant()) { - case "none": - default: - return ForceMerging.None; case "split": return ForceMerging.Split; case "merged": @@ -924,6 +941,9 @@ namespace SabreTools.Library.Tools return ForceMerging.NonMerged; case "full": return ForceMerging.Full; + case "none": + default: + return ForceMerging.None; } } @@ -936,15 +956,15 @@ namespace SabreTools.Library.Tools { switch (forcend?.ToLowerInvariant()) { - case "none": - default: - return ForceNodump.None; case "obsolete": return ForceNodump.Obsolete; case "required": return ForceNodump.Required; case "ignore": return ForceNodump.Ignore; + case "none": + default: + return ForceNodump.None; } } @@ -957,15 +977,15 @@ namespace SabreTools.Library.Tools { switch (forcepack?.ToLowerInvariant()) { - case "none": - default: - return ForcePacking.None; case "yes": case "zip": return ForcePacking.Zip; case "no": case "unzip": return ForcePacking.Unzip; + case "none": + default: + return ForcePacking.None; } } @@ -978,10 +998,6 @@ namespace SabreTools.Library.Tools { switch (status?.ToLowerInvariant()) { - case "none": - case "no": - default: - return ItemStatus.None; case "good": return ItemStatus.Good; case "baddump": @@ -991,6 +1007,10 @@ namespace SabreTools.Library.Tools return ItemStatus.Nodump; case "verified": return ItemStatus.Verified; + case "none": + case "no": + default: + return ItemStatus.None; } } @@ -1029,9 +1049,6 @@ namespace SabreTools.Library.Tools { switch (gametype?.ToLowerInvariant()) { - case "none": - default: - return MachineType.None; case "bios": return MachineType.Bios; case "dev": @@ -1040,6 +1057,9 @@ namespace SabreTools.Library.Tools case "mech": case "mechanical": return MachineType.Mechanical; + case "none": + default: + return MachineType.None; } } @@ -1078,13 +1098,10 @@ namespace SabreTools.Library.Tools { long size = -1; if (input != null && input.Contains("0x")) - { size = Convert.ToInt64(input, 16); - } + else if (input != null) - { Int64.TryParse(input, out size); - } return size; } @@ -1098,9 +1115,6 @@ namespace SabreTools.Library.Tools { switch (forceMerging) { - case ForceMerging.None: - default: - return SplitType.None; case ForceMerging.Split: return SplitType.Split; case ForceMerging.Merged: @@ -1109,6 +1123,9 @@ namespace SabreTools.Library.Tools return SplitType.NonMerged; case ForceMerging.Full: return SplitType.FullNonMerged; + case ForceMerging.None: + default: + return SplitType.None; } } @@ -1240,12 +1257,12 @@ namespace SabreTools.Library.Tools string ext = GetExtension(filename); // Read the input file, if possible - Globals.Logger.Verbose("Attempting to read file to get format: {0}", filename); + Globals.Logger.Verbose($"Attempting to read file to get format: {filename}"); // Check if file exists if (!File.Exists(filename)) { - Globals.Logger.Warning("File '{0}' could not read from!", filename); + Globals.Logger.Warning($"File '{filename}' could not read from!"); return 0; } @@ -1280,11 +1297,11 @@ namespace SabreTools.Library.Tools // Get the first two non-whitespace, non-comment lines to check StreamReader sr = File.OpenText(filename); string first = sr.ReadLine().ToLowerInvariant(); - while (String.IsNullOrWhiteSpace(first) || first.StartsWith("
File NameTotal SizeGamesRomsDisks# with CRC# with MD5# with SHA-1# with SHA-256BaddumpsNodumps
File NameTotal SizeGamesRomsDisks# with CRC# with MD5# with SHA-1# with SHA-256BaddumpsNodumps