/// <summary> /// Get what type of DAT the input file is /// </summary> /// <param name="filename">Name of the file to be parsed</param> /// <returns>The DatFormat corresponding to the DAT</returns> public static DatFormat GetDatFormat(this string filename) { // Limit the output formats based on extension if (!PathExtensions.HasValidDatExtension(filename)) return 0; // Get the extension from the filename string ext = PathExtensions.GetNormalizedExtension(filename); // Read the input file, if possible logger.Verbose($"Attempting to read file to get format: {filename}"); // Check if file exists if (!File.Exists(filename)) { logger.Warning($"File '{filename}' could not read from!"); return 0; } // Some formats should only require the extension to know switch (ext) { case "csv": return DatFormat.CSV; case "json": return DatFormat.SabreJSON; case "md5": return DatFormat.RedumpMD5; #if NET_FRAMEWORK case "ripemd160": return DatFormat.RedumpRIPEMD160; #endif 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 "spamsum": return DatFormat.RedumpSpamSum; 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, if possible string first = string.Empty, second = string.Empty; try { using (StreamReader sr = File.OpenText(filename)) { first = sr.ReadLine().ToLowerInvariant(); while ((string.IsNullOrWhiteSpace(first) || first.StartsWith("<!--")) && !sr.EndOfStream) { first = sr.ReadLine().ToLowerInvariant(); } if (!sr.EndOfStream) { second = sr.ReadLine().ToLowerInvariant(); while (string.IsNullOrWhiteSpace(second) || second.StartsWith("<!--") && !sr.EndOfStream) { second = sr.ReadLine().ToLowerInvariant(); } } } } catch { } // If we have an XML-based DAT if (first.Contains("<?xml") && first.Contains("?>")) { if (second.StartsWith("<!doctype datafile")) return DatFormat.Logiqx; else if (second.StartsWith("<!doctype mame") || second.StartsWith("<!doctype m1") || second.StartsWith("<mame") || second.StartsWith("<m1")) return DatFormat.Listxml; else if (second.StartsWith("<!doctype softwaredb")) return DatFormat.OpenMSX; else if (second.StartsWith("<!doctype softwarelist")) return DatFormat.SoftwareList; else if (second.StartsWith("<!doctype sabredat")) return DatFormat.SabreXML; else if ((second.StartsWith("<dat") && !second.StartsWith("<datafile")) || second.StartsWith("<?xml-stylesheet")) return DatFormat.OfflineList; // Older and non-compliant DATs else return DatFormat.Logiqx; } // If we have an SMDB (SHA-256, Filename, SHA-1, MD5, CRC32) else if (Regex.IsMatch(first, @"[0-9a-f]{64}\t.*?\t[0-9a-f]{40}\t[0-9a-f]{32}\t[0-9a-f]{8}")) return DatFormat.EverdriveSMDB; // If we have an INI-based DAT else if (first.Contains("[") && first.Contains("]")) return DatFormat.RomCenter; // If we have a listroms DAT else if (first.StartsWith("roms required for driver")) return DatFormat.Listrom; // If we have a CMP-based DAT else if (first.Contains("clrmamepro")) return DatFormat.ClrMamePro; else if (first.Contains("romvault")) return DatFormat.ClrMamePro; else if (first.Contains("doscenter")) return DatFormat.DOSCenter; else if (first.Contains("#Name;Title;Emulator;CloneOf;Year;Manufacturer;Category;Players;Rotation;Control;Status;DisplayCount;DisplayType;AltRomname;AltTitle;Extra")) return DatFormat.AttractMode; else return DatFormat.ClrMamePro; } catch (Exception ex) { logger.Warning(ex, $"An exception occurred trying to figure out the format of '{filename}'"); return 0; } }
/// <summary> /// Returns the file type of an input file /// </summary> /// <param name="input">Input file to check</param> /// <returns>FileType of inputted file (null on error)</returns> public static FileType? GetFileType(this string input) { FileType? outFileType = null; // If the file is null, then we have no archive type if (input == null) return outFileType; // First line of defense is going to be the extension, for better or worse if (!PathExtensions.HasValidArchiveExtension(input)) return outFileType; // 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(); // Now try to match it to a known signature if (magic.StartsWith(Constants.SevenZipSignature)) { outFileType = FileType.SevenZipArchive; } else if (magic.StartsWith(Constants.AaruFormatSignature)) { outFileType = FileType.AaruFormat; } else if (magic.StartsWith(Constants.CHDSignature)) { outFileType = FileType.CHD; } else if (magic.StartsWith(Constants.GzSignature)) { outFileType = FileType.GZipArchive; } else if (magic.StartsWith(Constants.LRZipSignature)) { outFileType = FileType.LRZipArchive; } else if (magic.StartsWith(Constants.LZ4Signature) || magic.StartsWith(Constants.LZ4SkippableMinSignature) || magic.StartsWith(Constants.LZ4SkippableMaxSignature)) { outFileType = FileType.LZ4Archive; } else if (magic.StartsWith(Constants.RarSignature) || magic.StartsWith(Constants.RarFiveSignature)) { outFileType = FileType.RarArchive; } else if (magic.StartsWith(Constants.TarSignature) || magic.StartsWith(Constants.TarZeroSignature)) { outFileType = FileType.TapeArchive; } else if (magic.StartsWith(Constants.XZSignature)) { outFileType = FileType.XZArchive; } else if (magic.StartsWith(Constants.ZipSignature) || magic.StartsWith(Constants.ZipSignatureEmpty) || magic.StartsWith(Constants.ZipSignatureSpanned)) { outFileType = FileType.ZipArchive; } else if (magic.StartsWith(Constants.ZPAQSignature)) { outFileType = FileType.ZPAQArchive; } else if (magic.StartsWith(Constants.ZstdSignature)) { outFileType = FileType.ZstdArchive; } } catch (Exception ex) { logger.Warning(ex, $"An exception occurred determining file type of '{input}'"); } return outFileType; }