Extract byte swapping helpers from RomGame, cleanup, and add tests

This commit is contained in:
YoshiRulz 2021-04-08 15:30:23 +10:00
parent 214f024d86
commit 1f080be047
No known key found for this signature in database
GPG Key ID: C4DE31C245353FB7
3 changed files with 94 additions and 50 deletions

View File

@ -86,7 +86,13 @@ namespace BizHawk.Client.Common
if (file.Extension == ".z64" || file.Extension == ".n64" || file.Extension == ".v64")
{
RomData = MutateSwapN64(RomData);
// Use a simple magic number to detect N64 rom format, then byteswap the ROM to ensure a consistent endianness/order
RomData = RomData[0] switch
{
0x37 => EndiannessUtils.ByteSwap16(RomData), // V64 format (byte swapped)
0x40 => EndiannessUtils.ByteSwap32(RomData), // N64 format (word swapped)
_ => RomData // Z64 format (no swap), or something unexpected; in either case do nothing
};
}
// note: this will be taking several hashes, of a potentially large amount of data.. yikes!
@ -137,55 +143,6 @@ namespace BizHawk.Client.Common
return output;
}
private static unsafe byte[] MutateSwapN64(byte[] source)
{
// N64 roms are in one of the following formats:
// .Z64 = No swapping
// .N64 = Word Swapped
// .V64 = Byte Swapped
// File extension does not always match the format
int size = source.Length;
// V64 format
fixed (byte* pSource = &source[0])
{
if (pSource[0] == 0x37)
{
for (int i = 0; i < size; i += 2)
{
byte temp = pSource[i];
pSource[i] = pSource[i + 1];
pSource[i + 1] = temp;
}
}
// N64 format
else if (pSource[0] == 0x40)
{
for (int i = 0; i < size; i += 4)
{
// output[i] = source[i + 3];
// output[i + 3] = source[i];
// output[i + 1] = source[i + 2];
// output[i + 2] = source[i + 1];
byte temp = pSource[i];
pSource[i] = pSource[i + 3];
pSource[i + 3] = temp;
temp = pSource[i + 1];
pSource[i + 1] = pSource[i + 2];
pSource[i + 2] = temp;
}
}
else // Z64 format (or some other unknown format)
{
}
}
return source;
}
private void CheckForPatchOptions()
{
try

View File

@ -0,0 +1,57 @@
using System;
using System.Diagnostics;
namespace BizHawk.Common
{
public static unsafe class EndiannessUtils
{
/// <summary>reverses pairs of octets: <c>0xAABBIIJJPPQQYYZZ</c> &lt;=> <c>0xBBAAJJIIQQPPZZYY</c></summary>
public static byte[] ByteSwap16(byte[] a)
{
var l = a.Length;
var copy = new byte[l];
Array.Copy(a, copy, l);
MutatingByteSwap16(copy);
return copy;
}
/// <summary>reverses groups of 4 octets: <c>0xAABBCCDDWWXXYYZZ</c> &lt;=> <c>0xDDCCBBAAZZYYXXWW</c></summary>
public static byte[] ByteSwap32(byte[] a)
{
var l = a.Length;
var copy = new byte[l];
Array.Copy(a, copy, l);
MutatingByteSwap32(copy);
return copy;
}
/// <summary>reverses pairs of octets in-place: <c>0xAABBIIJJPPQQYYZZ</c> &lt;=> <c>0xBBAAJJIIQQPPZZYY</c></summary>
public static void MutatingByteSwap16(byte[] a)
{
var l = a.Length;
Debug.Assert(l % 2 == 0);
fixed (byte* p = &a[0]) for (var i = 0; i < l; i += 2)
{
var b = p[i];
p[i] = p[i + 1];
p[i + 1] = b;
}
}
/// <summary>reverses groups of 4 octets in-place: <c>0xAABBCCDDWWXXYYZZ</c> &lt;=> <c>0xDDCCBBAAZZYYXXWW</c></summary>
public static void MutatingByteSwap32(byte[] a)
{
var l = a.Length;
Debug.Assert(l % 4 == 0);
fixed (byte* p = &a[0]) for (var i = 0; i < l; i += 4)
{
var b = p[i];
p[i] = p[i + 3];
p[i + 3] = b;
b = p[i + 1];
p[i + 1] = p[i + 2];
p[i + 2] = b;
}
}
}
}

View File

@ -0,0 +1,30 @@
using System.Linq;
using BizHawk.Common;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace BizHawk.Tests.Common
{
[TestClass]
public sealed class EndiannessUtilsTests
{
private static readonly byte[] expected = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF };
[TestMethod]
public void TestByteSwap16()
{
var a = new byte[] { 0x23, 0x01, 0x67, 0x45, 0xAB, 0x89, 0xEF, 0xCD };
EndiannessUtils.MutatingByteSwap16(a);
Assert.IsTrue(a.SequenceEqual(expected));
}
[TestMethod]
public void TestByteSwap32()
{
var a = new byte[] { 0x67, 0x45, 0x23, 0x01, 0xEF, 0xCD, 0xAB, 0x89 };
EndiannessUtils.MutatingByteSwap32(a);
Assert.IsTrue(a.SequenceEqual(expected));
}
}
}