Minor refactors to byteswapping (N64 rom loading and Lua bit library)

This commit is contained in:
YoshiRulz 2022-10-11 07:44:14 +10:00
parent bb4ba2184f
commit 82c3b471a5
No known key found for this signature in database
GPG Key ID: C4DE31C245353FB7
6 changed files with 52 additions and 49 deletions

View File

@ -1,3 +1,4 @@
; these are for the .z64 (no byte swapping) format -- EmuHawk automatically converts .v64 and .n64 to that format before hashing
167C3C433DEC1F1EB921736F7D53FAC8CB45EE31 G 007 - GoldenEye (Europe) N64 Glide_fb_smart=true;Glide64mk2_enable_hacks_for_game=8;Glide64mk2_filtering=1;Glide64mk2_fb_smart=true;Jabo_Clear_Frame=2
2A5DADE32F7FAD6C73C659D2026994632C1B3174 G 007 - GoldenEye (Japan) N64 Glide_fb_smart=true;Glide64mk2_enable_hacks_for_game=8;Glide64mk2_filtering=1;Glide64mk2_fb_smart=true
ABE01E4AEB033B6C0836819F549C791B26CFDE83 G 007 - GoldenEye (USA) N64 RiceEnableHacksForGame=19;RiceZHack=true;RiceFrameBufferOption=1;RiceScreenUpdateSettingHack=4;Glide_fb_smart=true;Glide64mk2_enable_hacks_for_game=8;Glide64mk2_filtering=1;Glide64mk2_fb_smart=true;Jabo_Clear_Frame=2

View File

@ -96,16 +96,7 @@ namespace BizHawk.Client.Common
RomData = DeInterleaveSMD(RomData);
}
if (file.Extension == ".z64" || file.Extension == ".n64" || file.Extension == ".v64")
{
// 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
};
}
if (file.Extension is ".n64" or ".v64" or ".z64") N64RomByteswapper.ToZ64Native(RomData); //TODO don't use file extension for N64 rom detection (yes that means detecting all formats before converting to Z64)
// note: this will be taking several hashes, of a potentially large amount of data.. yikes!
GameInfo = Database.GetGameInfo(RomData, file.Name);

View File

@ -1,4 +1,5 @@
using System;
using System.Buffers.Binary;
using System.ComponentModel;
// ReSharper disable UnusedMember.Global
@ -99,26 +100,16 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local usbitbyt = bit.byteswap_16( 100 );")]
[LuaMethod("byteswap_16", "Byte swaps 'short', i.e. bit.byteswap_16(0xFF00) would return 0x00FF")]
public static ushort Byteswap16(ushort val)
{
return (ushort)((val & 0xFFU) << 8 | (val & 0xFF00U) >> 8);
}
=> BinaryPrimitives.ReverseEndianness(val);
[LuaMethodExample("local uibitbyt = bit.byteswap_32( 1000 );")]
[LuaMethod("byteswap_32", "Byte swaps 'dword'")]
public static uint Byteswap32(uint val)
{
return (val & 0x000000FFU) << 24 | (val & 0x0000FF00U) << 8 |
(val & 0x00FF0000U) >> 8 | (val & 0xFF000000U) >> 24;
}
=> BinaryPrimitives.ReverseEndianness(val);
[LuaMethodExample("local ulbitbyt = bit.byteswap_64( 10000 );")]
[LuaMethod("byteswap_64", "Byte swaps 'long'")]
[LuaMethod("byteswap_64", "Byte swaps 'long' (NOTE: You may get unexpected results for large numbers! In current Lua engines, all numbers are double-precision floating-point, even if you intended to use integers.)")]
public static ulong Byteswap64(ulong val)
{
return (val & 0x00000000000000FFUL) << 56 | (val & 0x000000000000FF00UL) << 40 |
(val & 0x0000000000FF0000UL) << 24 | (val & 0x00000000FF000000UL) << 8 |
(val & 0x000000FF00000000UL) >> 8 | (val & 0x0000FF0000000000UL) >> 24 |
(val & 0x00FF000000000000UL) >> 40 | (val & 0xFF00000000000000UL) >> 56;
}
=> BinaryPrimitives.ReverseEndianness(val);
}
}

View File

@ -5,29 +5,10 @@ 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)
public static void MutatingByteSwap16(Span<byte> a)
{
#if true //TODO benchmark (both methods work correctly); also there is another method involving shifting the (output copy of the) array over by 1 byte then manually writing every second byte
var l = a.Length;
Debug.Assert(l % 2 == 0);
fixed (byte* p = &a[0]) for (var i = 0; i < l; i += 2)
@ -36,11 +17,17 @@ namespace BizHawk.Common
p[i] = p[i + 1];
p[i + 1] = b;
}
#else
Debug.Assert(a.Length % 2 == 0);
var shorts = MemoryMarshal.Cast<byte, ushort>(a);
for (var i = 0; i < shorts.Length; i++) shorts[i] = BinaryPrimitives.ReverseEndianness(shorts[i]);
#endif
}
/// <summary>reverses groups of 4 octets in-place: <c>0xAABBCCDDWWXXYYZZ</c> &lt;=> <c>0xDDCCBBAAZZYYXXWW</c></summary>
public static void MutatingByteSwap32(byte[] a)
public static void MutatingByteSwap32(Span<byte> a)
{
#if true //TODO benchmark (both methods work correctly)
var l = a.Length;
Debug.Assert(l % 4 == 0);
fixed (byte* p = &a[0]) for (var i = 0; i < l; i += 4)
@ -52,6 +39,11 @@ namespace BizHawk.Common
p[i + 1] = p[i + 2];
p[i + 2] = b;
}
#else
Debug.Assert(a.Length % 4 == 0);
var ints = MemoryMarshal.Cast<byte, uint>(a);
for (var i = 0; i < ints.Length; i++) ints[i] = BinaryPrimitives.ReverseEndianness(ints[i]);
#endif
}
}
}

View File

@ -0,0 +1,22 @@
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Common
{
/// <summary>Uses a simple magic number to detect N64 rom format, then byteswaps the ROM to ensure a consistent endianness/order</summary>
/// <remarks>http://n64dev.org/romformats.html</remarks>
public static class N64RomByteswapper
{
/// <remarks>not actually magic, just always the same in commercial carts? https://n64brew.dev/wiki/ROM_Header works all the same</remarks>
private static readonly byte[] MAGIC_BYTES = { 0x80, 0x37, 0x12, 0x40 };
/// <summary>ensures <paramref name="rom"/> is in the native (<c>.z64</c>) format, mutating it in-place if necessary</summary>
public static void ToZ64Native(Span<byte> rom)
{
if (rom[0] == MAGIC_BYTES[1]) EndiannessUtils.MutatingByteSwap16(rom); // byte-swapped (.v64)
else if (rom[0] == MAGIC_BYTES[3]) EndiannessUtils.MutatingByteSwap32(rom); // rare little-endian .n64
// else already native (.z64)
}
}
}

View File

@ -1,4 +1,4 @@
using System.Linq;
using System;
using BizHawk.Common;
@ -14,17 +14,23 @@ namespace BizHawk.Tests.Common
[TestMethod]
public void TestByteSwap16()
{
var a = new byte[] { 0x23, 0x01, 0x67, 0x45, 0xAB, 0x89, 0xEF, 0xCD };
var b = new byte[] { 0x23, 0x01, 0x67, 0x45, 0xAB, 0x89, 0xEF, 0xCD }.AsSpan();
var a = b.ToArray().AsSpan();
EndiannessUtils.MutatingByteSwap16(a);
Assert.IsTrue(a.SequenceEqual(expected));
EndiannessUtils.MutatingByteSwap16(a);
Assert.IsTrue(a.SequenceEqual(b));
}
[TestMethod]
public void TestByteSwap32()
{
var a = new byte[] { 0x67, 0x45, 0x23, 0x01, 0xEF, 0xCD, 0xAB, 0x89 };
var b = new byte[] { 0x67, 0x45, 0x23, 0x01, 0xEF, 0xCD, 0xAB, 0x89 }.AsSpan();
var a = b.ToArray().AsSpan();
EndiannessUtils.MutatingByteSwap32(a);
Assert.IsTrue(a.SequenceEqual(expected));
EndiannessUtils.MutatingByteSwap32(a);
Assert.IsTrue(a.SequenceEqual(b));
}
}
}