using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Xml;
using System.Xml.Schema;
using SabreTools.Library.Data;
using SabreTools.Library.DatFiles;
using SabreTools.Library.DatItems;
using SabreTools.Library.External;
using SabreTools.Library.FileTypes;
using SabreTools.Library.Reports;
using SabreTools.Library.Skippers;
#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;
using SharpCompress.Common;
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
ArchiveType? at = GetArchiveType(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 ArchiveType.GZip:
archive = new GZipArchive(input);
break;
case ArchiveType.Rar:
archive = new RarArchive(input);
break;
case ArchiveType.SevenZip:
archive = new SevenZipArchive(input);
break;
case ArchiveType.Tar:
archive = new TapeArchive(input);
break;
case ArchiveType.Zip:
archive = new TorrentZipArchive(input);
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(ArchiveType archiveType)
{
switch (archiveType)
{
case ArchiveType.GZip:
return new GZipArchive();
case ArchiveType.Rar:
return new RarArchive();
case ArchiveType.SevenZip:
return new SevenZipArchive();
case ArchiveType.Tar:
return new TapeArchive();
case ArchiveType.Zip:
return new TorrentZipArchive();
default:
return null;
}
}
///
/// Create an archive of the specified type, if possible
///
/// SabreTools.Library.Data.SharpCompress.OutputFormat representing the archive to create
/// Archive object representing the inputs
public static BaseArchive 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 TorrentZipArchive();
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.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.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();
}
}
///
/// 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?.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 "miss":
case "missfile":
return DatFormat.MissFile;
case "md5":
return DatFormat.RedumpMD5;
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 "xml":
case "logiqx":
return DatFormat.Logiqx;
default:
return 0x0;
}
}
///
/// 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 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 Disk object with internal SHA-1 on success, null on error, empty Disk otherwise
///
/// Original code had a "writable" param. This is not required for metadata checking
///
public static DatItem GetCHDInfo(string input)
{
FileStream fs = TryOpenRead(input);
DatItem datItem = GetCHDInfo(fs);
fs.Dispose();
return datItem;
}
///
/// Returns the archive type of an input file
///
/// Input file to check
/// ArchiveType of inputted file (null on error)
public static ArchiveType? GetArchiveType(string input)
{
ArchiveType? outtype = null;
// If the file is null, then we have no archive type
if (input == null)
{
return outtype;
}
// First line of defense is going to be the extension, for better or worse
if (!HasValidArchiveExtension(input))
{
return outtype;
}
// Read the first bytes of the file and get the magic number
try
{
byte[] magic = new byte[8];
BinaryReader br = new BinaryReader(TryOpenRead(input));
magic = br.ReadBytes(8);
br.Dispose();
// Convert it to an uppercase string
string mstr = string.Empty;
for (int i = 0; i < magic.Length; i++)
{
mstr += BitConverter.ToString(new byte[] { magic[i] });
}
mstr = mstr.ToUpperInvariant();
// Now try to match it to a known signature
if (mstr.StartsWith(Constants.SevenZipSig))
{
outtype = ArchiveType.SevenZip;
}
else if (mstr.StartsWith(Constants.GzSig))
{
outtype = ArchiveType.GZip;
}
else if (mstr.StartsWith(Constants.RarSig) || mstr.StartsWith(Constants.RarFiveSig))
{
outtype = ArchiveType.Rar;
}
else if (mstr.StartsWith(Constants.TarSig) || mstr.StartsWith(Constants.TarZeroSig))
{
outtype = ArchiveType.Tar;
}
else if (mstr.StartsWith(Constants.ZipSig) || mstr.StartsWith(Constants.ZipSigEmpty) || mstr.StartsWith(Constants.ZipSigSpanned))
{
outtype = ArchiveType.Zip;
}
}
catch (Exception)
{
// Don't log file open errors
}
return outtype;
}
///
/// 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("