diff --git a/SabreTools.Library/DatFiles/DatFile.cs b/SabreTools.Library/DatFiles/DatFile.cs index 4a5183dc..d10cfb96 100644 --- a/SabreTools.Library/DatFiles/DatFile.cs +++ b/SabreTools.Library/DatFiles/DatFile.cs @@ -5717,6 +5717,20 @@ namespace SabreTools.Library.DatFiles outfileNames.Add(DatFormat.SabreDat, CreateOutfileNamesHelper(outDir, ".sd.xml", overwrite)); }; + // Everdrive SMDB + if ((DatFormat & DatFormat.EverdriveSMDB) != 0 + && (DatFormat & DatFormat.AttractMode) == 0 + && (DatFormat & DatFormat.MissFile) == 0) + { + outfileNames.Add(DatFormat.EverdriveSMDB, CreateOutfileNamesHelper(outDir, ".txt", overwrite)); + } + if ((DatFormat & DatFormat.EverdriveSMDB) != 0 + && ((DatFormat & DatFormat.AttractMode) != 0 + || (DatFormat & DatFormat.MissFile) != 0)) + { + outfileNames.Add(DatFormat.SoftwareList, CreateOutfileNamesHelper(outDir, ".smdb.txt", overwrite)); + } + // Software List if ((DatFormat & DatFormat.SoftwareList) != 0 && (DatFormat & DatFormat.Logiqx) == 0 diff --git a/SabreTools.Library/DatFiles/EverdriveSmdb.cs b/SabreTools.Library/DatFiles/EverdriveSmdb.cs new file mode 100644 index 00000000..8409e7a3 --- /dev/null +++ b/SabreTools.Library/DatFiles/EverdriveSmdb.cs @@ -0,0 +1,214 @@ +using System; +using System.Collections.Generic; +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 +{ + /// + /// Represents parsing and writing of an Everdrive SMDB file + /// + internal class EverdriveSMDB : DatFile + { + /// + /// Constructor designed for casting a base DatFile + /// + /// Parent DatFile to copy from + public EverdriveSMDB(DatFile datFile) + : base(datFile, cloneHeader: false) + { + } + + /// + /// Parse an Everdrive SMDB file and return all found games within + /// + /// 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) + public override void ParseFile( + // Standard Dat parsing + string filename, + int sysid, + int srcid, + + // Miscellaneous + bool keep, + bool clean, + bool remUnicode) + { + // Open a file reader + Encoding enc = Utilities.GetEncoding(filename); + StreamReader sr = new StreamReader(Utilities.TryOpenRead(filename), enc); + + while (!sr.EndOfStream) + { + string line = sr.ReadLine(); + + /* + The gameinfo order is as follows + 0 - SHA-256 + 1 - Machine Name/Filename + 2 - SHA-1 + 3 - MD5 + 4 - CRC32 + */ + + string[] gameinfo = line.Split('\t'); + string[] fullname = gameinfo[1].Split('/'); + + Rom rom = new Rom + { + Name = gameinfo[1].Substring(fullname.Length + 1), + Size = Constants.SizeZero, + CRC = gameinfo[4].PadLeft(8, '0'), + MD5 = gameinfo[3].PadLeft(32, '0'), + SHA1 = gameinfo[2].PadLeft(40, '0'), + SHA256 = gameinfo[0].PadLeft(64, '0'), + ItemStatus = ItemStatus.None, + + MachineName = fullname[0], + MachineDescription = fullname[0], + }; + + // Now process and add the rom + ParseAddHelper(rom, clean, remUnicode); + } + + sr.Dispose(); + } + + /// + /// Create and open an output file for writing direct from a dictionary + /// + /// Name of the file to write to + /// True if blank roms should be skipped on output, false otherwise (default) + /// True if the DAT was written correctly, false otherwise + public override bool WriteToFile(string outfile, bool ignoreblanks = false) + { + try + { + Globals.Logger.User("Opening file for writing: {0}", 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); + return false; + } + + StreamWriter sw = new StreamWriter(fs, new UTF8Encoding(false)); + + // Get a properly sorted set of keys + List keys = Keys; + keys.Sort(new NaturalComparer()); + + foreach (string key in keys) + { + List roms = this[key]; + + // Resolve the names in the block + roms = DatItem.ResolveNames(roms); + + for (int index = 0; index < roms.Count; index++) + { + DatItem item = roms[index]; + + // There are apparently times when a null rom can skip by, skip them + if (item.Name == null || item.MachineName == null) + { + Globals.Logger.Warning("Null rom found!"); + continue; + } + + // 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); + + item.Name = (item.Name == "null" ? "-" : item.Name); + ((Rom)item).Size = Constants.SizeZero; + } + + WriteDatItem(sw, item, ignoreblanks); + } + } + + Globals.Logger.Verbose("File written!" + Environment.NewLine); + sw.Dispose(); + fs.Dispose(); + } + catch (Exception ex) + { + Globals.Logger.Error(ex.ToString()); + return false; + } + + return true; + } + + /// + /// Write out Game start using the supplied StreamWriter + /// + /// StreamWriter to output to + /// DatItem object to be output + /// True if the data was written, false on error + private bool WriteDatItem(StreamWriter sw, DatItem rom, 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))) + { + return true; + } + + try + { + // No game should start with a path separator + if (rom.MachineName.StartsWith(Path.DirectorySeparatorChar.ToString())) + rom.MachineName = rom.MachineName.Substring(1); + + // If the DatItem isn't a rom, we don't output it + if (rom.ItemType != ItemType.Rom) + return 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"; + + sw.Write(state); + sw.Flush(); + } + catch (Exception ex) + { + Globals.Logger.Error(ex.ToString()); + return false; + } + + return true; + } + } +} diff --git a/SabreTools.Library/Data/Flags.cs b/SabreTools.Library/Data/Flags.cs index 176dce83..9701e677 100644 --- a/SabreTools.Library/Data/Flags.cs +++ b/SabreTools.Library/Data/Flags.cs @@ -343,6 +343,11 @@ namespace SabreTools.Library.Data /// Listrom = 1 << 15, + /// + /// Everdrive Packs SMDB + /// + EverdriveSMDB = 1 << 16, + #endregion #region SFV-similar Formats @@ -350,32 +355,32 @@ namespace SabreTools.Library.Data /// /// CRC32 hash list /// - RedumpSFV = 1 << 16, + RedumpSFV = 1 << 17, /// /// MD5 hash list /// - RedumpMD5 = 1 << 17, + RedumpMD5 = 1 << 18, /// /// SHA-1 hash list /// - RedumpSHA1 = 1 << 18, + RedumpSHA1 = 1 << 19, /// /// SHA-256 hash list /// - RedumpSHA256 = 1 << 19, + RedumpSHA256 = 1 << 20, /// /// SHA-384 hash list /// - RedumpSHA384 = 1 << 20, + RedumpSHA384 = 1 << 21, /// /// SHA-512 hash list /// - RedumpSHA512 = 1 << 21, + RedumpSHA512 = 1 << 22, #endregion diff --git a/SabreTools.Library/README.1ST b/SabreTools.Library/README.1ST index 4b990700..4c476699 100644 --- a/SabreTools.Library/README.1ST +++ b/SabreTools.Library/README.1ST @@ -228,6 +228,7 @@ Options: sha256 - SHA256 sha384 - SHA384 sha512 - SHA512 + smdb, everdrive - Everdrive SMDB sl, softwarelist - MAME Software List XML ssv - Standardized Semicolon-Separated Value tsv - Standardized Tab-Separated Value @@ -756,6 +757,7 @@ Options: sha256 - SHA256 sha384 - SHA384 sha512 - SHA512 + smdb, everdrive - Everdrive SMDB sl, softwarelist - MAME Software List XML ssv - Standardized Semicolon-Separated Value tsv - Standardized Tab-Separated Value @@ -928,6 +930,7 @@ Options: sha256 - SHA256 sha384 - SHA384 sha512 - SHA512 + smdb, everdrive - Everdrive SMDB sl, softwarelist - MAME Software List XML ssv - Standardized Semicolon-Separated Value tsv - Standardized Tab-Separated Value @@ -1832,6 +1835,7 @@ This section contains remappings from old flag names to new ones for the purpose -osha256, --output-sha256 -> -ot=sha256, --output-type=sha256 -osha384, --output-sha384 -> -ot=sha384, --output-type=sha384 -osha512, --output-sha512 -> -ot=sha512, --output-type=sha512 +-osmdb, --output-everdrive -> -ot=smdb, --output-type=everdrive -osl, --output-sl -> -ot=sl, --output-type=softwarelist -osl, --output-softwarelist -> -ot=sl, --output-type=softwarelist -ossv, --output-ssv -> -ot=ssv, --output-type=ssv diff --git a/SabreTools.Library/SabreTools.Library.csproj b/SabreTools.Library/SabreTools.Library.csproj index 0d6522a4..5460d906 100644 --- a/SabreTools.Library/SabreTools.Library.csproj +++ b/SabreTools.Library/SabreTools.Library.csproj @@ -92,6 +92,7 @@ + diff --git a/SabreTools.Library/Tools/Utilities.cs b/SabreTools.Library/Tools/Utilities.cs index 762efd15..9f011b27 100644 --- a/SabreTools.Library/Tools/Utilities.cs +++ b/SabreTools.Library/Tools/Utilities.cs @@ -39,3219 +39,3206 @@ using NaturalSort; namespace SabreTools.Library.Tools { - /// - /// Static utility functions used throughout the library - /// - public static class Utilities - { - #region BinaryReader Extensions - - /// - /// Reads the specified number of bytes from the stream, starting from a specified point in the byte array. - /// - /// The buffer to read data into. - /// The starting point in the buffer at which to begin reading into the buffer. - /// The number of bytes to read. - /// The number of bytes read into buffer. This might be less than the number of bytes requested if that many bytes are not available, or it might be zero if the end of the stream is reached. - public static int ReadReverse(this BinaryReader reader, byte[] buffer, int index, int count) - { - int retval = reader.Read(buffer, index, count); - buffer = buffer.Reverse().ToArray(); - return retval; - } - - /// - /// Reads the specified number of characters from the stream, starting from a specified point in the character array. - /// - /// The buffer to read data into. - /// The starting point in the buffer at which to begin reading into the buffer. - /// The number of characters to read. - /// The total number of characters read into the buffer. This might be less than the number of characters requested if that many characters are not currently available, or it might be zero if the end of the stream is reached. - public static int ReadReverse(this BinaryReader reader, char[] buffer, int index, int count) - - { - int retval = reader.Read(buffer, index, count); - buffer = buffer.Reverse().ToArray(); - return retval; - } - - /// - /// Reads the specified number of bytes from the current stream into a byte array and advances the current position by that number of bytes. - /// - /// The number of bytes to read. This value must be 0 or a non-negative number or an exception will occur. - /// A byte array containing data read from the underlying stream. This might be less than the number of bytes requested if the end of the stream is reached. - public static byte[] ReadBytesReverse(this BinaryReader reader, int count) - { - byte[] retval = reader.ReadBytes(count); - retval = retval.Reverse().ToArray(); - return retval; - } - - /// - /// Reads a decimal value from the current stream and advances the current position of the stream by sixteen bytes. - /// - /// A decimal value read from the current stream. - public static decimal ReadDecimalReverse(this BinaryReader reader) - { - byte[] retval = reader.ReadBytes(16); - retval = retval.Reverse().ToArray(); - - int i1 = BitConverter.ToInt32(retval, 0); - int i2 = BitConverter.ToInt32(retval, 4); - int i3 = BitConverter.ToInt32(retval, 8); - int i4 = BitConverter.ToInt32(retval, 12); - - return new decimal(new int[] { i1, i2, i3, i4 }); - } - - /// - /// eads an 8-byte floating point value from the current stream and advances the current position of the stream by eight bytes. - /// - /// An 8-byte floating point value read from the current stream. - public static double ReadDoubleReverse(this BinaryReader reader) - { - byte[] retval = reader.ReadBytes(8); - retval = retval.Reverse().ToArray(); - return BitConverter.ToDouble(retval, 0); - } - - /// - /// Reads a 2-byte signed integer from the current stream and advances the current position of the stream by two bytes. - /// - /// A 2-byte signed integer read from the current stream. - public static short ReadInt16Reverse(this BinaryReader reader) - { - byte[] retval = reader.ReadBytes(2); - retval = retval.Reverse().ToArray(); - return BitConverter.ToInt16(retval, 0); - } - - /// - /// Reads a 4-byte signed integer from the current stream and advances the current position of the stream by four bytes. - /// - /// A 4-byte signed integer read from the current stream. - public static int ReadInt32Reverse(this BinaryReader reader) - { - byte[] retval = reader.ReadBytes(4); - retval = retval.Reverse().ToArray(); - return BitConverter.ToInt32(retval, 0); - } - - /// - /// Reads an 8-byte signed integer from the current stream and advances the current position of the stream by eight bytes. - /// - /// An 8-byte signed integer read from the current stream. - public static long ReadInt64Reverse(this BinaryReader reader) - { - byte[] retval = reader.ReadBytes(8); - retval = retval.Reverse().ToArray(); - return BitConverter.ToInt64(retval, 0); - } - - /// - /// Reads a 4-byte floating point value from the current stream and advances the current position of the stream by four bytes. - /// - /// A 4-byte floating point value read from the current stream. - public static float ReadSingleReverse(this BinaryReader reader) - { - byte[] retval = reader.ReadBytes(4); - retval = retval.Reverse().ToArray(); - return BitConverter.ToSingle(retval, 0); - } - - /// - /// Reads a 2-byte unsigned integer from the current stream using little-endian encoding and advances the position of the stream by two bytes. - /// - /// This API is not CLS-compliant. - /// - /// A 2-byte unsigned integer read from this stream. - public static ushort ReadUInt16Reverse(this BinaryReader reader) - { - byte[] retval = reader.ReadBytes(2); - retval = retval.Reverse().ToArray(); - return BitConverter.ToUInt16(retval, 0); - } - - /// - /// Reads a 4-byte unsigned integer from the current stream and advances the position of the stream by four bytes. - /// - /// This API is not CLS-compliant. - /// - /// A 4-byte unsigned integer read from this stream. - public static uint ReadUInt32Reverse(this BinaryReader reader) - { - byte[] retval = reader.ReadBytes(4); - retval = retval.Reverse().ToArray(); - return BitConverter.ToUInt32(retval, 0); - } - - /// - /// Reads an 8-byte unsigned integer from the current stream and advances the position of the stream by eight bytes. - /// - /// This API is not CLS-compliant. - /// - /// An 8-byte unsigned integer read from this stream. - public static ulong ReadUInt64Reverse(this BinaryReader reader) - { - byte[] retval = reader.ReadBytes(8); - retval = retval.Reverse().ToArray(); - return BitConverter.ToUInt64(retval, 0); - } - - #endregion - - #region DAT Cleaning - - /// - /// Clean a game (or rom) name to the WoD standard - /// - /// Name of the game to be cleaned - /// The cleaned name - public static string CleanGameName(string game) - { - ///Run the name through the filters to make sure that it's correct - game = NormalizeChars(game); - game = RussianToLatin(game); - game = SearchPattern(game); - - game = new Regex(@"(([[(].*[\)\]] )?([^([]+))").Match(game).Groups[1].Value; - game = game.TrimStart().TrimEnd(); - return game; - } - - /// - /// Clean a game (or rom) name to the WoD standard - /// - /// Array representing the path to be cleaned - /// 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); - outgame = outgame.TrimStart().TrimEnd(); - return outgame; - } - - /// - /// Clean a hash string and pad to the correct size - /// - /// Hash string to sanitize - /// Amount of characters to pad to - /// Cleaned string - public static string CleanHashData(string hash, int padding) - { - // If we have a known blank hash, return blank - if (string.IsNullOrWhiteSpace(hash) || hash == "-" || hash == "_") - { - return ""; - } - - // Check to see if it's a "hex" hash - hash = hash.Trim().Replace("0x", ""); - - // If we have a blank hash now, return blank - if (string.IsNullOrWhiteSpace(hash)) - { - return ""; - } - - // 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 ""; - } - - // Now normalize the hash - hash = hash.ToLowerInvariant(); - - // Otherwise, make sure that every character is a proper match - for (int i = 0; i < hash.Length; i++) - { - if ((hash[i] < '0' || hash[i] > '9') && (hash[i] < 'a' || hash[i] > 'f')) - { - hash = ""; - break; - } - } - - return hash; - } - - /// - /// Clean a hash string from a Listrom DAT - /// - /// Hash string to sanitize - /// Cleaned string - 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; - } - - /// - /// Replace accented characters - /// - /// String to be parsed - /// String with characters replaced - public static string NormalizeChars(string input) - { - string[,] charmap = { - { "Á", "A" }, { "á", "a" }, - { "À", "A" }, { "à", "a" }, - { "Â", "A" }, { "â", "a" }, - { "Ä", "Ae" }, { "ä", "ae" }, - { "Ã", "A" }, { "ã", "a" }, - { "Å", "A" }, { "å", "a" }, - { "Æ", "Ae" }, { "æ", "ae" }, - { "Ç", "C" }, { "ç", "c" }, - { "Ð", "D" }, { "ð", "d" }, - { "É", "E" }, { "é", "e" }, - { "È", "E" }, { "è", "e" }, - { "Ê", "E" }, { "ê", "e" }, - { "Ë", "E" }, { "ë", "e" }, - { "ƒ", "f" }, - { "Í", "I" }, { "í", "i" }, - { "Ì", "I" }, { "ì", "i" }, - { "Î", "I" }, { "î", "i" }, - { "Ï", "I" }, { "ï", "i" }, - { "Ñ", "N" }, { "ñ", "n" }, - { "Ó", "O" }, { "ó", "o" }, - { "Ò", "O" }, { "ò", "o" }, - { "Ô", "O" }, { "ô", "o" }, - { "Ö", "Oe" }, { "ö", "oe" }, - { "Õ", "O" }, { "õ", "o" }, - { "Ø", "O" }, { "ø", "o" }, - { "Š", "S" }, { "š", "s" }, - { "ß", "ss" }, - { "Þ", "B" }, { "þ", "b" }, - { "Ú", "U" }, { "ú", "u" }, - { "Ù", "U" }, { "ù", "u" }, - { "Û", "U" }, { "û", "u" }, - { "Ü", "Ue" }, { "ü", "ue" }, - { "ÿ", "y" }, - { "Ý", "Y" }, { "ý", "y" }, - { "Ž", "Z" }, { "ž", "z" }, - }; - - for (int i = 0; i < charmap.GetLength(0); i++) - { - input = input.Replace(charmap[i, 0], charmap[i, 1]); - } - - return input; - } - - /// - /// Convert Cyrillic lettering to Latin lettering - /// - /// String to be parsed - /// String with characters replaced - public static string RussianToLatin(string input) - { - string[,] charmap = { - { "А", "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" }, { "Я", "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" }, - { "я", "ya" }, - }; - - for (int i = 0; i < charmap.GetLength(0); i++) - { - input = input.Replace(charmap[i, 0], charmap[i, 1]); - } - - return input; - } - - /// - /// Replace special characters and patterns - /// - /// String to be parsed - /// String with characters replaced - public static string SearchPattern(string input) - { - string[,] charmap = { - { @"~", " - " }, - { @"_", " " }, - { @":", " " }, - { @">", ")" }, - { @"<", "(" }, - { @"\|", "-" }, - { "\"", "'" }, - { @"\*", "." }, - { @"\\", "-" }, - { @"/", "-" }, - { @"\?", " " }, - { @"\(([^)(]*)\(([^)]*)\)([^)(]*)\)", " " }, - { @"\(([^)]+)\)", " " }, - { @"\[([^]]+)\]", " " }, - { @"\{([^}]+)\}", " " }, - { @"(ZZZJUNK|ZZZ-UNK-|ZZZ-UNK |zzz unknow |zzz unk |Copy of |[.][a-z]{3}[.][a-z]{3}[.]|[.][a-z]{3}[.])", " " }, - { @" (r|rev|v|ver)\s*[\d\.]+[^\s]*", " " }, - { @"(( )|(\A))(\d{6}|\d{8})(( )|(\Z))", " " }, - { @"(( )|(\A))(\d{1,2})-(\d{1,2})-(\d{4}|\d{2})", " " }, - { @"(( )|(\A))(\d{4}|\d{2})-(\d{1,2})-(\d{1,2})", " " }, - { @"[-]+", "-" }, - { @"\A\s*\)", " " }, - { @"\A\s*(,|-)", " " }, - { @"\s+", " " }, - { @"\s+,", "," }, - { @"\s*(,|-)\s*\Z", " " }, - }; - - for (int i = 0; i < charmap.GetLength(0); i++) - { - input = Regex.Replace(input, charmap[i, 0], charmap[i, 1]); - } - - return input; - } - - #endregion - - #region Factories - - /// - /// Create an archive of the specified type, if possible - /// - /// Name of the file to create the archive from - /// Archive object representing the inputs - public static BaseArchive GetArchive(string input) - { - BaseArchive archive = null; - - // First get the archive type - FileType? at = GetFileType(input); - - // 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); - 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; - } - - return archive; - } - - /// - /// Create an archive of the specified type, if possible - /// - /// SharpCompress.Common.ArchiveType representing the archive to create - /// Archive object representing the inputs - public static BaseArchive GetArchive(FileType archiveType) - { - switch (archiveType) - { - 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; - } - } - - /// - /// Create an archive of the specified type, if possible - /// - /// SabreTools.Library.Data.OutputFormat representing the archive to create - /// Archive object representing the inputs - public static Folder GetArchive(OutputFormat outputFormat) - { - switch (outputFormat) - { - case OutputFormat.Folder: - return new Folder(); - case OutputFormat.TapeArchive: - return new TapeArchive(); - case OutputFormat.Torrent7Zip: - return new SevenZipArchive(); - case OutputFormat.TorrentGzip: - 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; - } - } - - /// - /// Create a specific type of BaseReport to be used based on a format and user inputs - /// - /// Format of the Statistics Report to be created - /// Name of the file to write out to - /// True if baddumps should be included in output, false otherwise - /// True if nodumps should be included in output, false otherwise - /// BaseReport of the specific internal type that corresponds to the inputs - public static BaseReport GetBaseReport(StatReportFormat statReportFormat, string filename, bool baddumpCol, bool nodumpCol) - { - switch (statReportFormat) - { - 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); - } - - return null; - } - - /// - /// Get a sanitized Date from an input string - /// - /// String to get value from - /// Date as a string, if possible - public static string GetDate(string input) - { - string date = ""; - if (input != null) - { - DateTime dateTime = DateTime.Now; - if (DateTime.TryParse(input, out dateTime)) - { - date = dateTime.ToString(); - } - else - { - date = input; - } - } - - return date; - } - - /// - /// Create a specific type of DatFile to be used based on an input file and a base DAT - /// - /// Name of the file to determine the DAT format from - /// DatFile containing the information to use in specific operations - /// DatFile of the specific internal type that corresponds to the inputs - public static DatFile GetDatFile(string input, DatFile baseDat) - { - DatFormat datFormat = GetDatFormatFromFile(input); - return GetDatFile(datFormat, baseDat); - } - - /// - /// Create a specific type of DatFile to be used based on a format and a base DAT - /// - /// Format of the DAT to be created - /// DatFile containing the information to use in specific operations - /// DatFile of the specific internal type that corresponds to the inputs - public static DatFile GetDatFile(DatFormat datFormat, DatFile baseDat) - { - switch (datFormat) - { - 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.Listrom: - return new Listrom(baseDat); - case DatFormat.Listxml: - return new Listxml(baseDat); - case DatFormat.Logiqx: - return new Logiqx(baseDat, false); - case DatFormat.LogiqxDepreciated: - 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.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'); - } - - return null; - } - - /// - /// Create a specific type of DatItem to be used based on an ItemType - /// - /// Type of the DatItem to be created - /// DatItem of the specific internal type that corresponds to the inputs - public static DatItem GetDatItem(ItemType itemType) - { - switch (itemType) - { - 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(); - } - } - - /// - /// Create a specific type of DatItem to be used based on a BaseFile - /// - /// BaseFile containing information to be created - /// DatItem of the specific internal type that corresponds to the inputs - public static DatItem GetDatItem(BaseFile baseFile) - { - switch (baseFile.Type) - { - case FileType.CHD: - return new Disk(baseFile); - case FileType.GZipArchive: - case FileType.LRZipArchive: - case FileType.LZ4Archive: - case FileType.None: - case FileType.RarArchive: - case FileType.SevenZipArchive: - case FileType.TapeArchive: - case FileType.XZArchive: - case FileType.ZipArchive: - case FileType.ZPAQArchive: - case FileType.ZstdArchive: - return new Rom(baseFile); - case FileType.Folder: - default: - return null; - } - } - - /// - /// Get DatFormat value from input string - /// - /// String to get value from - /// DatFormat value corresponding to the string - public static DatFormat GetDatFormat(string input) - { - switch (input?.Trim().ToLowerInvariant()) - { - case "all": - return DatFormat.ALL; - case "am": - case "attractmode": - return DatFormat.AttractMode; - case "cmp": - case "clrmamepro": - return DatFormat.ClrMamePro; - case "csv": - return DatFormat.CSV; - case "dc": - case "doscenter": - return DatFormat.DOSCenter; - case "lr": - case "listrom": - return DatFormat.Listrom; - case "lx": - case "listxml": - return DatFormat.Listxml; - case "md5": - return DatFormat.RedumpMD5; - case "miss": - case "missfile": - return DatFormat.MissFile; - case "msx": - case "openmsx": - return DatFormat.OpenMSX; - case "ol": - case "offlinelist": - return DatFormat.OfflineList; - case "rc": - case "romcenter": - return DatFormat.RomCenter; - case "sd": - case "sabredat": - return DatFormat.SabreDat; - case "sfv": - return DatFormat.RedumpSFV; - case "sha1": - return DatFormat.RedumpSHA1; - case "sha256": - return DatFormat.RedumpSHA256; - case "sha384": - return DatFormat.RedumpSHA384; - case "sha512": - return DatFormat.RedumpSHA512; - case "sl": - case "softwarelist": - return DatFormat.SoftwareList; - case "ssv": - return DatFormat.SSV; - case "tsv": - return DatFormat.TSV; - case "xml": - case "logiqx": - return DatFormat.Logiqx; - default: - return 0x0; - } - } - - /// - /// Get Field value from input string - /// - /// String to get value from - /// Field value corresponding to the string - public static Field GetField(string input) - { - switch (input?.ToLowerInvariant()) - { - case "areaname": - return Field.AreaName; - case "areasize": - return Field.AreaSize; - case "bios": - return Field.Bios; - case "board": - return Field.Board; - case "cloneof": - return Field.CloneOf; - case "comment": - return Field.Comment; - case "crc": - return Field.CRC; - case "default": - return Field.Default; - case "date": - return Field.Date; - case "description": - return Field.Description; - case "devices": - return Field.Devices; - case "features": - return Field.Features; - case "gamename": - case "machinename": - return Field.MachineName; - case "gametype": - case "machinetype": - return Field.MachineType; - case "index": - return Field.Index; - case "infos": - return Field.Infos; - case "language": - return Field.Language; - case "manufacturer": - return Field.Manufacturer; - case "md5": - return Field.MD5; - case "merge": - return Field.Merge; - case "name": - return Field.Name; - case "offset": - return Field.Offset; - case "optional": - return Field.Optional; - case "partinterface": - return Field.PartInterface; - case "partname": - return Field.PartName; - case "publisher": - return Field.Publisher; - case "rebuildto": - return Field.RebuildTo; - case "region": - return Field.Region; - case "romof": - return Field.RomOf; - case "runnable": - return Field.Runnable; - case "sampleof": - return Field.SampleOf; - case "sha1": - return Field.SHA1; - case "sha256": - return Field.SHA256; - case "sha384": - return Field.SHA384; - case "sha512": - return Field.SHA512; - case "size": - return Field.Size; - case "slotoptions": - return Field.SlotOptions; - case "sourcefile": - return Field.SourceFile; - case "status": - return Field.Status; - case "supported": - return Field.Supported; - case "writable": - return Field.Writable; - case "year": - return Field.Year; - default: - return Field.NULL; - } - } - - /// - /// Get ForceMerging value from input string - /// - /// String to get value from - /// ForceMerging value corresponding to the string - public static ForceMerging GetForceMerging(string forcemerge) - { - switch (forcemerge?.ToLowerInvariant()) - { - case "none": - default: - return ForceMerging.None; - case "split": - return ForceMerging.Split; - case "merged": - return ForceMerging.Merged; - case "nonmerged": - return ForceMerging.NonMerged; - case "full": - return ForceMerging.Full; - } - } - - /// - /// Get ForceNodump value from input string - /// - /// String to get value from - /// ForceNodump value corresponding to the string - public static ForceNodump GetForceNodump(string forcend) - { - switch (forcend?.ToLowerInvariant()) - { - case "none": - default: - return ForceNodump.None; - case "obsolete": - return ForceNodump.Obsolete; - case "required": - return ForceNodump.Required; - case "ignore": - return ForceNodump.Ignore; - } - } - - /// - /// Get ForcePacking value from input string - /// - /// String to get value from - /// ForcePacking value corresponding to the string - public static ForcePacking GetForcePacking(string forcepack) - { - switch (forcepack?.ToLowerInvariant()) - { - case "none": - default: - return ForcePacking.None; - case "yes": - case "zip": - return ForcePacking.Zip; - case "no": - case "unzip": - return ForcePacking.Unzip; - } - } - - /// - /// Get ItemStatus value from input string - /// - /// String to get value from - /// ItemStatus value corresponding to the string - public static ItemStatus GetItemStatus(string status) - { - switch (status?.ToLowerInvariant()) - { - case "none": - case "no": - default: - return ItemStatus.None; - case "good": - return ItemStatus.Good; - case "baddump": - return ItemStatus.BadDump; - case "nodump": - case "yes": - return ItemStatus.Nodump; - case "verified": - return ItemStatus.Verified; - } - } - - /// - /// Get ItemType? value from input string - /// - /// String to get value from - /// ItemType? value corresponding to the string - public static ItemType? GetItemType(string itemType) - { - switch (itemType?.ToLowerInvariant()) - { - case "archive": - return ItemType.Archive; - case "biosset": - return ItemType.BiosSet; - case "disk": - return ItemType.Disk; - case "release": - return ItemType.Release; - case "rom": - return ItemType.Rom; - case "sample": - return ItemType.Sample; - default: - return null; - } - } - - /// - /// Get MachineType value from input string - /// - /// String to get value from - /// MachineType value corresponding to the string - public static MachineType GetMachineType(string gametype) - { - switch (gametype?.ToLowerInvariant()) - { - case "none": - default: - return MachineType.None; - case "bios": - return MachineType.Bios; - case "dev": - case "device": - return MachineType.Device; - case "mech": - case "mechanical": - return MachineType.Mechanical; - } - } - - /// - /// Get StatReportFormat value from input string - /// - /// String to get value from - /// StatReportFormat value corresponding to the string - public static StatReportFormat GetStatFormat(string input) - { - switch (input?.Trim().ToLowerInvariant()) - { - case "all": - return StatReportFormat.All; - case "csv": - return StatReportFormat.CSV; - case "html": - return StatReportFormat.HTML; - case "ssv": - return StatReportFormat.SSV; - case "text": - return StatReportFormat.Textfile; - case "tsv": - return StatReportFormat.TSV; - default: - return 0x0; - } - } - - /// - /// Get a sanitized size from an input string - /// - /// String to get value from - /// Size as a long, if possible - public static long GetSize(string input) - { - 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; - } - - /// - /// Get SplitType value from input ForceMerging - /// - /// ForceMerging to get value from - /// SplitType value corresponding to the string - public static SplitType GetSplitType(ForceMerging forceMerging) - { - switch (forceMerging) - { - case ForceMerging.None: - default: - return SplitType.None; - case ForceMerging.Split: - return SplitType.Split; - case ForceMerging.Merged: - return SplitType.Merged; - case ForceMerging.NonMerged: - return SplitType.NonMerged; - case ForceMerging.Full: - return SplitType.FullNonMerged; - } - } - - /// - /// Get bool value from input string - /// - /// String to get value from - /// Bool corresponding to the string - public static bool? GetYesNo(string yesno) - { - switch (yesno?.ToLowerInvariant()) - { - case "yes": - return true; - case "no": - return false; - default: - return null; - } - } - - #endregion - - #region File Information - - /// - /// Get the archive scan level based on the inputs - /// - /// User-defined scan level for 7z archives - /// User-defined scan level for GZ archives - /// User-defined scan level for RAR archives - /// User-defined scan level for Zip archives - /// ArchiveScanLevel representing the levels - public static ArchiveScanLevel GetArchiveScanLevelFromNumbers(int sevenzip, int gzip, int rar, int zip) - { - ArchiveScanLevel archiveScanLevel = 0x0000; - - // 7z - sevenzip = (sevenzip < 0 || sevenzip > 2 ? 0 : sevenzip); - switch (sevenzip) - { - case 0: - archiveScanLevel |= ArchiveScanLevel.SevenZipBoth; - break; - case 1: - archiveScanLevel |= ArchiveScanLevel.SevenZipInternal; - break; - case 2: - archiveScanLevel |= ArchiveScanLevel.SevenZipExternal; - break; - } - - // GZip - gzip = (gzip < 0 || gzip > 2 ? 0 : gzip); - switch (gzip) - { - case 0: - archiveScanLevel |= ArchiveScanLevel.GZipBoth; - break; - case 1: - archiveScanLevel |= ArchiveScanLevel.GZipInternal; - break; - case 2: - archiveScanLevel |= ArchiveScanLevel.GZipExternal; - break; - } - - // RAR - rar = (rar < 0 || rar > 2 ? 0 : rar); - switch (rar) - { - case 0: - archiveScanLevel |= ArchiveScanLevel.RarBoth; - break; - case 1: - archiveScanLevel |= ArchiveScanLevel.RarInternal; - break; - case 2: - archiveScanLevel |= ArchiveScanLevel.RarExternal; - break; - } - - // Zip - zip = (zip < 0 || zip > 2 ? 0 : zip); - switch (zip) - { - case 0: - archiveScanLevel |= ArchiveScanLevel.ZipBoth; - break; - case 1: - archiveScanLevel |= ArchiveScanLevel.ZipInternal; - break; - case 2: - archiveScanLevel |= ArchiveScanLevel.ZipExternal; - break; - } - - return archiveScanLevel; - } - - /// - /// Get internal metadata from a CHD - /// - /// Filename of possible CHD - /// A CHDFile object with internal SHA-1 on success, null otherwise - /// - /// Original code had a "writable" param. This is not required for metadata checking - /// - public static BaseFile GetCHDInfo(string input) - { - FileStream fs = TryOpenRead(input); - BaseFile chd = GetCHDInfo(fs); - fs.Dispose(); - return chd; - } - - /// - /// Get what type of DAT the input file is - /// - /// Name of the file to be parsed - /// The DatFormat corresponding to the DAT - public static DatFormat GetDatFormatFromFile(string filename) - { - // Limit the output formats based on extension - if (!HasValidDatExtension(filename)) - { - return 0; - } - - // Get the extension from the filename - string ext = GetExtension(filename); - - // Read the input file, if possible - Globals.Logger.Verbose("Attempting to read file to get format: {0}", filename); - - // Check if file exists - if (!File.Exists(filename)) - { - Globals.Logger.Warning("File '{0}' could not read from!", filename); - return 0; - } - - // Some formats should only require the extension to know - switch (ext) - { - case "csv": - return DatFormat.CSV; - case "md5": - return DatFormat.RedumpMD5; - case "sfv": - return DatFormat.RedumpSFV; - case "sha1": - return DatFormat.RedumpSHA1; - case "sha256": - return DatFormat.RedumpSHA256; - case "sha384": - return DatFormat.RedumpSHA384; - case "sha512": - return DatFormat.RedumpSHA512; - case "ssv": - return DatFormat.SSV; - case "tsv": - return DatFormat.TSV; - } - - // For everything else, we need to read it - try - { - // 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("