using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.IO; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using System.Web; using DamienG.Security.Cryptography; namespace SabreTools { /* TODO: Add the following flags: Remove MD5 Remove SHA1 Set forceunpacking Zips as files Old style DATs Set all of the fields in the DAT header Auto set Name and Description from current folder Version and date from current date */ class DATFromDir { // Path-related variables private static string _7zPath; private static string _basePath; private static string _tempDir; // Extraction and listing related variables private static char _delim; private static string _baseExtract; private static ProcessStartInfo _psi; private static List> _roms; // User specified variables private static bool _remMD5; private static bool _remSHA1; private static bool _forceunzip; private static bool _allfiles; private static bool _old; private static string _name; private static string _desc; private static string _cat; // Other required variables private static string _version = DateTime.Now.ToString("yyyyMMddHHmmss"); static void Main(string[] args) { // First things first, take care of all of the arguments that this could have _remMD5 = false; _remSHA1 = false; _forceunzip = false; _allfiles = false; _old = false; _name = ""; _desc = ""; _cat = "SabreTools Dir2DAT"; List inputs = new List(); foreach (string arg in args) { switch (arg) { case "-m": case "--noMD5": _remMD5 = true; break; case "-s": case "--noSHA1": _remSHA1 = true; break; case "-u": case "--unzip": _forceunzip = true; break; case "-f": case "--files": _allfiles = true; break; case "-o": case "--old": _old = true; break; default: if (arg.StartsWith("-n=") || arg.StartsWith("--name=")) { _name = arg.Split('=')[1]; } else if (arg.StartsWith("-d=") || arg.StartsWith("--desc=")) { _desc = arg.Split('=')[1]; } else if (arg.StartsWith("-c=") || arg.StartsWith("--cat=")) { _cat = arg.Split('=')[1]; } else { inputs.Add(arg); } break; } } // Determine the deliminator that is to be used if (Environment.CurrentDirectory.Contains("\\")) { _delim = '\\'; } else { _delim = '/'; } // Set local paths and vars _7zPath = Environment.CurrentDirectory + _delim + "7z" + (Environment.Is64BitOperatingSystem ? _delim + "x64" : "") + _delim; _tempDir = Environment.CurrentDirectory + _delim + "temp" + DateTime.Now.ToString("yyyyMMddHHmmss") + _delim; _basePath = (args.Length == 0 ? Environment.CurrentDirectory + _delim : (File.Exists(args[0]) ? args[0] : args[0] + _delim)); _name = (_name == "" ? _basePath.Split(_delim).Last() : _name); _desc = (_desc == "" ? _name + " (" + _version + ")" : _desc); // Set base arguments to be used _baseExtract = "x -o\"" + _tempDir + "\""; // Set the basic Process information for 7za _psi = new ProcessStartInfo { Arguments = "", FileName = _7zPath + "7za.exe", RedirectStandardError = true, RedirectStandardOutput = true, UseShellExecute = false, }; // Get an output array going that has the right mappings (parent, name, size, hash) _roms = new List>(); // This is where the main loop would go if (File.Exists(_basePath)) { ProcessFile(_basePath); } else { foreach (string item in Directory.GetFiles(_basePath, "*", SearchOption.AllDirectories)) { ProcessFile(item); } } // Order the roms by name of parent, then name of rom _roms.Sort(delegate (Tuple A, Tuple B) { if (A.Item1 == B.Item1) { if (A.Item2 == B.Item2) { return (int)(A.Item3 - B.Item3); } return String.Compare(A.Item2, B.Item2); } return String.Compare(A.Item1, B.Item1); }); //TODO: So, this below section is a pretty much one for one copy of code that is written in generate // this means that in the future, "writing to DAT" will be abstracted out to the DLL so that any // properly formatted data can be passed in and it will get written as necessary. That would open // the possibiliites for different ways to generate a DAT from multiple things // Now write it all out as a DAT try { FileStream fs = File.Create(_desc + ".xml"); StreamWriter sw = new StreamWriter(fs, Encoding.UTF8); string header = "\n" + "\n\n" + "\t\n" + "\t\t
\n" + "\t\t\t" + _name + "\n" + "\t\t\t" + _desc + "\n" + "\t\t\tSabreTools Dir2DAT\n" + "\t\t\t" + _version + "\n" + "\t\t\t" + _version + "\n" + "\t\t\tDarksabre76\n" + "\t\t\t\n" + "\t\t
\n"; // Write the header out sw.Write(header); // Write out each of the machines and roms string lastgame = ""; foreach (Tuple rom in _roms) { string state = ""; if (lastgame != "" && lastgame != rom.Item1) { state += "\t\n"; } if (lastgame != rom.Item1) { state += "\t\n" + "\t\t" + HttpUtility.HtmlEncode(rom.Item1) + "\n"; } state += "\t\t\n"; lastgame = rom.Item1; sw.Write(state); } sw.Write("\t\n
"); Console.Write("File written!"); sw.Close(); fs.Close(); } catch (Exception ex) { Console.Write(ex.ToString()); } } private static void ProcessFile (string item) { // Create the temporary output directory Directory.CreateDirectory(_tempDir); _psi.Arguments = _baseExtract + " " + item; Process zip = Process.Start(_psi); zip.WaitForExit(); bool encounteredErrors = zip.StandardError.ReadToEnd().Contains("ERROR"); // Get a list of files including size and hashes Crc32 crc = new Crc32(); MD5 md5 = MD5.Create(); SHA1 sha1 = SHA1.Create(); // If the file was an archive and was extracted successfully, check it if (!encounteredErrors) { foreach (string entry in Directory.GetFiles(_tempDir, "*", SearchOption.AllDirectories)) { string fileCRC = String.Empty; string fileMD5 = String.Empty; string fileSHA1 = String.Empty; try { using (FileStream fs = File.Open(entry, FileMode.Open)) { foreach (byte b in crc.ComputeHash(fs)) { fileCRC += b.ToString("x2").ToLower(); } } using (FileStream fs = File.Open(entry, FileMode.Open)) { fileMD5 = BitConverter.ToString(md5.ComputeHash(fs)).Replace("-", ""); } using (FileStream fs = File.Open(entry, FileMode.Open)) { fileSHA1 = BitConverter.ToString(sha1.ComputeHash(fs)).Replace("-", ""); } } catch (IOException) { continue; } _roms.Add(new Tuple( Path.GetFileNameWithoutExtension(item), entry.Remove(0, _tempDir.Length), (new FileInfo(entry)).Length, fileCRC, fileMD5, fileSHA1)); Console.WriteLine("File parsed: " + entry.Remove(0, _tempDir.Length)); } } // Otherwise, just get the info on the file itself else if (!Directory.Exists(item) && File.Exists(item)) { string fileCRC = String.Empty; string fileMD5 = String.Empty; string fileSHA1 = String.Empty; try { using (FileStream fs = File.Open(item, FileMode.Open)) { foreach (byte b in crc.ComputeHash(fs)) { fileCRC += b.ToString("x2").ToLower(); } } using (FileStream fs = File.Open(item, FileMode.Open)) { fileMD5 = BitConverter.ToString(md5.ComputeHash(fs)).Replace("-", ""); } using (FileStream fs = File.Open(item, FileMode.Open)) { fileSHA1 = BitConverter.ToString(sha1.ComputeHash(fs)).Replace("-", ""); } string actualroot = (item == _basePath ? "Default" : Path.GetDirectoryName(item.Remove(0, _basePath.Length)).Split(_delim)[0]); actualroot = (actualroot == "" ? "Default" : actualroot); string actualitem = (item == _basePath ? item : item.Remove(0, _basePath.Length).Remove(0, (actualroot != "Default" ? actualroot.Length + 1 : 0))); _roms.Add(new Tuple( actualroot, actualitem, (new FileInfo(item)).Length, fileCRC, fileMD5, fileSHA1)); Console.WriteLine("File parsed: " + item.Remove(0, _basePath.Length)); } catch (IOException) { } } // Delete the temp directory if (Directory.Exists(_tempDir)) { Directory.Delete(_tempDir, true); } } } }