mirror of
https://github.com/claunia/SabreTools.git
synced 2025-12-16 19:14:27 +00:00
[Skipper] Rearrange Skipper methods without changing logic
This commit is contained in:
@@ -1,46 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SabreTools.Helper
|
||||
{
|
||||
#region Skipper structs
|
||||
|
||||
/// <summary>
|
||||
/// Intermediate struct for holding header skipper information
|
||||
/// </summary>
|
||||
public struct Skipper
|
||||
{
|
||||
public string Name;
|
||||
public string Author;
|
||||
public string Version;
|
||||
public List<SkipperRule> Rules;
|
||||
public string SourceFile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Intermediate struct for holding header skipper rule information
|
||||
/// </summary>
|
||||
public struct SkipperRule
|
||||
{
|
||||
public long? StartOffset; // null is EOF
|
||||
public long? EndOffset; // null if EOF
|
||||
public HeaderSkipOperation Operation;
|
||||
public List<SkipperTest> Tests;
|
||||
public string SourceFile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Intermediate struct for holding header test information
|
||||
/// </summary>
|
||||
public struct SkipperTest
|
||||
{
|
||||
public HeaderSkipTest Type;
|
||||
public long? Offset; // null is EOF
|
||||
public byte[] Value;
|
||||
public bool Result;
|
||||
public byte[] Mask;
|
||||
public long? Size; // null is PO2, "power of 2" filesize
|
||||
public HeaderSkipTestFileOperator Operator;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -4564,11 +4564,12 @@ namespace SabreTools.Helper
|
||||
/// <param name="addDate">True if dates should be archived for all files, false otherwise</param>
|
||||
/// <param name="tempDir">Name of the directory to create a temp folder in (blank is current directory)</param>
|
||||
/// <param name="copyFiles">True if files should be copied to the temp directory before hashing, false otherwise</param>
|
||||
/// <param name="tryRemoveHeader">True if headers should be removed from files if possible, false otherwise</param>
|
||||
/// <param name="headerToCheckAgainst">Populated string representing the name of the skipper to use, a blank string to use the first available checker, null otherwise</param>
|
||||
/// <param name="maxDegreeOfParallelism">Integer representing the maximum amount of parallelization to be used</param>
|
||||
/// <param name="logger">Logger object for console and file output</param>
|
||||
public bool PopulateDatFromDir(string basePath, bool noMD5, bool noSHA1, bool bare, bool archivesAsFiles,
|
||||
bool enableGzip, bool addBlanks, bool addDate, string tempDir, bool copyFiles, bool tryRemoveHeader, int maxDegreeOfParallelism, Logger logger)
|
||||
bool enableGzip, bool addBlanks, bool addDate, string tempDir, bool copyFiles, string headerToCheckAgainst,
|
||||
int maxDegreeOfParallelism, Logger logger)
|
||||
{
|
||||
// If the description is defined but not the name, set the name from the description
|
||||
if (String.IsNullOrEmpty(Name) && !String.IsNullOrEmpty(Description))
|
||||
@@ -4599,7 +4600,7 @@ namespace SabreTools.Helper
|
||||
item =>
|
||||
{
|
||||
DFDProcessPossibleArchive(item, basePath, noMD5, noSHA1, bare, archivesAsFiles, enableGzip, addBlanks, addDate,
|
||||
tempDir, copyFiles, tryRemoveHeader, maxDegreeOfParallelism, logger);
|
||||
tempDir, copyFiles, headerToCheckAgainst, maxDegreeOfParallelism, logger);
|
||||
});
|
||||
|
||||
// Now find all folders that are empty, if we are supposed to
|
||||
@@ -4688,11 +4689,12 @@ namespace SabreTools.Helper
|
||||
/// <param name="addDate">True if dates should be archived for all files, false otherwise</param>
|
||||
/// <param name="tempDir">Name of the directory to create a temp folder in (blank is current directory)</param>
|
||||
/// <param name="copyFiles">True if files should be copied to the temp directory before hashing, false otherwise</param>
|
||||
/// <param name="tryRemoveHeader">True if headers should be removed from files if possible, false otherwise</param>
|
||||
/// <param name="headerToCheckAgainst">Populated string representing the name of the skipper to use, a blank string to use the first available checker, null otherwise</param>
|
||||
/// <param name="maxDegreeOfParallelism">Integer representing the maximum amount of parallelization to be used</param>
|
||||
/// <param name="logger">Logger object for console and file output</param>
|
||||
private void DFDProcessPossibleArchive(string item, string basePath, bool noMD5, bool noSHA1, bool bare, bool archivesAsFiles,
|
||||
bool enableGzip, bool addBlanks, bool addDate, string tempDir, bool copyFiles, bool tryRemoveHeader, int maxDegreeOfParallelism, Logger logger)
|
||||
bool enableGzip, bool addBlanks, bool addDate, string tempDir, bool copyFiles, string headerToCheckAgainst,
|
||||
int maxDegreeOfParallelism, Logger logger)
|
||||
{
|
||||
// Define the temporary directory
|
||||
string tempSubDir = Path.GetFullPath(Path.Combine(tempDir, Path.GetRandomFileName())) + Path.DirectorySeparatorChar;
|
||||
@@ -4762,7 +4764,7 @@ namespace SabreTools.Helper
|
||||
// Otherwise, just get the info on the file itself
|
||||
else if (File.Exists(newItem))
|
||||
{
|
||||
DFDProcessFile(newItem, "", newBasePath, noMD5, noSHA1, addDate, tryRemoveHeader, logger);
|
||||
DFDProcessFile(newItem, "", newBasePath, noMD5, noSHA1, addDate, headerToCheckAgainst, logger);
|
||||
}
|
||||
}
|
||||
// Otherwise, attempt to extract the files to the temporary directory
|
||||
@@ -4794,14 +4796,14 @@ namespace SabreTools.Helper
|
||||
noMD5,
|
||||
noSHA1,
|
||||
addDate,
|
||||
tryRemoveHeader,
|
||||
headerToCheckAgainst,
|
||||
logger);
|
||||
});
|
||||
}
|
||||
// Otherwise, just get the info on the file itself
|
||||
else if (File.Exists(newItem))
|
||||
{
|
||||
DFDProcessFile(newItem, "", newBasePath, noMD5, noSHA1, addDate, tryRemoveHeader, logger);
|
||||
DFDProcessFile(newItem, "", newBasePath, noMD5, noSHA1, addDate, headerToCheckAgainst, logger);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4831,12 +4833,12 @@ namespace SabreTools.Helper
|
||||
/// <param name="noMD5">True if MD5 hashes should be skipped over, false otherwise</param>
|
||||
/// <param name="noSHA1">True if SHA-1 hashes should be skipped over, false otherwise</param>
|
||||
/// <param name="addDate">True if dates should be archived for all files, false otherwise</param>
|
||||
/// <param name="tryRemoveHeader">True if headers should be removed from files if possible, false otherwise</param>
|
||||
/// <param name="headerToCheckAgainst">Populated string representing the name of the skipper to use, a blank string to use the first available checker, null otherwise</param>
|
||||
/// <param name="logger">Logger object for console and file output</param>
|
||||
private void DFDProcessFile(string item, string parent, string basePath, bool noMD5, bool noSHA1, bool addDate, bool tryRemoveHeader, Logger logger)
|
||||
private void DFDProcessFile(string item, string parent, string basePath, bool noMD5, bool noSHA1, bool addDate, string headerToCheckAgainst, Logger logger)
|
||||
{
|
||||
logger.Verbose(Path.GetFileName(item) + " treated like a file");
|
||||
Rom rom = FileTools.GetFileInfo(item, logger, noMD5: noMD5, noSHA1: noSHA1, date: addDate, tryRemoveHeader: tryRemoveHeader);
|
||||
Rom rom = FileTools.GetFileInfo(item, logger, noMD5: noMD5, noSHA1: noSHA1, date: addDate, header: headerToCheckAgainst);
|
||||
|
||||
DFDProcessFileHelper(item, rom, basePath, parent, logger);
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ namespace SabreTools.Helper
|
||||
{
|
||||
_datdata.PopulateDatFromDir(input, false /* noMD5 */, false /* noSHA1 */, true /* bare */, false /* archivesAsFiles */,
|
||||
true /* enableGzip */, false /* addBlanks */, false /* addDate */, "" /* tempDir */, false /* copyFiles */,
|
||||
false /* removeHeader */, 4 /* maxDegreeOfParallelism */, _logger);
|
||||
null /* headerToCheckAgainst */, 4 /* maxDegreeOfParallelism */, _logger);
|
||||
}
|
||||
|
||||
// Setup the fixdat
|
||||
@@ -486,7 +486,7 @@ namespace SabreTools.Helper
|
||||
|
||||
// Now attempt to see if the file has a header
|
||||
FileStream input = File.OpenRead(file);
|
||||
SkipperRule rule = Skippers.GetMatchingRule(input, "", _logger);
|
||||
SkipperRule rule = Skipper.GetMatchingRule(input, "", _logger);
|
||||
|
||||
// If there's a match, get the new information from the stream
|
||||
if (rule.Tests != null && rule.Tests.Count != 0)
|
||||
@@ -495,7 +495,7 @@ namespace SabreTools.Helper
|
||||
MemoryStream output = new MemoryStream();
|
||||
|
||||
// Transform the stream and get the information from it
|
||||
Skippers.TransformStream(input, output, rule, _logger, false, true);
|
||||
rule.TransformStream(input, output, _logger, false, true);
|
||||
Rom romNH = FileTools.GetStreamInfo(output, output.Length);
|
||||
romNH.Name = "HEAD::" + rom.Name;
|
||||
romNH.MachineName = rom.MachineName;
|
||||
@@ -610,14 +610,14 @@ namespace SabreTools.Helper
|
||||
}
|
||||
|
||||
// Now get the transformed file if it exists
|
||||
SkipperRule rule = Skippers.GetMatchingRule(input, "", _logger);
|
||||
SkipperRule rule = Skipper.GetMatchingRule(input, "", _logger);
|
||||
|
||||
// If we have have a non-empty rule, apply it
|
||||
if (rule.Tests != null && rule.Tests.Count != 0)
|
||||
{
|
||||
// Otherwise, apply the rule to the file
|
||||
string newinput = input + ".new";
|
||||
Skippers.TransformFile(input, newinput, rule, _logger);
|
||||
rule.TransformFile(input, newinput, _logger);
|
||||
Rom drom = FileTools.GetFileInfo(newinput, _logger);
|
||||
|
||||
// If we have a blank RomData, it's an error
|
||||
|
||||
@@ -6,8 +6,16 @@ using System.Xml;
|
||||
|
||||
namespace SabreTools.Helper
|
||||
{
|
||||
public class Skippers
|
||||
public class Skipper
|
||||
{
|
||||
#region Fields
|
||||
|
||||
public string Name;
|
||||
public string Author;
|
||||
public string Version;
|
||||
public List<SkipperRule> Rules;
|
||||
public string SourceFile;
|
||||
|
||||
// Local paths
|
||||
public const string LocalPath = "Skippers";
|
||||
|
||||
@@ -25,50 +33,30 @@ namespace SabreTools.Helper
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populate the entire list of header Skippers
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// http://mamedev.emulab.it/clrmamepro/docs/xmlheaders.txt
|
||||
/// http://www.emulab.it/forum/index.php?topic=127.0
|
||||
/// </remarks>
|
||||
private static void PopulateSkippers()
|
||||
{
|
||||
if (_list == null)
|
||||
{
|
||||
_list = new List<Skipper>();
|
||||
}
|
||||
#endregion
|
||||
|
||||
foreach (string skipperFile in Directory.EnumerateFiles(LocalPath, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
_list.Add(PopulateSkippersHelper(Path.GetFullPath(skipperFile)));
|
||||
}
|
||||
#region Constructors
|
||||
|
||||
public Skipper()
|
||||
{
|
||||
Name = "";
|
||||
Author = "";
|
||||
Version = "";
|
||||
Rules = new List<SkipperRule>();
|
||||
SourceFile = "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populate an individual Skipper from file
|
||||
/// </summary>
|
||||
/// <param name="filename">Name of the file to be read from</param>
|
||||
/// <returns>The Skipper object associated with the file</returns>
|
||||
private static Skipper PopulateSkippersHelper(string filename)
|
||||
public Skipper(string filename)
|
||||
{
|
||||
Skipper skipper = new Skipper
|
||||
{
|
||||
Rules = new List<SkipperRule>(),
|
||||
SourceFile = Path.GetFileName(filename),
|
||||
};
|
||||
|
||||
if (!File.Exists(filename))
|
||||
{
|
||||
return skipper;
|
||||
}
|
||||
Rules = new List<SkipperRule>();
|
||||
SourceFile = Path.GetFileNameWithoutExtension(filename);
|
||||
|
||||
Logger logger = new Logger(false, "");
|
||||
XmlReader xtr = FileTools.GetXmlTextReader(filename, logger);
|
||||
|
||||
if (xtr == null)
|
||||
{
|
||||
return skipper;
|
||||
return;
|
||||
}
|
||||
|
||||
bool valid = false;
|
||||
@@ -87,13 +75,13 @@ namespace SabreTools.Helper
|
||||
xtr.Read();
|
||||
break;
|
||||
case "name":
|
||||
skipper.Name = xtr.ReadElementContentAsString();
|
||||
Name = xtr.ReadElementContentAsString();
|
||||
break;
|
||||
case "author":
|
||||
skipper.Author = xtr.ReadElementContentAsString();
|
||||
Author = xtr.ReadElementContentAsString();
|
||||
break;
|
||||
case "version":
|
||||
skipper.Version = xtr.ReadElementContentAsString();
|
||||
Version = xtr.ReadElementContentAsString();
|
||||
break;
|
||||
case "rule":
|
||||
// Get the information from the rule first
|
||||
@@ -103,7 +91,7 @@ namespace SabreTools.Helper
|
||||
EndOffset = 0,
|
||||
Operation = HeaderSkipOperation.None,
|
||||
Tests = new List<SkipperTest>(),
|
||||
SourceFile = Path.GetFileName(filename),
|
||||
SourceFile = Path.GetFileNameWithoutExtension(filename),
|
||||
};
|
||||
|
||||
if (xtr.GetAttribute("start_offset") != null)
|
||||
@@ -279,7 +267,7 @@ namespace SabreTools.Helper
|
||||
}
|
||||
|
||||
// Add the created rule to the skipper
|
||||
skipper.Rules.Add(rule);
|
||||
Rules.Add(rule);
|
||||
xtr.Skip();
|
||||
break;
|
||||
default:
|
||||
@@ -288,14 +276,46 @@ namespace SabreTools.Helper
|
||||
}
|
||||
}
|
||||
|
||||
return (valid ? skipper : new Skipper());
|
||||
// If we somehow have an invalid file, zero out the fields
|
||||
if (!valid)
|
||||
{
|
||||
Name = null;
|
||||
Author = null;
|
||||
Version = null;
|
||||
Rules = null;
|
||||
SourceFile = null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Static Methods
|
||||
|
||||
/// <summary>
|
||||
/// Populate the entire list of header Skippers
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// http://mamedev.emulab.it/clrmamepro/docs/xmlheaders.txt
|
||||
/// http://www.emulab.it/forum/index.php?topic=127.0
|
||||
/// </remarks>
|
||||
private static void PopulateSkippers()
|
||||
{
|
||||
if (_list == null)
|
||||
{
|
||||
_list = new List<Skipper>();
|
||||
}
|
||||
|
||||
foreach (string skipperFile in Directory.EnumerateFiles(LocalPath, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
_list.Add(new Skipper(Path.GetFullPath(skipperFile)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the SkipperRule associated with a given file
|
||||
/// </summary>
|
||||
/// <param name="input">Name of the file to be checked</param>
|
||||
/// <param name="skipperName">Name of the skipper to be used</param>
|
||||
/// <param name="skipperName">Name of the skipper to be used, blank to find a matching skipper</param>
|
||||
/// <param name="logger">Logger object for file and console output</param>
|
||||
/// <returns>The SkipperRule that matched the file</returns>
|
||||
public static SkipperRule GetMatchingRule(string input, string skipperName, Logger logger)
|
||||
@@ -314,7 +334,7 @@ namespace SabreTools.Helper
|
||||
/// Get the SkipperRule associated with a given stream
|
||||
/// </summary>
|
||||
/// <param name="input">Name of the file to be checked</param>
|
||||
/// <param name="skipperName">Name of the skipper to be used</param>
|
||||
/// <param name="skipperName">Name of the skipper to be used, blank to find a matching skipper</param>
|
||||
/// <param name="logger">Logger object for file and console output</param>
|
||||
/// <param name="keepOpen">True if the underlying stream should be kept open, false otherwise</param>
|
||||
/// <returns>The SkipperRule that matched the file</returns>
|
||||
@@ -329,7 +349,10 @@ namespace SabreTools.Helper
|
||||
|
||||
foreach (Skipper skipper in tempList)
|
||||
{
|
||||
if (String.IsNullOrEmpty(skipperName) || (!String.IsNullOrEmpty(skipper.Name) && skipperName.ToLowerInvariant() == skipper.Name.ToLowerInvariant()))
|
||||
// If we're searching for the skipper OR we have a match to an inputted one
|
||||
if (String.IsNullOrEmpty(skipperName)
|
||||
|| (!String.IsNullOrEmpty(skipper.Name) && skipperName.ToLowerInvariant() == skipper.Name.ToLowerInvariant())
|
||||
|| (!String.IsNullOrEmpty(skipper.Name) && skipperName.ToLowerInvariant() == skipper.SourceFile.ToLowerInvariant()))
|
||||
{
|
||||
// Loop through the rules until one is found that works
|
||||
BinaryReader br = new BinaryReader(input);
|
||||
@@ -492,187 +515,6 @@ namespace SabreTools.Helper
|
||||
return skipperRule;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transform an input file using the given rule
|
||||
/// </summary>
|
||||
/// <param name="input">Input file name</param>
|
||||
/// <param name="output">Output file name</param>
|
||||
/// <param name="rule">SkipperRule to apply to the file</param>
|
||||
/// <param name="logger">Logger object for file and console output</param>
|
||||
/// <returns>True if the file was transformed properly, false otherwise</returns>
|
||||
public static bool TransformFile(string input, string output, SkipperRule rule, Logger logger)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
// If the input file doesn't exist, fail
|
||||
if (!File.Exists(input))
|
||||
{
|
||||
logger.Error("I'm sorry but '" + input + "' doesn't exist!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the output directory if it doesn't already
|
||||
if (!Directory.Exists(Path.GetDirectoryName(output)))
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(output));
|
||||
}
|
||||
|
||||
logger.User("Attempting to apply rule to '" + input + "'");
|
||||
success = TransformStream(File.Open(input, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), File.OpenWrite(output), rule, logger);
|
||||
|
||||
// If the output file has size 0, delete it
|
||||
if (new FileInfo(output).Length == 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(output);
|
||||
success = false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Don't log this file deletion error
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transform an input stream using the given rule
|
||||
/// </summary>
|
||||
/// <param name="input">Input stream</param>
|
||||
/// <param name="output">Output stream</param>
|
||||
/// <param name="rule">SkipperRule to apply to the stream</param>
|
||||
/// <param name="logger">Logger object for file and console output</param>
|
||||
/// <param name="keepReadOpen">True if the underlying read stream should be kept open, false otherwise</param>
|
||||
/// <param name="keepWriteOpen">True if the underlying write stream should be kept open, false otherwise</param>
|
||||
/// <returns>True if the file was transformed properly, false otherwise</returns>
|
||||
public static bool TransformStream(Stream input, Stream output, SkipperRule rule, Logger logger, bool keepReadOpen = false, bool keepWriteOpen = false)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
// If the sizes are wrong for the values, fail
|
||||
long extsize = input.Length;
|
||||
if ((rule.Operation > HeaderSkipOperation.Bitswap && (extsize % 2) != 0)
|
||||
|| (rule.Operation > HeaderSkipOperation.Byteswap && (extsize % 4) != 0)
|
||||
|| (rule.Operation > HeaderSkipOperation.Bitswap && (rule.StartOffset == null || rule.StartOffset % 2 == 0)))
|
||||
{
|
||||
logger.Error("The stream did not have the correct size to be transformed!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now read the proper part of the file and apply the rule
|
||||
BinaryWriter bw = null;
|
||||
BinaryReader br = null;
|
||||
try
|
||||
{
|
||||
logger.User("Applying found rule to input stream");
|
||||
bw = new BinaryWriter(output);
|
||||
br = new BinaryReader(input);
|
||||
|
||||
// Seek to the beginning offset
|
||||
if (rule.StartOffset == null)
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
else if (Math.Abs((long)rule.StartOffset) > input.Length)
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
else if (rule.StartOffset > 0)
|
||||
{
|
||||
input.Seek((long)rule.StartOffset, SeekOrigin.Begin);
|
||||
}
|
||||
else if (rule.StartOffset < 0)
|
||||
{
|
||||
input.Seek((long)rule.StartOffset, SeekOrigin.End);
|
||||
}
|
||||
|
||||
// Then read and apply the operation as you go
|
||||
if (success)
|
||||
{
|
||||
byte[] buffer = new byte[4];
|
||||
int pos = 0;
|
||||
while (input.Position < (rule.EndOffset != null ? rule.EndOffset : input.Length)
|
||||
&& input.Position < input.Length)
|
||||
{
|
||||
byte b = br.ReadByte();
|
||||
switch (rule.Operation)
|
||||
{
|
||||
case HeaderSkipOperation.Bitswap:
|
||||
// http://stackoverflow.com/questions/3587826/is-there-a-built-in-function-to-reverse-bit-order
|
||||
uint r = b;
|
||||
int s = 7;
|
||||
for (b >>= 1; b != 0; b >>= 1)
|
||||
{
|
||||
r <<= 1;
|
||||
r |= (byte)(b & 1);
|
||||
s--;
|
||||
}
|
||||
r <<= s;
|
||||
buffer[pos] = (byte)r;
|
||||
break;
|
||||
case HeaderSkipOperation.Byteswap:
|
||||
if (pos % 2 == 1)
|
||||
{
|
||||
buffer[pos - 1] = b;
|
||||
}
|
||||
if (pos % 2 == 0)
|
||||
{
|
||||
buffer[pos + 1] = b;
|
||||
}
|
||||
break;
|
||||
case HeaderSkipOperation.Wordswap:
|
||||
buffer[3 - pos] = b;
|
||||
break;
|
||||
case HeaderSkipOperation.WordByteswap:
|
||||
buffer[(pos + 2) % 4] = b;
|
||||
break;
|
||||
case HeaderSkipOperation.None:
|
||||
default:
|
||||
buffer[pos] = b;
|
||||
break;
|
||||
}
|
||||
|
||||
// Set the buffer position to default write to
|
||||
pos = (pos + 1) % 4;
|
||||
|
||||
// If we filled a buffer, flush to the stream
|
||||
if (pos == 0)
|
||||
{
|
||||
bw.Write(buffer);
|
||||
bw.Flush();
|
||||
buffer = new byte[4];
|
||||
}
|
||||
}
|
||||
// If there's anything more in the buffer, write only the left bits
|
||||
for (int i = 0; i < pos; i++)
|
||||
{
|
||||
bw.Write(buffer[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex.ToString());
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// If we're not keeping the read stream open, dispose of the binary reader
|
||||
if (!keepReadOpen)
|
||||
{
|
||||
br?.Dispose();
|
||||
}
|
||||
|
||||
// If we're not keeping the write stream open, dispose of the binary reader
|
||||
if (!keepWriteOpen)
|
||||
{
|
||||
bw?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
211
SabreTools.Helper/Objects/Skippers/SkipperRule.cs
Normal file
211
SabreTools.Helper/Objects/Skippers/SkipperRule.cs
Normal file
@@ -0,0 +1,211 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace SabreTools.Helper
|
||||
{
|
||||
public class SkipperRule
|
||||
{
|
||||
// Public variables
|
||||
public long? StartOffset; // null is EOF
|
||||
public long? EndOffset; // null if EOF
|
||||
public HeaderSkipOperation Operation;
|
||||
public List<SkipperTest> Tests;
|
||||
public string SourceFile;
|
||||
|
||||
/// <summary>
|
||||
/// Transform an input file using the given rule
|
||||
/// </summary>
|
||||
/// <param name="input">Input file name</param>
|
||||
/// <param name="output">Output file name</param>
|
||||
/// <param name="logger">Logger object for file and console output</param>
|
||||
/// <returns>True if the file was transformed properly, false otherwise</returns>
|
||||
public bool TransformFile(string input, string output, Logger logger)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
// If the input file doesn't exist, fail
|
||||
if (!File.Exists(input))
|
||||
{
|
||||
logger.Error("I'm sorry but '" + input + "' doesn't exist!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the output directory if it doesn't already
|
||||
if (!Directory.Exists(Path.GetDirectoryName(output)))
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(output));
|
||||
}
|
||||
|
||||
logger.User("Attempting to apply rule to '" + input + "'");
|
||||
success = TransformStream(File.Open(input, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), File.OpenWrite(output), logger);
|
||||
|
||||
// If the output file has size 0, delete it
|
||||
if (new FileInfo(output).Length == 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(output);
|
||||
success = false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Don't log this file deletion error
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transform an input stream using the given rule
|
||||
/// </summary>
|
||||
/// <param name="input">Input stream</param>
|
||||
/// <param name="output">Output stream</param>
|
||||
/// <param name="logger">Logger object for file and console output</param>
|
||||
/// <param name="keepReadOpen">True if the underlying read stream should be kept open, false otherwise</param>
|
||||
/// <param name="keepWriteOpen">True if the underlying write stream should be kept open, false otherwise</param>
|
||||
/// <returns>True if the file was transformed properly, false otherwise</returns>
|
||||
public bool TransformStream(Stream input, Stream output, Logger logger, bool keepReadOpen = false, bool keepWriteOpen = false)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
// If the sizes are wrong for the values, fail
|
||||
long extsize = input.Length;
|
||||
if ((Operation > HeaderSkipOperation.Bitswap && (extsize % 2) != 0)
|
||||
|| (Operation > HeaderSkipOperation.Byteswap && (extsize % 4) != 0)
|
||||
|| (Operation > HeaderSkipOperation.Bitswap && (StartOffset == null || StartOffset % 2 == 0)))
|
||||
{
|
||||
logger.Error("The stream did not have the correct size to be transformed!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now read the proper part of the file and apply the rule
|
||||
BinaryWriter bw = null;
|
||||
BinaryReader br = null;
|
||||
try
|
||||
{
|
||||
logger.User("Applying found rule to input stream");
|
||||
bw = new BinaryWriter(output);
|
||||
br = new BinaryReader(input);
|
||||
|
||||
// Seek to the beginning offset
|
||||
if (StartOffset == null)
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
else if (Math.Abs((long)StartOffset) > input.Length)
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
else if (StartOffset > 0)
|
||||
{
|
||||
input.Seek((long)StartOffset, SeekOrigin.Begin);
|
||||
}
|
||||
else if (StartOffset < 0)
|
||||
{
|
||||
input.Seek((long)StartOffset, SeekOrigin.End);
|
||||
}
|
||||
|
||||
// Then read and apply the operation as you go
|
||||
if (success)
|
||||
{
|
||||
byte[] buffer = new byte[4];
|
||||
int pos = 0;
|
||||
while (input.Position < (EndOffset != null ? EndOffset : input.Length)
|
||||
&& input.Position < input.Length)
|
||||
{
|
||||
byte b = br.ReadByte();
|
||||
switch (Operation)
|
||||
{
|
||||
case HeaderSkipOperation.Bitswap:
|
||||
// http://stackoverflow.com/questions/3587826/is-there-a-built-in-function-to-reverse-bit-order
|
||||
uint r = b;
|
||||
int s = 7;
|
||||
for (b >>= 1; b != 0; b >>= 1)
|
||||
{
|
||||
r <<= 1;
|
||||
r |= (byte)(b & 1);
|
||||
s--;
|
||||
}
|
||||
r <<= s;
|
||||
buffer[pos] = (byte)r;
|
||||
break;
|
||||
case HeaderSkipOperation.Byteswap:
|
||||
if (pos % 2 == 1)
|
||||
{
|
||||
buffer[pos - 1] = b;
|
||||
}
|
||||
if (pos % 2 == 0)
|
||||
{
|
||||
buffer[pos + 1] = b;
|
||||
}
|
||||
break;
|
||||
case HeaderSkipOperation.Wordswap:
|
||||
buffer[3 - pos] = b;
|
||||
break;
|
||||
case HeaderSkipOperation.WordByteswap:
|
||||
buffer[(pos + 2) % 4] = b;
|
||||
break;
|
||||
case HeaderSkipOperation.None:
|
||||
default:
|
||||
buffer[pos] = b;
|
||||
break;
|
||||
}
|
||||
|
||||
// Set the buffer position to default write to
|
||||
pos = (pos + 1) % 4;
|
||||
|
||||
// If we filled a buffer, flush to the stream
|
||||
if (pos == 0)
|
||||
{
|
||||
bw.Write(buffer);
|
||||
bw.Flush();
|
||||
buffer = new byte[4];
|
||||
}
|
||||
}
|
||||
// If there's anything more in the buffer, write only the left bits
|
||||
for (int i = 0; i < pos; i++)
|
||||
{
|
||||
bw.Write(buffer[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex.ToString());
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// If we're not keeping the read stream open, dispose of the binary reader
|
||||
if (!keepReadOpen)
|
||||
{
|
||||
br?.Dispose();
|
||||
}
|
||||
|
||||
// If we're not keeping the write stream open, dispose of the binary reader
|
||||
if (!keepWriteOpen)
|
||||
{
|
||||
bw?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Intermediate class for storing Skipper Test information
|
||||
/// </summary>
|
||||
public struct SkipperTest
|
||||
{
|
||||
public HeaderSkipTest Type;
|
||||
public long? Offset; // null is EOF
|
||||
public byte[] Value;
|
||||
public bool Result;
|
||||
public byte[] Mask;
|
||||
public long? Size; // null is PO2, "power of 2" filesize
|
||||
public HeaderSkipTestFileOperator Operator;
|
||||
}
|
||||
}
|
||||
@@ -113,6 +113,8 @@
|
||||
<Compile Include="Objects\SimpleSort.cs" />
|
||||
<Compile Include="Objects\Archive\ZipFileEntry.cs" />
|
||||
<Compile Include="Objects\Archive\ZipFile.cs" />
|
||||
<Compile Include="Objects\Skippers\Skipper.cs" />
|
||||
<Compile Include="Objects\Skippers\SkipperRule.cs" />
|
||||
<Compile Include="Resources\Resources.de-DE.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
@@ -133,14 +135,12 @@
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.fr-FR.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Skippers\Skippers.cs" />
|
||||
<Compile Include="Tools\ArchiveTools.cs" />
|
||||
<Compile Include="Tools\FileTools.cs" />
|
||||
<Compile Include="Tools\DatabaseTools.cs" />
|
||||
<Compile Include="Data\Enums.cs" />
|
||||
<Compile Include="Objects\Logger.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Data\Structs.cs" />
|
||||
<Compile Include="Tools\Style.cs" />
|
||||
<Compile Include="Data\Build.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -156,9 +156,9 @@ namespace SabreTools.Helper
|
||||
/// <param name="noSHA1">True if SHA-1 hashes should not be calcluated, false otherwise (default)</param>
|
||||
/// <param name="offset">Set a >0 number for getting hash for part of the file, 0 otherwise (default)</param>
|
||||
/// <param name="date">True if the file Date should be included, false otherwise (default)</param>
|
||||
/// <param name="tryRemoveHeader">True if headers should be removed from files if possible, false otherwise (default)</param>
|
||||
/// <param name="header">Populated string representing the name of the skipper to use, a blank string to use the first available checker, null otherwise</param>
|
||||
/// <returns>Populated RomData object if success, empty one on error</returns>
|
||||
public static Rom GetFileInfo(string input, Logger logger, bool noMD5 = false, bool noSHA1 = false, long offset = 0, bool date = false, bool tryRemoveHeader = false)
|
||||
public static Rom GetFileInfo(string input, Logger logger, bool noMD5 = false, bool noSHA1 = false, long offset = 0, bool date = false, string header = null)
|
||||
{
|
||||
// Add safeguard if file doesn't exist
|
||||
if (!File.Exists(input))
|
||||
@@ -168,9 +168,9 @@ namespace SabreTools.Helper
|
||||
|
||||
// Get the information from the file stream
|
||||
Rom rom = new Rom();
|
||||
if (tryRemoveHeader)
|
||||
if (header != null)
|
||||
{
|
||||
SkipperRule rule = Skippers.GetMatchingRule(input, "", logger);
|
||||
SkipperRule rule = Skipper.GetMatchingRule(input, Path.GetFileNameWithoutExtension(header), logger);
|
||||
|
||||
// If there's a match, get the new information from the stream
|
||||
if (rule.Tests != null && rule.Tests.Count != 0)
|
||||
@@ -180,7 +180,7 @@ namespace SabreTools.Helper
|
||||
FileStream inputStream = File.OpenRead(input);
|
||||
|
||||
// Transform the stream and get the information from it
|
||||
Skippers.TransformStream(inputStream, outputStream, rule, logger, keepReadOpen: false, keepWriteOpen: true);
|
||||
rule.TransformStream(inputStream, outputStream, logger, keepReadOpen: false, keepWriteOpen: true);
|
||||
rom = GetStreamInfo(outputStream, outputStream.Length);
|
||||
|
||||
// Dispose of the streams
|
||||
@@ -328,7 +328,7 @@ namespace SabreTools.Helper
|
||||
logger.User("\nGetting skipper information for '" + file + "'");
|
||||
|
||||
// Get the skipper rule that matches the file, if any
|
||||
SkipperRule rule = Skippers.GetMatchingRule(file, "", logger);
|
||||
SkipperRule rule = Skipper.GetMatchingRule(file, "", logger);
|
||||
|
||||
// If we have an empty rule, return false
|
||||
if (rule.Tests == null || rule.Tests.Count == 0 || rule.Operation != HeaderSkipOperation.None)
|
||||
@@ -352,7 +352,7 @@ namespace SabreTools.Helper
|
||||
|
||||
// Apply the rule to the file
|
||||
string newfile = (outDir == "" ? Path.GetFullPath(file) + ".new" : Path.Combine(outDir, Path.GetFileName(file)));
|
||||
Skippers.TransformFile(file, newfile, rule, logger);
|
||||
rule.TransformFile(file, newfile, logger);
|
||||
|
||||
// If the output file doesn't exist, return false
|
||||
if (!File.Exists(newfile))
|
||||
|
||||
Reference in New Issue
Block a user