Extract byte swapping helpers from RomGame, cleanup, and add tests
This commit is contained in:
parent
214f024d86
commit
1f080be047
|
@ -86,7 +86,13 @@ namespace BizHawk.Client.Common
|
||||||
|
|
||||||
if (file.Extension == ".z64" || file.Extension == ".n64" || file.Extension == ".v64")
|
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!
|
// 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;
|
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()
|
private void CheckForPatchOptions()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
@ -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> <=> <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> <=> <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> <=> <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> <=> <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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue