diff --git a/Deheader/Headerer.cs b/Deheader/Headerer.cs index 4045f417..5cf66ec0 100644 --- a/Deheader/Headerer.cs +++ b/Deheader/Headerer.cs @@ -22,7 +22,7 @@ namespace SabreTools /// Start deheader operation with supplied parameters /// /// String array representing command line parameters - static void Main(string[] args) + public static void Main(string[] args) { // If output is being redirected, don't allow clear screens if (!Console.IsOutputRedirected) @@ -132,111 +132,77 @@ namespace SabreTools /// Name of the file to be parsed private static void DetectRemoveHeader(string file) { - // Open the file in read mode - BinaryReader br = new BinaryReader(File.OpenRead(file)); - - // Extract the first 1024 bytes of the file - byte[] hbin = br.ReadBytes(1024); - string header = BitConverter.ToString(hbin).Replace("-", string.Empty); - - // Determine the type of the file from the header, if possible - HeaderType type = HeaderType.None; - int headerSize = 0; - - // Loop over the header types and see if there's a match - foreach (HeaderType test in Enum.GetValues(typeof(HeaderType))) - { - Dictionary tempDict = new Dictionary(); - - // Try populating the dictionary from the master list - try - { - tempDict = Remapping.HeaderMaps[test.ToString()]; - } - catch - { - logger.Warning("The mapping for '" + test.ToString() + "' cannot be found!"); - } - - // Loop over the dictionary and see if there are matches - foreach (KeyValuePair entry in tempDict) - { - if (Regex.IsMatch(header, entry.Key)) - { - type = test; - headerSize = entry.Value; - break; - } - } - - // If we found something, break out - if (type != HeaderType.None) - { - break; - } - } + // First get the HeaderType, if any + int headerSize = -1; + HeaderType type = RomTools.GetFileHeaderType(file, out headerSize, logger); + // If we have a valid HeaderType, remove the correct byte count logger.User("File has header: " + (type != HeaderType.None)); - if (type != HeaderType.None) { logger.Log("Deteched header type: " + type); - int hs = headerSize; - // Save header as string in the database - string realhead = ""; - for (int i = 0; i < hs; i++) + // Now take care of the header and new output file + string hstr = string.Empty; + using (BinaryReader br = new BinaryReader(File.OpenRead(file))) { - realhead += BitConverter.ToString(new byte[] { hbin[i] }); + // Extract the header as a string for the database + byte[] hbin = br.ReadBytes(headerSize); + for (int i = 0; i < headerSize; i++) + { + hstr += BitConverter.ToString(new byte[] { hbin[i] }); + } } - // Get the bytes that aren't from the header from the extracted bit so they can be written before the rest of the file - hbin = hbin.Skip(hs).ToArray(); - - // Write out the new file + // Write out the remaining bytes to new file logger.User("Creating unheadered file: " + file + ".new"); - BinaryWriter bw = new BinaryWriter(File.OpenWrite(file + ".new")); - FileInfo fi = new FileInfo(file); - bw.Write(hbin); - bw.Write(br.ReadBytes((int)fi.Length - hs)); - bw.Close(); + Output.RemoveBytesFromFile(file, file + ".new", headerSize, 0); logger.User("Unheadered file created!"); // Now add the information to the database if it's not already there - SHA1 sha1 = SHA1.Create(); - sha1.ComputeHash(File.ReadAllBytes(file + ".new")); - bool exists = false; + RomData rom = RomTools.GetSingleFileInfo(file + ".new"); + AddHeaderToDatabase(hstr, rom.SHA1, type); + } + } - string query = @"SELECT * FROM data WHERE sha1='" + BitConverter.ToString(sha1.Hash) + "' AND header='" + realhead + "'"; + /// + /// Add a header to the database + /// + /// String representing the header bytes + /// SHA-1 of the deheadered file + /// HeaderType representing the detected header + private static void AddHeaderToDatabase(string header, string SHA1, HeaderType type) + { + bool exists = false; + + string query = @"SELECT * FROM data WHERE sha1='" + SHA1 + "' AND header='" + header + "'"; + using (SqliteConnection dbc = new SqliteConnection(_connectionString)) + { + dbc.Open(); + using (SqliteCommand slc = new SqliteCommand(query, dbc)) + { + using (SqliteDataReader sldr = slc.ExecuteReader()) + { + exists = sldr.HasRows; + } + } + } + + if (!exists) + { + query = @"INSERT INTO data (sha1, header, type) VALUES ('" + + SHA1 + "', " + + "'" + header + "', " + + "'" + type.ToString() + "')"; using (SqliteConnection dbc = new SqliteConnection(_connectionString)) { dbc.Open(); using (SqliteCommand slc = new SqliteCommand(query, dbc)) { - using (SqliteDataReader sldr = slc.ExecuteReader()) - { - exists = sldr.HasRows; - } - } - } - - if (!exists) - { - query = @"INSERT INTO data (sha1, header, type) VALUES ('" + - BitConverter.ToString(sha1.Hash) + "', " + - "'" + realhead + "', " + - "'" + type.ToString() + "')"; - using (SqliteConnection dbc = new SqliteConnection(_connectionString)) - { - dbc.Open(); - using (SqliteCommand slc = new SqliteCommand(query, dbc)) - { - logger.Log("Result of inserting header: " + slc.ExecuteNonQuery()); - } + logger.Log("Result of inserting header: " + slc.ExecuteNonQuery()); } } } - br.Close(); } /// @@ -246,14 +212,12 @@ namespace SabreTools private static void ReplaceHeader(string file) { // First, get the SHA-1 hash of the file - SHA1 sha1 = SHA1.Create(); - sha1.ComputeHash(File.ReadAllBytes(file)); - string hash = BitConverter.ToString(sha1.Hash); + RomData rom = RomTools.GetSingleFileInfo(file); // Then try to pull the corresponding headers from the database string header = ""; - string query = @"SELECT header, type FROM data WHERE sha1='" + hash + "'"; + string query = @"SELECT header, type FROM data WHERE sha1='" + rom.SHA1 + "'"; using (SqliteConnection dbc = new SqliteConnection(_connectionString)) { dbc.Open(); @@ -270,15 +234,7 @@ namespace SabreTools header = sldr.GetString(0); logger.User("Creating reheadered file: " + file + ".new" + sub); - BinaryWriter bw = new BinaryWriter(File.OpenWrite(file + ".new" + sub)); - - // Source: http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa - for (int i = 0; i < header.Length; i += 2) - { - bw.Write(Convert.ToByte(header.Substring(i, 2), 16)); - } - bw.Write(File.ReadAllBytes(file)); - bw.Close(); + Output.AppendBytesToFile(file, file + ".new" + sub, header, string.Empty); logger.User("Reheadered file created!"); } } diff --git a/SabreTools.Helper/Tools/Output.cs b/SabreTools.Helper/Tools/Output.cs index e311a3f6..b0b75899 100644 --- a/SabreTools.Helper/Tools/Output.cs +++ b/SabreTools.Helper/Tools/Output.cs @@ -584,5 +584,116 @@ namespace SabreTools.Helper catch { } } } + + /// + /// Remove an arbitrary number of bytes from the inputted file + /// + /// File to be cropped + /// Outputted file + /// Bytes to be removed from head of file + /// Bytes to be removed from tail of file + public static void RemoveBytesFromFile(string input, string output, long bytesToRemoveFromHead, long bytesToRemoveFromTail) + { + // If any of the inputs are invalid, skip + if (!File.Exists(input) || new FileInfo(input).Length <= (bytesToRemoveFromHead + bytesToRemoveFromTail)) + { + return; + } + + // Read the input file and write to the fail + using (BinaryReader br = new BinaryReader(File.OpenRead(input))) + using (BinaryWriter bw = new BinaryWriter(File.OpenWrite(output))) + { + int bufferSize = 1024; + long adjustedLength = br.BaseStream.Length - bytesToRemoveFromTail; + + // Seek to the correct position + br.BaseStream.Seek(bytesToRemoveFromHead, SeekOrigin.Begin); + + // Now read the file in chunks and write out + byte[] buffer = new byte[bufferSize]; + while (br.BaseStream.Position <= (adjustedLength - bufferSize)) + { + buffer = br.ReadBytes(bufferSize); + bw.Write(buffer); + } + + // For the final chunk, if any, write out only that number of bytes + int length = (int)(adjustedLength - br.BaseStream.Position); + buffer = new byte[length]; + buffer = br.ReadBytes(length); + bw.Write(buffer); + } + } + + /// + /// Add an aribtrary number of bytes to the inputted file + /// + /// File to be appended to + /// Outputted file + /// String representing bytes to be added to head of file + /// String representing bytes to be added to tail of file + public static void AppendBytesToFile(string input, string output, string bytesToAddToHead, string bytesToAddToTail) + { + // Source: http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa + byte[] bytesToAddToHeadArray = new byte[bytesToAddToHead.Length / 2]; + for (int i = 0; i < bytesToAddToHead.Length; i += 2) + { + bytesToAddToHeadArray[i/2] = Convert.ToByte(bytesToAddToHead.Substring(i, 2), 16); + } + byte[] bytesToAddToTailArray = new byte[bytesToAddToTail.Length / 2]; + for (int i = 0; i < bytesToAddToTail.Length; i += 2) + { + bytesToAddToTailArray[i / 2] = Convert.ToByte(bytesToAddToTail.Substring(i, 2), 16); + } + + AppendBytesToFile(input, output, bytesToAddToHeadArray, bytesToAddToTailArray); + } + + /// + /// Add an aribtrary number of bytes to the inputted file + /// + /// File to be appended to + /// Outputted file + /// Bytes to be added to head of file + /// Bytes to be added to tail of file + public static void AppendBytesToFile(string input, string output, byte[] bytesToAddToHead, byte[] bytesToAddToTail) + { + // If any of the inputs are invalid, skip + if (!File.Exists(input)) + { + return; + } + + using (BinaryReader br = new BinaryReader(File.OpenRead(input))) + using (BinaryWriter bw = new BinaryWriter(File.OpenWrite(output))) + { + if (bytesToAddToHead.Count() > 0) + { + bw.Write(bytesToAddToHead); + } + + int bufferSize = 1024; + + // Now read the file in chunks and write out + byte[] buffer = new byte[bufferSize]; + while (br.BaseStream.Position <= (br.BaseStream.Length - bufferSize)) + { + buffer = br.ReadBytes(bufferSize); + bw.Write(buffer); + } + + // For the final chunk, if any, write out only that number of bytes + int length = (int)(br.BaseStream.Length - br.BaseStream.Position); + buffer = new byte[length]; + buffer = br.ReadBytes(length); + bw.Write(buffer); + + if (bytesToAddToTail.Count() > 0) + { + bw.Write(bytesToAddToTail); + } + } + } } } diff --git a/SabreTools.Helper/Tools/RomTools.cs b/SabreTools.Helper/Tools/RomTools.cs index 3c20cc96..d59ac89b 100644 --- a/SabreTools.Helper/Tools/RomTools.cs +++ b/SabreTools.Helper/Tools/RomTools.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Security.Cryptography; +using System.Text.RegularExpressions; namespace SabreTools.Helper { @@ -72,6 +73,64 @@ namespace SabreTools.Helper return rom; } + /// + /// Get the header type for the input file + /// + /// Input file to parse for header + /// Passed back size of the header + /// Logger object for file and console output + /// The detected HeaderType + public static HeaderType GetFileHeaderType(string input, out int hs, Logger logger) + { + // Open the file in read mode + BinaryReader br = new BinaryReader(File.OpenRead(input)); + + // Extract the first 1024 bytes of the file + byte[] hbin = br.ReadBytes(1024); + string header = BitConverter.ToString(hbin).Replace("-", string.Empty); + br.Dispose(); + + // Determine the type of the file from the header, if possible + HeaderType type = HeaderType.None; + + // Loop over the header types and see if there's a match + hs = -1; + foreach (HeaderType test in Enum.GetValues(typeof(HeaderType))) + { + Dictionary tempDict = new Dictionary(); + + // Try populating the dictionary from the master list + try + { + tempDict = Remapping.HeaderMaps[test.ToString()]; + } + catch + { + logger.Warning("The mapping for '" + test.ToString() + "' cannot be found!"); + continue; + } + + // Loop over the dictionary and see if there are matches + foreach (KeyValuePair entry in tempDict) + { + if (Regex.IsMatch(header, entry.Key)) + { + type = test; + hs = entry.Value; + break; + } + } + + // If we found something, break out + if (type != HeaderType.None) + { + break; + } + } + + return type; + } + /// /// Merge an arbitrary set of ROMs based on the supplied information ///