2016-10-03 21:16:59 -07:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
2020-06-10 22:37:19 -07:00
|
|
|
|
using System.IO;
|
2016-10-24 13:51:39 -07:00
|
|
|
|
|
2017-05-04 02:41:11 -07:00
|
|
|
|
using SabreTools.Library.Data;
|
2020-08-01 23:04:11 -07:00
|
|
|
|
using SabreTools.Library.IO;
|
2016-10-03 21:16:59 -07:00
|
|
|
|
|
2017-05-04 02:41:11 -07:00
|
|
|
|
namespace SabreTools.Library.Skippers
|
2016-10-03 21:16:59 -07:00
|
|
|
|
{
|
2019-02-08 21:01:54 -08:00
|
|
|
|
public class SkipperRule
|
|
|
|
|
|
{
|
|
|
|
|
|
#region Fields
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Starting offset for applying rule
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public long? StartOffset { get; set; } // null is EOF
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Ending offset for applying rule
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public long? EndOffset { get; set; } // null if EOF
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Byte manipulation operation
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public HeaderSkipOperation Operation { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// List of matching tests in a rule
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public List<SkipperTest> Tests { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Filename the skipper rule lives in
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string SourceFile { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2020-07-30 22:32:16 -07:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Check if a Stream passes all tests in the SkipperRule
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="input">Stream to check</param>
|
|
|
|
|
|
/// <returns>True if all tests passed, false otherwise</returns>
|
|
|
|
|
|
public bool PassesAllTests(Stream input)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool success = true;
|
|
|
|
|
|
foreach (SkipperTest test in Tests)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool result = test.Passes(input);
|
|
|
|
|
|
success &= result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return success;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-02-08 21:01:54 -08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Transform an input file using the given rule
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="input">Input file name</param>
|
|
|
|
|
|
/// <param name="output">Output file name</param>
|
|
|
|
|
|
/// <returns>True if the file was transformed properly, false otherwise</returns>
|
|
|
|
|
|
public bool TransformFile(string input, string output)
|
|
|
|
|
|
{
|
|
|
|
|
|
// If the input file doesn't exist, fail
|
|
|
|
|
|
if (!File.Exists(input))
|
|
|
|
|
|
{
|
2020-06-10 22:37:19 -07:00
|
|
|
|
Globals.Logger.Error($"I'm sorry but '{input}' doesn't exist!");
|
2019-02-08 21:01:54 -08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Create the output directory if it doesn't already
|
2020-07-15 09:41:59 -07:00
|
|
|
|
DirectoryExtensions.Ensure(Path.GetDirectoryName(output));
|
2019-02-08 21:01:54 -08:00
|
|
|
|
|
2020-06-10 22:37:19 -07:00
|
|
|
|
Globals.Logger.User($"Attempting to apply rule to '{input}'");
|
2020-07-15 09:41:59 -07:00
|
|
|
|
bool success = TransformStream(FileExtensions.TryOpenRead(input), FileExtensions.TryCreate(output));
|
2019-02-08 21:01:54 -08:00
|
|
|
|
|
|
|
|
|
|
// If the output file has size 0, delete it
|
|
|
|
|
|
if (new FileInfo(output).Length == 0)
|
|
|
|
|
|
{
|
2020-07-15 09:41:59 -07:00
|
|
|
|
FileExtensions.TryDelete(output);
|
2019-02-08 21:01:54 -08:00
|
|
|
|
success = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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="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, 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)))
|
|
|
|
|
|
{
|
|
|
|
|
|
Globals.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
|
|
|
|
|
|
{
|
|
|
|
|
|
Globals.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;
|
2020-06-10 22:37:19 -07:00
|
|
|
|
|
2019-02-08 21:01:54 -08:00
|
|
|
|
else if (Math.Abs((long)StartOffset) > input.Length)
|
|
|
|
|
|
success = false;
|
2020-06-10 22:37:19 -07:00
|
|
|
|
|
2019-02-08 21:01:54 -08:00
|
|
|
|
else if (StartOffset > 0)
|
|
|
|
|
|
input.Seek((long)StartOffset, SeekOrigin.Begin);
|
2020-06-10 22:37:19 -07:00
|
|
|
|
|
2019-02-08 21:01:54 -08:00
|
|
|
|
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 ?? 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;
|
2020-06-10 22:37:19 -07:00
|
|
|
|
|
2019-02-08 21:01:54 -08:00
|
|
|
|
case HeaderSkipOperation.Byteswap:
|
|
|
|
|
|
if (pos % 2 == 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
buffer[pos - 1] = b;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (pos % 2 == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
buffer[pos + 1] = b;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
2020-06-10 22:37:19 -07:00
|
|
|
|
|
2019-02-08 21:01:54 -08:00
|
|
|
|
case HeaderSkipOperation.Wordswap:
|
|
|
|
|
|
buffer[3 - pos] = b;
|
|
|
|
|
|
break;
|
2020-06-10 22:37:19 -07:00
|
|
|
|
|
2019-02-08 21:01:54 -08:00
|
|
|
|
case HeaderSkipOperation.WordByteswap:
|
|
|
|
|
|
buffer[(pos + 2) % 4] = b;
|
|
|
|
|
|
break;
|
2020-06-10 22:37:19 -07:00
|
|
|
|
|
2019-02-08 21:01:54 -08:00
|
|
|
|
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];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-06-10 22:37:19 -07:00
|
|
|
|
|
2019-02-08 21:01:54 -08:00
|
|
|
|
// 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)
|
|
|
|
|
|
{
|
2020-09-15 14:38:37 -07:00
|
|
|
|
Globals.Logger.Error(ex);
|
2019-02-08 21:01:54 -08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2016-10-03 21:16:59 -07:00
|
|
|
|
}
|