mirror of
https://github.com/claunia/SabreTools.git
synced 2025-12-16 19:14:27 +00:00
[Mappings, Skippers] Separate out old Remapping class
All things that have to do with DAT mappings are in Mappings now and eveything having to do with header skippers are in Skippers now. Also, Skippers got proper implementations of matching a file to a rule and applying a rule to a file.
This commit is contained in:
@@ -130,7 +130,7 @@ namespace SabreTools
|
|||||||
{
|
{
|
||||||
// First get the HeaderType, if any
|
// First get the HeaderType, if any
|
||||||
int headerSize = -1;
|
int headerSize = -1;
|
||||||
HeaderType type = RomTools.GetFileHeaderType(file, out headerSize, logger);
|
HeaderType type = Skippers.GetFileHeaderType(file, out headerSize, logger);
|
||||||
|
|
||||||
// If we have a valid HeaderType, remove the correct byte count
|
// If we have a valid HeaderType, remove the correct byte count
|
||||||
logger.User("File has header: " + (type != HeaderType.None));
|
logger.User("File has header: " + (type != HeaderType.None));
|
||||||
|
|||||||
@@ -121,6 +121,7 @@
|
|||||||
Bitswap,
|
Bitswap,
|
||||||
Byteswap,
|
Byteswap,
|
||||||
Wordswap,
|
Wordswap,
|
||||||
|
WordByteswap,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
111
SabreTools.Helper/Mappings.cs
Normal file
111
SabreTools.Helper/Mappings.cs
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Xml;
|
||||||
|
|
||||||
|
namespace SabreTools.Helper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains all remappings of known import classes
|
||||||
|
/// </summary>
|
||||||
|
public class Mappings
|
||||||
|
{
|
||||||
|
// Local paths
|
||||||
|
private const string _remappersPath = "Mappings";
|
||||||
|
|
||||||
|
// Remapping classes represented by a dictionary of dictionaries (name, (from, to))
|
||||||
|
private static Dictionary<string, Dictionary<string, string>> _datMaps = new Dictionary<string, Dictionary<string, string>>();
|
||||||
|
public static Dictionary<string, Dictionary<string, string>> DatMaps
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_datMaps.Count == 0)
|
||||||
|
{
|
||||||
|
CreateRemappings();
|
||||||
|
}
|
||||||
|
return _datMaps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region DAT Name Remappings
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create all remappings to be used by the program
|
||||||
|
/// </summary>
|
||||||
|
private static void CreateRemappings()
|
||||||
|
{
|
||||||
|
// Create array of dictionary names
|
||||||
|
string[] remappings =
|
||||||
|
{
|
||||||
|
"Good", "MAME", "MaybeIntro", "NoIntro", "NonGood", "Redump", "TOSEC", "TruRip",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Loop through and add all remappings
|
||||||
|
foreach (string remapping in remappings)
|
||||||
|
{
|
||||||
|
_datMaps.Add(remapping, new Dictionary<string, string>());
|
||||||
|
RemappingHelper(remapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a remapping from XML
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mapping">Name of the mapping to be populated</param>
|
||||||
|
private static void RemappingHelper(string mapping)
|
||||||
|
{
|
||||||
|
// Read in remapping from file
|
||||||
|
XmlDocument doc = new XmlDocument();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
doc.LoadXml(File.ReadAllText(Path.Combine(_remappersPath, mapping + ".xml")));
|
||||||
|
}
|
||||||
|
catch (XmlException ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(mapping + " remappings could not be loaded! " + ex.ToString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the mappings parent node
|
||||||
|
XmlNode node = doc.FirstChild;
|
||||||
|
while (node.Name != "mappings")
|
||||||
|
{
|
||||||
|
node = node.NextSibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the node is empty, just return so it doesn't crash
|
||||||
|
if (!node.HasChildNodes)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the first mapping node
|
||||||
|
node = node.FirstChild;
|
||||||
|
while (node.NodeType != XmlNodeType.Element && node.Name != "mapping")
|
||||||
|
{
|
||||||
|
node = node.NextSibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now read in the mappings
|
||||||
|
while (node != null && node.Name == "mapping")
|
||||||
|
{
|
||||||
|
_datMaps[mapping].Add(node.Attributes["from"].Value, node.Attributes["to"].Value);
|
||||||
|
|
||||||
|
// Get the next node and skip over anything that's not an element
|
||||||
|
node = node.NextSibling;
|
||||||
|
|
||||||
|
if (node == null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (node.NodeType != XmlNodeType.Element && node.Name != "mapping")
|
||||||
|
{
|
||||||
|
node = node.NextSibling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -85,6 +85,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Data\Constants.cs" />
|
<Compile Include="Data\Constants.cs" />
|
||||||
|
<Compile Include="Skippers.cs" />
|
||||||
<Compile Include="Tools\ArchiveTools.cs" />
|
<Compile Include="Tools\ArchiveTools.cs" />
|
||||||
<Compile Include="Tools\CRC32.cs" />
|
<Compile Include="Tools\CRC32.cs" />
|
||||||
<Compile Include="Tools\DBTools.cs" />
|
<Compile Include="Tools\DBTools.cs" />
|
||||||
@@ -94,7 +95,7 @@
|
|||||||
<Compile Include="Logger.cs" />
|
<Compile Include="Logger.cs" />
|
||||||
<Compile Include="Tools\Output.cs" />
|
<Compile Include="Tools\Output.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Remapping.cs" />
|
<Compile Include="Mappings.cs" />
|
||||||
<Compile Include="Tools\DatTools.cs" />
|
<Compile Include="Tools\DatTools.cs" />
|
||||||
<Compile Include="Data\Structs.cs" />
|
<Compile Include="Data\Structs.cs" />
|
||||||
<Compile Include="Tools\RomTools.cs" />
|
<Compile Include="Tools\RomTools.cs" />
|
||||||
|
|||||||
@@ -2,33 +2,16 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
|
||||||
namespace SabreTools.Helper
|
namespace SabreTools.Helper
|
||||||
{
|
{
|
||||||
/// <summary>
|
public class Skippers
|
||||||
/// Contains all remappings of known import classes
|
|
||||||
/// </summary>
|
|
||||||
public class Remapping
|
|
||||||
{
|
{
|
||||||
// Local paths
|
// Local paths
|
||||||
private const string _remappersPath = "Mappings";
|
|
||||||
private const string _skippersPath = "Skippers";
|
private const string _skippersPath = "Skippers";
|
||||||
|
|
||||||
// Remapping classes represented by a dictionary of dictionaries (name, (from, to))
|
|
||||||
private static Dictionary<string, Dictionary<string, string>> _datMaps = new Dictionary<string, Dictionary<string, string>>();
|
|
||||||
public static Dictionary<string, Dictionary<string, string>> DatMaps
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_datMaps.Count == 0)
|
|
||||||
{
|
|
||||||
CreateRemappings();
|
|
||||||
}
|
|
||||||
return _datMaps;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Header skippers represented by a list of skipper objects
|
// Header skippers represented by a list of skipper objects
|
||||||
private static List<Skipper> _list;
|
private static List<Skipper> _list;
|
||||||
public static List<Skipper> List
|
public static List<Skipper> List
|
||||||
@@ -57,87 +40,6 @@ namespace SabreTools.Helper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#region DAT Name Remappings
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create all remappings to be used by the program
|
|
||||||
/// </summary>
|
|
||||||
private static void CreateRemappings()
|
|
||||||
{
|
|
||||||
// Create array of dictionary names
|
|
||||||
string[] remappings =
|
|
||||||
{
|
|
||||||
"Good", "MAME", "MaybeIntro", "NoIntro", "NonGood", "Redump", "TOSEC", "TruRip",
|
|
||||||
};
|
|
||||||
|
|
||||||
// Loop through and add all remappings
|
|
||||||
foreach (string remapping in remappings)
|
|
||||||
{
|
|
||||||
_datMaps.Add(remapping, new Dictionary<string, string>());
|
|
||||||
RemappingHelper(remapping);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a remapping from XML
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="mapping">Name of the mapping to be populated</param>
|
|
||||||
private static void RemappingHelper(string mapping)
|
|
||||||
{
|
|
||||||
// Read in remapping from file
|
|
||||||
XmlDocument doc = new XmlDocument();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
doc.LoadXml(File.ReadAllText(Path.Combine(_remappersPath, mapping + ".xml")));
|
|
||||||
}
|
|
||||||
catch (XmlException ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine(mapping + " remappings could not be loaded! " + ex.ToString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the mappings parent node
|
|
||||||
XmlNode node = doc.FirstChild;
|
|
||||||
while (node.Name != "mappings")
|
|
||||||
{
|
|
||||||
node = node.NextSibling;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the node is empty, just return so it doesn't crash
|
|
||||||
if (!node.HasChildNodes)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the first mapping node
|
|
||||||
node = node.FirstChild;
|
|
||||||
while (node.NodeType != XmlNodeType.Element && node.Name != "mapping")
|
|
||||||
{
|
|
||||||
node = node.NextSibling;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now read in the mappings
|
|
||||||
while (node != null && node.Name == "mapping")
|
|
||||||
{
|
|
||||||
_datMaps[mapping].Add(node.Attributes["from"].Value, node.Attributes["to"].Value);
|
|
||||||
|
|
||||||
// Get the next node and skip over anything that's not an element
|
|
||||||
node = node.NextSibling;
|
|
||||||
|
|
||||||
if (node == null)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (node.NodeType != XmlNodeType.Element && node.Name != "mapping")
|
|
||||||
{
|
|
||||||
node = node.NextSibling;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Header Skips (new)
|
#region Header Skips (new)
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -409,6 +311,320 @@ namespace SabreTools.Helper
|
|||||||
return (valid ? skipper : new Skipper());
|
return (valid ? skipper : new Skipper());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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="logger">Logger object for file and console output</param>
|
||||||
|
/// <returns>The SkipperRule that matched the file</returns>
|
||||||
|
public static SkipperRule MatchesSkipper(string input, string skippername, Logger logger)
|
||||||
|
{
|
||||||
|
SkipperRule skipperRule = new SkipperRule();
|
||||||
|
|
||||||
|
// If the file doesn't exist, return a blank skipper rule
|
||||||
|
if (!File.Exists(input))
|
||||||
|
{
|
||||||
|
logger.Error("The file '" + input + "' does not exist so it cannot be tested");
|
||||||
|
return skipperRule;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through and find a Skipper that has the right name
|
||||||
|
Skipper matchedSkipper = new Skipper();
|
||||||
|
foreach (Skipper skipper in List)
|
||||||
|
{
|
||||||
|
if (!String.IsNullOrEmpty(skipper.Name) && skippername.ToLowerInvariant() == skipper.Name.ToLowerInvariant())
|
||||||
|
{
|
||||||
|
matchedSkipper = skipper;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a blank skipper, return a blank skipper rule
|
||||||
|
if (String.IsNullOrEmpty(matchedSkipper.Name) || matchedSkipper.Rules == null)
|
||||||
|
{
|
||||||
|
logger.Warning("No header skipper by the name of '" + skippername + "' can be found");
|
||||||
|
return skipperRule;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, loop through the rules until one is found that works
|
||||||
|
using (BinaryReader br = new BinaryReader(File.OpenRead(input)))
|
||||||
|
{
|
||||||
|
logger.User("Beginning search for matching header skip rules");
|
||||||
|
foreach (SkipperRule rule in matchedSkipper.Rules)
|
||||||
|
{
|
||||||
|
// Always reset the stream back to the original place
|
||||||
|
br.BaseStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
// For each rule, make sure it passes each test
|
||||||
|
bool success = true;
|
||||||
|
foreach (SkipperTest test in rule.Tests)
|
||||||
|
{
|
||||||
|
bool result = true;
|
||||||
|
switch (test.Type)
|
||||||
|
{
|
||||||
|
case HeaderSkipTest.Data:
|
||||||
|
// First seek to the correct position
|
||||||
|
if (test.Offset == null)
|
||||||
|
{
|
||||||
|
br.BaseStream.Seek(0, SeekOrigin.End);
|
||||||
|
}
|
||||||
|
else if (test.Offset > 0 && test.Offset <= br.BaseStream.Length)
|
||||||
|
{
|
||||||
|
br.BaseStream.Seek((long)test.Offset, SeekOrigin.Begin);
|
||||||
|
}
|
||||||
|
else if (test.Offset < 0 && Math.Abs((long)test.Offset) <= br.BaseStream.Length)
|
||||||
|
{
|
||||||
|
br.BaseStream.Seek((long)test.Offset, SeekOrigin.End);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then read and compare bytewise
|
||||||
|
result = true;
|
||||||
|
for (int i = 0; i < test.Value.Length; i++)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (br.ReadByte() != test.Value[i])
|
||||||
|
{
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return if the expected and actual results match
|
||||||
|
success &= (result == test.Result);
|
||||||
|
break;
|
||||||
|
case HeaderSkipTest.Or:
|
||||||
|
case HeaderSkipTest.Xor:
|
||||||
|
case HeaderSkipTest.And:
|
||||||
|
// First seek to the correct position
|
||||||
|
if (test.Offset == null)
|
||||||
|
{
|
||||||
|
br.BaseStream.Seek(0, SeekOrigin.End);
|
||||||
|
}
|
||||||
|
else if (test.Offset > 0 && test.Offset <= br.BaseStream.Length)
|
||||||
|
{
|
||||||
|
br.BaseStream.Seek((long)test.Offset, SeekOrigin.Begin);
|
||||||
|
}
|
||||||
|
else if (test.Offset < 0 && Math.Abs((long)test.Offset) <= br.BaseStream.Length)
|
||||||
|
{
|
||||||
|
br.BaseStream.Seek((long)test.Offset, SeekOrigin.End);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Then apply the mask if it exists
|
||||||
|
byte[] read = br.ReadBytes(test.Mask.Length);
|
||||||
|
byte[] masked = new byte[test.Mask.Length];
|
||||||
|
for (int i = 0; i < read.Length; i++)
|
||||||
|
{
|
||||||
|
masked[i] = (byte)(test.Type == HeaderSkipTest.And ? read[i] & test.Mask[i] :
|
||||||
|
(test.Type == HeaderSkipTest.Or ? read[i] | test.Mask[i] : read[i] ^ test.Mask[i])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, compare it against the value
|
||||||
|
for (int i = 0; i < test.Value.Length; i++)
|
||||||
|
{
|
||||||
|
if (masked[i] != test.Value[i])
|
||||||
|
{
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return if the expected and actual results match
|
||||||
|
success &= (result == test.Result);
|
||||||
|
break;
|
||||||
|
case HeaderSkipTest.File:
|
||||||
|
// First get the file size from stream
|
||||||
|
long size = br.BaseStream.Length;
|
||||||
|
|
||||||
|
// If we have a null size, check that the size is a power of 2
|
||||||
|
result = true;
|
||||||
|
if (test.Size == null)
|
||||||
|
{
|
||||||
|
// http://stackoverflow.com/questions/600293/how-to-check-if-a-number-is-a-power-of-2
|
||||||
|
result = (((ulong)size & ((ulong)size - 1)) == 0);
|
||||||
|
}
|
||||||
|
else if (test.Operator == HeaderSkipTestFileOperator.Less)
|
||||||
|
{
|
||||||
|
result = (size < test.Size);
|
||||||
|
}
|
||||||
|
else if (test.Operator == HeaderSkipTestFileOperator.Greater)
|
||||||
|
{
|
||||||
|
result = (size > test.Size);
|
||||||
|
}
|
||||||
|
else if (test.Operator == HeaderSkipTestFileOperator.Equal)
|
||||||
|
{
|
||||||
|
result = (size == test.Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return if the expected and actual results match
|
||||||
|
success &= (result == test.Result);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we still have a success, then return this rule
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
logger.User("Matching rule found!");
|
||||||
|
skipperRule = rule;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a blank rule, inform the user
|
||||||
|
if (skipperRule.Tests == null)
|
||||||
|
{
|
||||||
|
logger.User("No matching rule found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
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></returns>
|
||||||
|
public static bool TransformFile(string input, string output, SkipperRule rule, Logger logger)
|
||||||
|
{
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the sizes are wrong for the values, fail
|
||||||
|
long extsize = new FileInfo(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 file did not have the correct size to be transformed!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now read the proper part of the file and apply the rule
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (BinaryWriter bw = new BinaryWriter(File.OpenWrite(output)))
|
||||||
|
using (BinaryReader br = new BinaryReader(File.OpenRead(input)))
|
||||||
|
{
|
||||||
|
// Seek to the beginning offset
|
||||||
|
if (rule.StartOffset == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (Math.Abs((long)rule.StartOffset) > br.BaseStream.Length)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (rule.StartOffset > 0)
|
||||||
|
{
|
||||||
|
br.BaseStream.Seek((long)rule.StartOffset, SeekOrigin.Begin);
|
||||||
|
}
|
||||||
|
else if (rule.StartOffset < 0)
|
||||||
|
{
|
||||||
|
br.BaseStream.Seek((long)rule.StartOffset, SeekOrigin.End);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then read and apply the operation as you go
|
||||||
|
byte[] buffer = new byte[4];
|
||||||
|
int pos = 0;
|
||||||
|
while (br.BaseStream.Position <= (rule.EndOffset != null ? rule.EndOffset : br.BaseStream.Length)
|
||||||
|
&& br.BaseStream.Position <= br.BaseStream.Length)
|
||||||
|
{
|
||||||
|
uint b = br.ReadUInt32();
|
||||||
|
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] = (byte)b;
|
||||||
|
}
|
||||||
|
if (pos % 2 == 0)
|
||||||
|
{
|
||||||
|
buffer[pos + 1] = (byte)b;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HeaderSkipOperation.Wordswap:
|
||||||
|
buffer[3 - pos] = (byte)b;
|
||||||
|
break;
|
||||||
|
case HeaderSkipOperation.WordByteswap:
|
||||||
|
buffer[(pos + 2) % 4] = (byte)b;
|
||||||
|
break;
|
||||||
|
case HeaderSkipOperation.None:
|
||||||
|
default:
|
||||||
|
buffer[pos] = (byte)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);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Header Skips (old)
|
#region Header Skips (old)
|
||||||
@@ -495,6 +711,64 @@ namespace SabreTools.Helper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the header type for the input file
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input">Input file to parse for header</param>
|
||||||
|
/// <param name="hs">Passed back size of the header</param>
|
||||||
|
/// <param name="logger">Logger object for file and console output</param>
|
||||||
|
/// <returns>The detected HeaderType</returns>
|
||||||
|
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<string, int> tempDict = new Dictionary<string, int>();
|
||||||
|
|
||||||
|
// Try populating the dictionary from the master list
|
||||||
|
try
|
||||||
|
{
|
||||||
|
tempDict = Skippers.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<string, int> 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;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,64 +80,6 @@ namespace SabreTools.Helper
|
|||||||
return rom;
|
return rom;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the header type for the input file
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="input">Input file to parse for header</param>
|
|
||||||
/// <param name="hs">Passed back size of the header</param>
|
|
||||||
/// <param name="logger">Logger object for file and console output</param>
|
|
||||||
/// <returns>The detected HeaderType</returns>
|
|
||||||
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<string, int> tempDict = new Dictionary<string, int>();
|
|
||||||
|
|
||||||
// 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<string, int> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Merge an arbitrary set of ROMs based on the supplied information
|
/// Merge an arbitrary set of ROMs based on the supplied information
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -293,7 +293,7 @@ COMMIT;";
|
|||||||
if (Regex.IsMatch(filename, Constants.NonGoodPattern))
|
if (Regex.IsMatch(filename, Constants.NonGoodPattern))
|
||||||
{
|
{
|
||||||
fileinfo = Regex.Match(filename, Constants.NonGoodPattern).Groups;
|
fileinfo = Regex.Match(filename, Constants.NonGoodPattern).Groups;
|
||||||
if (!Remapping.DatMaps["NonGood"].ContainsKey(fileinfo[1].Value))
|
if (!Mappings.DatMaps["NonGood"].ContainsKey(fileinfo[1].Value))
|
||||||
{
|
{
|
||||||
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as NonGood but could not be mapped.");
|
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as NonGood but could not be mapped.");
|
||||||
return source;
|
return source;
|
||||||
@@ -303,7 +303,7 @@ COMMIT;";
|
|||||||
else if (Regex.IsMatch(filename, Constants.NonGoodSpecialPattern))
|
else if (Regex.IsMatch(filename, Constants.NonGoodSpecialPattern))
|
||||||
{
|
{
|
||||||
fileinfo = Regex.Match(filename, Constants.NonGoodSpecialPattern).Groups;
|
fileinfo = Regex.Match(filename, Constants.NonGoodSpecialPattern).Groups;
|
||||||
if (!Remapping.DatMaps["NonGood"].ContainsKey(fileinfo[1].Value))
|
if (!Mappings.DatMaps["NonGood"].ContainsKey(fileinfo[1].Value))
|
||||||
{
|
{
|
||||||
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as NonGood but could not be mapped.");
|
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as NonGood but could not be mapped.");
|
||||||
return source;
|
return source;
|
||||||
@@ -313,7 +313,7 @@ COMMIT;";
|
|||||||
else if (Regex.IsMatch(filename, Constants.GoodPattern))
|
else if (Regex.IsMatch(filename, Constants.GoodPattern))
|
||||||
{
|
{
|
||||||
fileinfo = Regex.Match(filename, Constants.GoodPattern).Groups;
|
fileinfo = Regex.Match(filename, Constants.GoodPattern).Groups;
|
||||||
if (!Remapping.DatMaps["Good"].ContainsKey(fileinfo[1].Value))
|
if (!Mappings.DatMaps["Good"].ContainsKey(fileinfo[1].Value))
|
||||||
{
|
{
|
||||||
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as Good but could not be mapped.");
|
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as Good but could not be mapped.");
|
||||||
return source;
|
return source;
|
||||||
@@ -323,7 +323,7 @@ COMMIT;";
|
|||||||
else if (Regex.IsMatch(filename, Constants.GoodXmlPattern))
|
else if (Regex.IsMatch(filename, Constants.GoodXmlPattern))
|
||||||
{
|
{
|
||||||
fileinfo = Regex.Match(filename, Constants.GoodXmlPattern).Groups;
|
fileinfo = Regex.Match(filename, Constants.GoodXmlPattern).Groups;
|
||||||
if (!Remapping.DatMaps["Good"].ContainsKey(fileinfo[1].Value))
|
if (!Mappings.DatMaps["Good"].ContainsKey(fileinfo[1].Value))
|
||||||
{
|
{
|
||||||
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as Good but could not be mapped.");
|
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as Good but could not be mapped.");
|
||||||
return source;
|
return source;
|
||||||
@@ -333,7 +333,7 @@ COMMIT;";
|
|||||||
else if (Regex.IsMatch(filename, Constants.MaybeIntroPattern))
|
else if (Regex.IsMatch(filename, Constants.MaybeIntroPattern))
|
||||||
{
|
{
|
||||||
fileinfo = Regex.Match(filename, Constants.MaybeIntroPattern).Groups;
|
fileinfo = Regex.Match(filename, Constants.MaybeIntroPattern).Groups;
|
||||||
if (!Remapping.DatMaps["MaybeIntro"].ContainsKey(fileinfo[1].Value))
|
if (!Mappings.DatMaps["MaybeIntro"].ContainsKey(fileinfo[1].Value))
|
||||||
{
|
{
|
||||||
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as Maybe-Intro but could not be mapped.");
|
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as Maybe-Intro but could not be mapped.");
|
||||||
return source;
|
return source;
|
||||||
@@ -343,7 +343,7 @@ COMMIT;";
|
|||||||
else if (Regex.IsMatch(filename, Constants.NoIntroPattern))
|
else if (Regex.IsMatch(filename, Constants.NoIntroPattern))
|
||||||
{
|
{
|
||||||
fileinfo = Regex.Match(filename, Constants.NoIntroPattern).Groups;
|
fileinfo = Regex.Match(filename, Constants.NoIntroPattern).Groups;
|
||||||
if (!Remapping.DatMaps["NoIntro"].ContainsKey(fileinfo[1].Value))
|
if (!Mappings.DatMaps["NoIntro"].ContainsKey(fileinfo[1].Value))
|
||||||
{
|
{
|
||||||
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as No-Intro but could not be mapped.");
|
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as No-Intro but could not be mapped.");
|
||||||
return source;
|
return source;
|
||||||
@@ -354,7 +354,7 @@ COMMIT;";
|
|||||||
else if (Regex.IsMatch(filename, Constants.NoIntroNumberedPattern))
|
else if (Regex.IsMatch(filename, Constants.NoIntroNumberedPattern))
|
||||||
{
|
{
|
||||||
fileinfo = Regex.Match(filename, Constants.NoIntroNumberedPattern).Groups;
|
fileinfo = Regex.Match(filename, Constants.NoIntroNumberedPattern).Groups;
|
||||||
if (!Remapping.DatMaps["NoIntro"].ContainsKey(fileinfo[1].Value))
|
if (!Mappings.DatMaps["NoIntro"].ContainsKey(fileinfo[1].Value))
|
||||||
{
|
{
|
||||||
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as No-Intro but could not be mapped.");
|
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as No-Intro but could not be mapped.");
|
||||||
return source;
|
return source;
|
||||||
@@ -365,7 +365,7 @@ COMMIT;";
|
|||||||
else if (Regex.IsMatch(filename, Constants.NoIntroSpecialPattern))
|
else if (Regex.IsMatch(filename, Constants.NoIntroSpecialPattern))
|
||||||
{
|
{
|
||||||
fileinfo = Regex.Match(filename, Constants.NoIntroSpecialPattern).Groups;
|
fileinfo = Regex.Match(filename, Constants.NoIntroSpecialPattern).Groups;
|
||||||
if (!Remapping.DatMaps["NoIntro"].ContainsKey(fileinfo[1].Value))
|
if (!Mappings.DatMaps["NoIntro"].ContainsKey(fileinfo[1].Value))
|
||||||
{
|
{
|
||||||
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as No-Intro but could not be mapped.");
|
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as No-Intro but could not be mapped.");
|
||||||
return source;
|
return source;
|
||||||
@@ -375,7 +375,7 @@ COMMIT;";
|
|||||||
else if (Regex.IsMatch(filename, Constants.RedumpPattern))
|
else if (Regex.IsMatch(filename, Constants.RedumpPattern))
|
||||||
{
|
{
|
||||||
fileinfo = Regex.Match(filename, Constants.RedumpPattern).Groups;
|
fileinfo = Regex.Match(filename, Constants.RedumpPattern).Groups;
|
||||||
if (!Remapping.DatMaps["Redump"].ContainsKey(fileinfo[1].Value))
|
if (!Mappings.DatMaps["Redump"].ContainsKey(fileinfo[1].Value))
|
||||||
{
|
{
|
||||||
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as Redump but could not be mapped.");
|
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as Redump but could not be mapped.");
|
||||||
return source;
|
return source;
|
||||||
@@ -386,7 +386,7 @@ COMMIT;";
|
|||||||
else if (Regex.IsMatch(filename, Constants.RedumpBiosPattern))
|
else if (Regex.IsMatch(filename, Constants.RedumpBiosPattern))
|
||||||
{
|
{
|
||||||
fileinfo = Regex.Match(filename, Constants.RedumpBiosPattern).Groups;
|
fileinfo = Regex.Match(filename, Constants.RedumpBiosPattern).Groups;
|
||||||
if (!Remapping.DatMaps["Redump"].ContainsKey(fileinfo[1].Value))
|
if (!Mappings.DatMaps["Redump"].ContainsKey(fileinfo[1].Value))
|
||||||
{
|
{
|
||||||
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as Redump but could not be mapped.");
|
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as Redump but could not be mapped.");
|
||||||
return source;
|
return source;
|
||||||
@@ -396,16 +396,16 @@ COMMIT;";
|
|||||||
else if (Regex.IsMatch(filename, Constants.TosecPattern))
|
else if (Regex.IsMatch(filename, Constants.TosecPattern))
|
||||||
{
|
{
|
||||||
fileinfo = Regex.Match(filename, Constants.TosecPattern).Groups;
|
fileinfo = Regex.Match(filename, Constants.TosecPattern).Groups;
|
||||||
if (!Remapping.DatMaps["TOSEC"].ContainsKey(fileinfo[1].Value))
|
if (!Mappings.DatMaps["TOSEC"].ContainsKey(fileinfo[1].Value))
|
||||||
{
|
{
|
||||||
// Handle special case mappings found only in TOSEC
|
// Handle special case mappings found only in TOSEC
|
||||||
fileinfo = Regex.Match(filename, Constants.TosecSpecialPatternA).Groups;
|
fileinfo = Regex.Match(filename, Constants.TosecSpecialPatternA).Groups;
|
||||||
|
|
||||||
if (!Remapping.DatMaps["TOSEC"].ContainsKey(fileinfo[1].Value))
|
if (!Mappings.DatMaps["TOSEC"].ContainsKey(fileinfo[1].Value))
|
||||||
{
|
{
|
||||||
fileinfo = Regex.Match(filename, Constants.TosecSpecialPatternB).Groups;
|
fileinfo = Regex.Match(filename, Constants.TosecSpecialPatternB).Groups;
|
||||||
|
|
||||||
if (!Remapping.DatMaps["TOSEC"].ContainsKey(fileinfo[1].Value))
|
if (!Mappings.DatMaps["TOSEC"].ContainsKey(fileinfo[1].Value))
|
||||||
{
|
{
|
||||||
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as TOSEC but could not be mapped.");
|
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as TOSEC but could not be mapped.");
|
||||||
return source;
|
return source;
|
||||||
@@ -417,7 +417,7 @@ COMMIT;";
|
|||||||
else if (Regex.IsMatch(filename, Constants.TruripPattern))
|
else if (Regex.IsMatch(filename, Constants.TruripPattern))
|
||||||
{
|
{
|
||||||
fileinfo = Regex.Match(filename, Constants.TruripPattern).Groups;
|
fileinfo = Regex.Match(filename, Constants.TruripPattern).Groups;
|
||||||
if (!Remapping.DatMaps["TruRip"].ContainsKey(fileinfo[1].Value))
|
if (!Mappings.DatMaps["TruRip"].ContainsKey(fileinfo[1].Value))
|
||||||
{
|
{
|
||||||
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as TruRip but could not be mapped.");
|
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as TruRip but could not be mapped.");
|
||||||
return source;
|
return source;
|
||||||
@@ -441,7 +441,7 @@ COMMIT;";
|
|||||||
else if (Regex.IsMatch(filename, Constants.MamePattern))
|
else if (Regex.IsMatch(filename, Constants.MamePattern))
|
||||||
{
|
{
|
||||||
fileinfo = Regex.Match(filename, Constants.MamePattern).Groups;
|
fileinfo = Regex.Match(filename, Constants.MamePattern).Groups;
|
||||||
if (!Remapping.DatMaps["MAME"].ContainsKey(fileinfo[1].Value))
|
if (!Mappings.DatMaps["MAME"].ContainsKey(fileinfo[1].Value))
|
||||||
{
|
{
|
||||||
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as MAME but could not be mapped.");
|
_logger.Warning("The filename " + fileinfo[1].Value + " was matched as MAME but could not be mapped.");
|
||||||
return source;
|
return source;
|
||||||
|
|||||||
@@ -377,7 +377,7 @@ namespace SabreTools
|
|||||||
|
|
||||||
// Now get the headerless file if it exists
|
// Now get the headerless file if it exists
|
||||||
int hs = 0;
|
int hs = 0;
|
||||||
RomTools.GetFileHeaderType(input, out hs, _logger);
|
Skippers.GetFileHeaderType(input, out hs, _logger);
|
||||||
if (hs > 0)
|
if (hs > 0)
|
||||||
{
|
{
|
||||||
string newinput = input + ".new";
|
string newinput = input + ".new";
|
||||||
|
|||||||
Reference in New Issue
Block a user