diff --git a/SabreTools.Data.Extensions.Test/NintendoDiscExtensionsTests.cs b/SabreTools.Data.Extensions.Test/NintendoDiscExtensionsTests.cs new file mode 100644 index 00000000..6a4861d2 --- /dev/null +++ b/SabreTools.Data.Extensions.Test/NintendoDiscExtensionsTests.cs @@ -0,0 +1,129 @@ +using SabreTools.Data.Models.NintendoDisc; +using Xunit; + +namespace SabreTools.Data.Extensions.Test +{ + public class NintendoDiscExtensionsTests + { + #region GetPlatform + + [Fact] + public void GetPlatform_Object_WiiMagic_Wii() + { + var header = new DiscHeader { WiiMagic = Constants.WiiMagicWord }; + Platform actual = header.GetPlatform(); + Assert.Equal(Platform.Wii, actual); + } + + [Fact] + public void GetPlatform_Object_GCMagic_GC() + { + var header = new DiscHeader { GCMagic = Constants.GCMagicWord }; + Platform actual = header.GetPlatform(); + Assert.Equal(Platform.GameCube, actual); + } + + [Fact] + public void GetPlatform_Object_ValidGameID_GC() + { + var header = new DiscHeader { GameId = "GABC01" }; + Platform actual = header.GetPlatform(); + Assert.Equal(Platform.GameCube, actual); + } + + [Fact] + public void GetPlatform_Object_InvalidGameID_Unknown() + { + var header = new DiscHeader { GameId = "XABC01" }; + Platform actual = header.GetPlatform(); + Assert.Equal(Platform.Unknown, actual); + } + + [Fact] + public void GetPlatform_Object_DefaultHeader_Unknown() + { + var header = new DiscHeader(); + Platform actual = header.GetPlatform(); + Assert.Equal(Platform.Unknown, actual); + } + + [Fact] + public void GetPlatform_Bytes_WiiMagic_Wii() + { + byte[] header = + [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00-0x07 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x08-0x0F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x10-0x17 + 0x5D, 0x1C, 0x9E, 0xA3, 0x00, 0x00, 0x00, 0x00, // 0x18-0x1F + ]; + + Platform actual = header.GetPlatform(); + Assert.Equal(Platform.Wii, actual); + } + + [Fact] + public void GetPlatform_Bytes_GCMagic_GC() + { + byte[] header = + [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00-0x07 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x08-0x0F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x10-0x17 + 0x00, 0x00, 0x00, 0x00, 0xC2, 0x33, 0x9F, 0x3D, // 0x18-0x1F + ]; + + Platform actual = header.GetPlatform(); + Assert.Equal(Platform.GameCube, actual); + } + + [Fact] + public void GetPlatform_Bytes_ValidGameID_GC() + { + byte[] header = [(byte)'G', (byte)'A', (byte)'B', (byte)'C']; + + Platform actual = header.GetPlatform(); + Assert.Equal(Platform.GameCube, actual); + } + + [Fact] + public void GetPlatform_Bytes_InvalidGameID_Unknown() + { + byte[] header = [(byte)'G', (byte)'A', (byte)'B', 0x00]; + + Platform actual = header.GetPlatform(); + Assert.Equal(Platform.Unknown, actual); + } + + [Fact] + public void GetPlatform_Bytes_DefaultHeader_Unknown() + { + byte[] header = []; + + Platform actual = header.GetPlatform(); + Assert.Equal(Platform.Unknown, actual); + } + + #endregion + + #region IsGameCubeTitleType + + [Theory] + [InlineData('\0', false)] + [InlineData('a', false)] + [InlineData('A', false)] + [InlineData('d', false)] + [InlineData('D', true)] + [InlineData('g', false)] + [InlineData('G', true)] + [InlineData('r', false)] + [InlineData('R', true)] + public void IsGameCubeTitleTypeTest(char c, bool expected) + { + bool actual = c.IsGameCubeTitleType(); + Assert.Equal(expected, actual); + } + + #endregion + } +} diff --git a/SabreTools.Data.Extensions/NintendoDiscExtensions.cs b/SabreTools.Data.Extensions/NintendoDiscExtensions.cs index f238808d..d7e1223d 100644 --- a/SabreTools.Data.Extensions/NintendoDiscExtensions.cs +++ b/SabreTools.Data.Extensions/NintendoDiscExtensions.cs @@ -1,8 +1,8 @@ using SabreTools.Data.Models.NintendoDisc; +using SabreTools.Numerics.Extensions; namespace SabreTools.Data.Extensions { - // TODO: Write tests for these public static class NintendoDiscExtensions { /// @@ -14,12 +14,51 @@ namespace SabreTools.Data.Extensions return Platform.Wii; else if (header.GCMagic == Constants.GCMagicWord) return Platform.GameCube; - else if (header.GameId is not null && header.GameId.Length >= 1 && IsGameCubeTitleType(header.GameId[0])) + else if (header.GameId.Length >= 1 && IsGameCubeTitleType(header.GameId[0])) return Platform.GameCube; else return Platform.Unknown; } + /// + /// Get the platform associated with a disc header + /// + public static Platform GetPlatform(this byte[] header) + { + // Check for Wii magic bytes + if (header.Length >= 0x1C) + { + int offset = 0x18; + uint magic = header.ReadUInt32BigEndian(ref offset); + if (magic == Constants.WiiMagicWord) + return Platform.Wii; + } + + // Check for GameCube magic bytes + if (header.Length >= 0x20) + { + int offset = 0x1C; + uint magic = header.ReadUInt32BigEndian(ref offset); + if (magic == Constants.GCMagicWord) + return Platform.GameCube; + } + + // Check for a valid game ID + if (header.Length >= 4) + { + for (int i = 0; i < 4; i++) + { + byte c = header[i]; + if (!((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))) + return Platform.Unknown; + } + + return Platform.GameCube; + } + + return Platform.Unknown; + } + /// /// Returns true if the GameId first character is a known GameCube title type prefix. /// Used as a fallback when the GC magic word is absent from the disc image. diff --git a/SabreTools.Wrappers/WIA.Writing.cs b/SabreTools.Wrappers/WIA.Writing.cs index 0bb31d86..4a879db7 100644 --- a/SabreTools.Wrappers/WIA.Writing.cs +++ b/SabreTools.Wrappers/WIA.Writing.cs @@ -4,6 +4,7 @@ using System.IO; #if NET462_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER using System.Threading.Tasks; #endif +using SabreTools.Data.Extensions; using SabreTools.Data.Models.NintendoDisc; using SabreTools.Data.Models.WIA; using SabreTools.Hashing; @@ -136,7 +137,7 @@ namespace SabreTools.Wrappers if (discHdr is null) return false; - Platform platform = DetectWiaPlatform(discHdr); + Platform platform = discHdr.GetPlatform(); return platform switch { Platform.GameCube => WriteGameCube(source, dest, isRvz, compressionType, compressionLevel, chunkSize, isoSize, discHdr), @@ -1562,41 +1563,6 @@ namespace SabreTools.Wrappers #region Helpers - /// - /// - /// - /// - /// - /// TODO: Can this be replaced by - private static Platform DetectWiaPlatform(byte[] header) - { - if (header.Length >= 0x1C) - { - uint wiiMagic = (uint)((header[0x18] << 24) | (header[0x19] << 16) | (header[0x1A] << 8) | header[0x1B]); - if (wiiMagic == WiiMagicWord) - return Platform.Wii; - } - - if (header.Length >= 4) - { - bool valid = true; - for (int i = 0; i < 4; i++) - { - char c = (char)header[i]; - if (!((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))) - { - valid = false; - break; - } - } - - if (valid) - return Platform.GameCube; - } - - return Platform.Unknown; - } - /// /// ///