diff --git a/CSharp/Content/UniversalEditor.Content.PlatformIndependent/Extensions/GameDeveloper/Associations/FileSystem/Nintendo/Optical/NintendoOpticalDisc.xml b/CSharp/Content/UniversalEditor.Content.PlatformIndependent/Extensions/GameDeveloper/Associations/FileSystem/Nintendo/Optical/NintendoOpticalDisc.xml new file mode 100644 index 00000000..b5d49112 --- /dev/null +++ b/CSharp/Content/UniversalEditor.Content.PlatformIndependent/Extensions/GameDeveloper/Associations/FileSystem/Nintendo/Optical/NintendoOpticalDisc.xml @@ -0,0 +1,20 @@ + + + + + + + + *.gcm + + + + + + + + + + + + \ No newline at end of file diff --git a/CSharp/Content/UniversalEditor.Content.PlatformIndependent/UniversalEditor.Content.PlatformIndependent.csproj b/CSharp/Content/UniversalEditor.Content.PlatformIndependent/UniversalEditor.Content.PlatformIndependent.csproj index dc493c3c..60ed5f88 100644 --- a/CSharp/Content/UniversalEditor.Content.PlatformIndependent/UniversalEditor.Content.PlatformIndependent.csproj +++ b/CSharp/Content/UniversalEditor.Content.PlatformIndependent/UniversalEditor.Content.PlatformIndependent.csproj @@ -172,6 +172,7 @@ + diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Nintendo/DataFormats/FileSystem/Nintendo/Optical/NintendoOpticalDiscDataFormat.cs b/CSharp/Plugins/UniversalEditor.Plugins.Nintendo/DataFormats/FileSystem/Nintendo/Optical/NintendoOpticalDiscDataFormat.cs new file mode 100644 index 00000000..674bbfa8 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Nintendo/DataFormats/FileSystem/Nintendo/Optical/NintendoOpticalDiscDataFormat.cs @@ -0,0 +1,260 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UniversalEditor.IO; +using UniversalEditor.ObjectModels.FileSystem; + +namespace UniversalEditor.DataFormats.FileSystem.Nintendo.Optical +{ + public class NintendoOpticalDiscDataFormat : DataFormat + { + private static DataFormatReference _dfr = null; + protected override DataFormatReference MakeReferenceInternal() + { + if (_dfr == null) + { + _dfr = base.MakeReferenceInternal(); + _dfr.Capabilities.Add(typeof(FileSystemObjectModel), DataFormatCapabilities.All); + _dfr.ExportOptions.Add(new CustomOptionChoice("FormatCode", "&Format code:", true, new CustomOptionFieldChoice[] + { + new CustomOptionFieldChoice(NintendoOpticalDiscFormatCodes.Revolution), + new CustomOptionFieldChoice(NintendoOpticalDiscFormatCodes.Wii, true), + new CustomOptionFieldChoice(NintendoOpticalDiscFormatCodes.GameCube), + new CustomOptionFieldChoice(NintendoOpticalDiscFormatCodes.Utility), + new CustomOptionFieldChoice(NintendoOpticalDiscFormatCodes.GameCubeDemo), + new CustomOptionFieldChoice(NintendoOpticalDiscFormatCodes.GameCubePromotional), + new CustomOptionFieldChoice(NintendoOpticalDiscFormatCodes.Diagnostic), + new CustomOptionFieldChoice(NintendoOpticalDiscFormatCodes.Diagnostic1), + new CustomOptionFieldChoice(NintendoOpticalDiscFormatCodes.WiiBackup), + new CustomOptionFieldChoice(NintendoOpticalDiscFormatCodes.WiiFitChanInstaller) + })); + _dfr.ExportOptions.Add(new CustomOptionText("GameCode", "&Game code:", String.Empty, 2)); + _dfr.ExportOptions.Add(new CustomOptionChoice("RegionCode", "&Region code:", true, new CustomOptionFieldChoice[] + { + new CustomOptionFieldChoice(NintendoOpticalDiscRegionCodes.German), + new CustomOptionFieldChoice(NintendoOpticalDiscRegionCodes.UnitedStates, true), + new CustomOptionFieldChoice(NintendoOpticalDiscRegionCodes.France), + new CustomOptionFieldChoice(NintendoOpticalDiscRegionCodes.Italy), + new CustomOptionFieldChoice(NintendoOpticalDiscRegionCodes.Japan), + new CustomOptionFieldChoice(NintendoOpticalDiscRegionCodes.Korea), + new CustomOptionFieldChoice(NintendoOpticalDiscRegionCodes.PAL), + new CustomOptionFieldChoice(NintendoOpticalDiscRegionCodes.Russia), + new CustomOptionFieldChoice(NintendoOpticalDiscRegionCodes.Spanish), + new CustomOptionFieldChoice(NintendoOpticalDiscRegionCodes.Taiwan), + new CustomOptionFieldChoice(NintendoOpticalDiscRegionCodes.Australia), + })); + + _dfr.ExportOptions.Add(new CustomOptionChoice("SystemType", "&System type:", true, new CustomOptionFieldChoice[] + { + new CustomOptionFieldChoice("GameCube", NintendoOpticalDiscSystemType.GameCube), + new CustomOptionFieldChoice("Wii", NintendoOpticalDiscSystemType.Wii, true) + })); + + _dfr.ExportOptions.Add(new CustomOptionText("GameTitle", "Game &title:", String.Empty, 64)); + + _dfr.Sources.Add("http://wiibrew.org/wiki/Wii_Disc"); + _dfr.Sources.Add("http://www.emutalk.net/threads/21512-GCM-file-extension!/page3"); + } + return _dfr; + } + + private NintendoOpticalDiscFormatCode mvarFormatCode = NintendoOpticalDiscFormatCodes.Wii; + public NintendoOpticalDiscFormatCode FormatCode { get { return mvarFormatCode; } set { mvarFormatCode = value; } } + + private string mvarGameCode = String.Empty; + public string GameCode { get { return mvarGameCode; } set { mvarGameCode = value; } } + + private NintendoOpticalDiscRegionCode mvarRegionCode = NintendoOpticalDiscRegionCodes.UnitedStates; + public NintendoOpticalDiscRegionCode RegionCode { get { return mvarRegionCode; } set { mvarRegionCode = value; } } + + private string mvarMakerCode = String.Empty; + public string MakerCode { get { return mvarMakerCode; } set { mvarMakerCode = value; } } + + private byte mvarDiscNumber = 0; + public byte DiscNumber { get { return mvarDiscNumber; } set { mvarDiscNumber = value; } } + + private byte mvarDiscVersion = 0; + public byte DiscVersion { get { return mvarDiscVersion; } set { mvarDiscVersion = value; } } + + private NintendoOpticalDiscSystemType mvarSystemType = NintendoOpticalDiscSystemType.Wii; + public NintendoOpticalDiscSystemType SystemType { get { return mvarSystemType; } set { mvarSystemType = value; } } + + private string mvarGameTitle = String.Empty; + public string GameTitle { get { return mvarGameTitle; } set { mvarGameTitle = value; } } + + private bool mvarDisableHashVerification = false; + /// + /// Disable hash verification and make all disc reads fail even before they reach the DVD drive. Neither this nor + /// will allow unsigned code. + /// + public bool DisableHashVerification { get { return mvarDisableHashVerification; } set { mvarDisableHashVerification = value; } } + + private bool mvarDisableDiscEncryption = false; + /// + /// Disable disc encryption and h3 hash table loading and verification (which effectively also makes all disc reads fail because + /// the h2 hashes won't be able to verify against "something" that will be in the memory of the h3 hash table. Neither this nor + /// will allow unsigned code. + /// + public bool DisableDiscEncryption { get { return mvarDisableDiscEncryption; } set { mvarDisableDiscEncryption = value; } } + + private static readonly byte[] WII_MAGIC_WORD = new byte[] { 0x5D, 0x1C, 0x9E, 0xA3 }; + private static readonly byte[] GAMECUBE_MAGIC_WORD = new byte[] { 0xC2, 0x33, 0x9F, 0x3D }; + + protected override void LoadInternal(ref ObjectModel objectModel) + { + FileSystemObjectModel fsom = (objectModel as FileSystemObjectModel); + if (fsom == null) throw new ObjectModelNotSupportedException(); + + Reader reader = base.Accessor.Reader; + reader.Endianness = Endianness.BigEndian; + + string gameMagic = reader.ReadFixedLengthString(4); + // gameMagic: 4 chars, [1:Disc ID][2:Game Code][1:Region Code] + mvarFormatCode = NintendoOpticalDiscFormatCode.FromCode(gameMagic[0]); + mvarGameCode = gameMagic.Substring(1, 2); + mvarRegionCode = NintendoOpticalDiscRegionCode.FromCode(gameMagic[3]); + + mvarMakerCode = reader.ReadFixedLengthString(2); + mvarDiscNumber = reader.ReadByte(); + mvarDiscVersion = reader.ReadByte(); + + byte audioStreaming = reader.ReadByte(); + byte streamingBufferSize = reader.ReadByte(); + + byte[] unused = reader.ReadBytes(14); + + byte[] wiiMagicWord = reader.ReadBytes(4); + byte[] gameCubeMagicWord = reader.ReadBytes(4); + + bool isWii = wiiMagicWord.Match(WII_MAGIC_WORD); + bool isGameCube = gameCubeMagicWord.Match(GAMECUBE_MAGIC_WORD); + + if (isWii && isGameCube) + { + Console.WriteLine("Unexpected twist: disc image is BOTH a GameCube and Wii??"); + mvarSystemType = NintendoOpticalDiscSystemType.GameCube | NintendoOpticalDiscSystemType.Wii; + } + else if (isWii) + { + mvarSystemType = NintendoOpticalDiscSystemType.Wii; + } + else if (isGameCube) + { + mvarSystemType = NintendoOpticalDiscSystemType.GameCube; + } + else + { + mvarSystemType = NintendoOpticalDiscSystemType.Unknown; + } + + // from http://wiibrew.org/wiki/Wii_Disc - what does this mean? + // "though most docs claim it to be 0x400 the Wii only reads 0x44 which will be padded by the DI driver to 0x60" + mvarGameTitle = reader.ReadFixedLengthString(64).TrimNull(); + + mvarDisableHashVerification = reader.ReadBoolean(); + mvarDisableDiscEncryption = reader.ReadBoolean(); + + // from emutalk.net + reader.Seek(0x0420, SeekOrigin.Begin); + + int offsetToMainExecutableDOL = reader.ReadInt32(); // offset of main executable DOL + m_OffsetToFST = reader.ReadInt32(); // offset of the FST + + // Apploader at offset 0x00002440 + reader.Seek(0x00002440, SeekOrigin.Begin); + + string apploaderDate = reader.ReadFixedLengthString(16).TrimNull(); + int apploaderEntryPoint = reader.ReadInt32(); + int apploaderCodeSize = reader.ReadInt32(); + int unknown1 = reader.ReadInt32(); + int unknown2 = reader.ReadInt32(); + byte[] apploaderCode = reader.ReadBytes(apploaderCodeSize); + + // Main executable DOL + reader.Seek(offsetToMainExecutableDOL, SeekOrigin.Begin); + + int[] textFileOffsets = reader.ReadInt32Array(7); + int[] dataFileOffsets = reader.ReadInt32Array(7); + int[] textMemoryOffsets = reader.ReadInt32Array(7); + int[] dataMemoryOffsets = reader.ReadInt32Array(7); + int[] textLengths = reader.ReadInt32Array(7); + int[] dataLengths = reader.ReadInt32Array(7); + int bssMemoryAddress = reader.ReadInt32(); + int bssLength = reader.ReadInt32(); + int entryPoint = reader.ReadInt32(); + + reader.Seek(m_OffsetToFST, SeekOrigin.Begin); + int unknownA1 = reader.ReadInt32(); + int unknownA2 = reader.ReadInt32(); + + int fileCount = reader.ReadInt32(); + + m_NameTableOffset = (fileCount * 12); + + reader.Accessor.SavePosition(); + + reader.Seek(m_OffsetToFST + m_NameTableOffset, SeekOrigin.Begin); + m_NameTableEntries.Clear(); + for (int i = 0; i < fileCount; i++) + { + int pos = (int)(reader.Accessor.Position - m_NameTableOffset - m_OffsetToFST); + string value = reader.ReadNullTerminatedString(); + m_NameTableEntries.Add(pos, value); + } + reader.Accessor.LoadPosition(); + + // this doesn't seem right... 16777216??? + do + { + int filesRead = LoadFileSystemObject(reader, fsom); + fileCount -= filesRead; + } + while (fileCount > 1); + } + + private Dictionary m_NameTableEntries = new Dictionary(); + private int m_OffsetToFST = 0; + private int m_NameTableOffset = 0; + + private int LoadFileSystemObject(Reader reader, IFileSystemContainer parent) + { + int relativeNameOffsetAndFlag = reader.ReadInt32(); + int relativeNameOffset = relativeNameOffsetAndFlag; + + bool isDirectory = false; + if ((relativeNameOffsetAndFlag & 0x01000000) == 0x01000000) + { + relativeNameOffset = (int)(relativeNameOffsetAndFlag & ~0x01000000); + isDirectory = true; + } + + string fileName = m_NameTableEntries[relativeNameOffset]; + int diskAddress = reader.ReadInt32(); + int fileSize = reader.ReadInt32(); + + if (isDirectory) + { + int filesRead = 0; + Folder folder = parent.Folders.Add(fileName); + for (int i = 0; i < fileSize; i++) + { + filesRead += LoadFileSystemObject(reader, folder); + } + return filesRead + 1; + } + else + { + File file = parent.Files.Add(fileName); + file.Size = fileSize; + return 1; + } + return 0; + } + + protected override void SaveInternal(ObjectModel objectModel) + { + throw new NotImplementedException(); + } + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Nintendo/DataFormats/FileSystem/Nintendo/Optical/NintendoOpticalDiscFormatCode.cs b/CSharp/Plugins/UniversalEditor.Plugins.Nintendo/DataFormats/FileSystem/Nintendo/Optical/NintendoOpticalDiscFormatCode.cs new file mode 100644 index 00000000..3ab0acf5 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Nintendo/DataFormats/FileSystem/Nintendo/Optical/NintendoOpticalDiscFormatCode.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace UniversalEditor.DataFormats.FileSystem.Nintendo.Optical +{ + /// + /// + /// + /// + public class NintendoOpticalDiscFormatCode + { + private char mvarValue = '\0'; + public char Value { get { return mvarValue; } set { mvarValue = value; } } + + private string mvarTitle = String.Empty; + public string Title { get { return mvarTitle; } set { mvarTitle = value; } } + + public override string ToString() + { + return mvarTitle + " [" + mvarValue.ToString() + "]"; + } + + public NintendoOpticalDiscFormatCode(string title, char value) + { + mvarTitle = title; + mvarValue = value; + } + + + /// + /// Gets the with the given code if valid. + /// + /// The code to search on. + /// If the code is known, returns an instance of the associated . Otherwise, returns null. + public static NintendoOpticalDiscFormatCode FromCode(char value) + { + Type t = typeof(NintendoOpticalDiscFormatCodes); + + MethodAttributes methodAttributes = MethodAttributes.Public | MethodAttributes.Static; + PropertyInfo[] properties = t.GetProperties(); + for (int i = 0; i < properties.Length; i++) + { + PropertyInfo propertyInfo = properties[i]; + if (propertyInfo.PropertyType == typeof(NintendoOpticalDiscFormatCode)) + { + MethodInfo getMethod = propertyInfo.GetGetMethod(); + if (getMethod != null && (getMethod.Attributes & methodAttributes) == methodAttributes) + { + object[] index = null; + NintendoOpticalDiscFormatCode val = (NintendoOpticalDiscFormatCode)propertyInfo.GetValue(null, index); + + if (val.Value == value) return val; + } + } + } + return null; + } + } + public class NintendoOpticalDiscFormatCodes + { + private static NintendoOpticalDiscFormatCode mvarRevolution = new NintendoOpticalDiscFormatCode("Revolution (Wii)", 'R'); + public static NintendoOpticalDiscFormatCode Revolution { get { return mvarRevolution; } } + private static NintendoOpticalDiscFormatCode mvarWii = new NintendoOpticalDiscFormatCode("Wii", 'S'); + public static NintendoOpticalDiscFormatCode Wii { get { return mvarWii; } } + private static NintendoOpticalDiscFormatCode mvarGameCube = new NintendoOpticalDiscFormatCode("GameCube", 'G'); + public static NintendoOpticalDiscFormatCode GameCube { get { return mvarGameCube; } } + private static NintendoOpticalDiscFormatCode mvarUtility = new NintendoOpticalDiscFormatCode("Utility disc / GBA-Player", 'U'); + public static NintendoOpticalDiscFormatCode Utility { get { return mvarUtility; } } + private static NintendoOpticalDiscFormatCode mvarGameCubeDemo = new NintendoOpticalDiscFormatCode("GameCube demo disc (?)", 'D'); + public static NintendoOpticalDiscFormatCode GameCubeDemo { get { return mvarGameCubeDemo; } } + private static NintendoOpticalDiscFormatCode mvarGameCubePromotional = new NintendoOpticalDiscFormatCode("GameCube promotional disc (?)", 'P'); + public static NintendoOpticalDiscFormatCode GameCubePromotional { get { return mvarGameCubePromotional; } } + private static NintendoOpticalDiscFormatCode mvarDiagnostic = new NintendoOpticalDiscFormatCode("Diagnostic disc (auto boot)", '0'); + public static NintendoOpticalDiscFormatCode Diagnostic { get { return mvarDiagnostic; } } + private static NintendoOpticalDiscFormatCode mvarDiagnostic1 = new NintendoOpticalDiscFormatCode("Diagnostic disc (?)", '1'); + public static NintendoOpticalDiscFormatCode Diagnostic1 { get { return mvarDiagnostic1; } } + private static NintendoOpticalDiscFormatCode mvarWiiBackup = new NintendoOpticalDiscFormatCode("Wii Backup disc", '4'); + public static NintendoOpticalDiscFormatCode WiiBackup { get { return mvarWiiBackup; } } + private static NintendoOpticalDiscFormatCode mvarWiiFitChanInstaller = new NintendoOpticalDiscFormatCode("WiiFit-chan installer", '_'); + public static NintendoOpticalDiscFormatCode WiiFitChanInstaller { get { return mvarWiiFitChanInstaller; } } + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Nintendo/DataFormats/FileSystem/Nintendo/Optical/NintendoOpticalDiscRegionCode.cs b/CSharp/Plugins/UniversalEditor.Plugins.Nintendo/DataFormats/FileSystem/Nintendo/Optical/NintendoOpticalDiscRegionCode.cs new file mode 100644 index 00000000..2c4c03ee --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Nintendo/DataFormats/FileSystem/Nintendo/Optical/NintendoOpticalDiscRegionCode.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace UniversalEditor.DataFormats.FileSystem.Nintendo.Optical +{ + /// + /// + /// + /// + public class NintendoOpticalDiscRegionCode + { + private char mvarValue = '\0'; + public char Value { get { return mvarValue; } set { mvarValue = value; } } + + private string mvarTitle = String.Empty; + public string Title { get { return mvarTitle; } set { mvarTitle = value; } } + + public override string ToString() + { + return mvarTitle + " [" + mvarValue.ToString() + "]"; + } + + public NintendoOpticalDiscRegionCode(string title, char value) + { + mvarTitle = title; + mvarValue = value; + } + + + /// + /// Gets the with the given code if valid. + /// + /// The code to search on. + /// If the code is known, returns an instance of the associated . Otherwise, returns null. + public static NintendoOpticalDiscRegionCode FromCode(char value) + { + Type t = typeof(NintendoOpticalDiscRegionCodes); + + MethodAttributes methodAttributes = MethodAttributes.Public | MethodAttributes.Static; + PropertyInfo[] properties = t.GetProperties(); + for (int i = 0; i < properties.Length; i++) + { + PropertyInfo propertyInfo = properties[i]; + if (propertyInfo.PropertyType == typeof(NintendoOpticalDiscRegionCode)) + { + MethodInfo getMethod = propertyInfo.GetGetMethod(); + if (getMethod != null && (getMethod.Attributes & methodAttributes) == methodAttributes) + { + object[] index = null; + NintendoOpticalDiscRegionCode val = (NintendoOpticalDiscRegionCode)propertyInfo.GetValue(null, index); + + if (val.Value == value) return val; + } + } + } + return null; + } + } + public class NintendoOpticalDiscRegionCodes + { + private static NintendoOpticalDiscRegionCode mvarGerman = new NintendoOpticalDiscRegionCode("German", 'D'); + public static NintendoOpticalDiscRegionCode German { get { return mvarGerman; } } + private static NintendoOpticalDiscRegionCode mvarUnitedStates = new NintendoOpticalDiscRegionCode("United States", 'E'); + public static NintendoOpticalDiscRegionCode UnitedStates { get { return mvarUnitedStates; } } + private static NintendoOpticalDiscRegionCode mvarFrance = new NintendoOpticalDiscRegionCode("France", 'F'); + public static NintendoOpticalDiscRegionCode France { get { return mvarFrance; } } + private static NintendoOpticalDiscRegionCode mvarItaly = new NintendoOpticalDiscRegionCode("Italy", 'I'); + public static NintendoOpticalDiscRegionCode Italy { get { return mvarItaly; } } + private static NintendoOpticalDiscRegionCode mvarJapan = new NintendoOpticalDiscRegionCode("Japan", 'J'); + public static NintendoOpticalDiscRegionCode Japan { get { return mvarJapan; } } + private static NintendoOpticalDiscRegionCode mvarKorea = new NintendoOpticalDiscRegionCode("Korea", 'K'); + public static NintendoOpticalDiscRegionCode Korea { get { return mvarKorea; } } + private static NintendoOpticalDiscRegionCode mvarPAL = new NintendoOpticalDiscRegionCode("PAL", 'P'); + public static NintendoOpticalDiscRegionCode PAL { get { return mvarPAL; } } + private static NintendoOpticalDiscRegionCode mvarRussia = new NintendoOpticalDiscRegionCode("Russia", 'R'); + public static NintendoOpticalDiscRegionCode Russia { get { return mvarRussia; } } + private static NintendoOpticalDiscRegionCode mvarSpanish = new NintendoOpticalDiscRegionCode("Spanish", 'S'); + public static NintendoOpticalDiscRegionCode Spanish { get { return mvarSpanish; } } + private static NintendoOpticalDiscRegionCode mvarTaiwan = new NintendoOpticalDiscRegionCode("Taiwan", 'T'); + public static NintendoOpticalDiscRegionCode Taiwan { get { return mvarTaiwan; } } + private static NintendoOpticalDiscRegionCode mvarAustralia = new NintendoOpticalDiscRegionCode("Australia", 'U'); + public static NintendoOpticalDiscRegionCode Australia { get { return mvarAustralia; } } + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Nintendo/DataFormats/FileSystem/Nintendo/Optical/NintendoOpticalDiscSystemType.cs b/CSharp/Plugins/UniversalEditor.Plugins.Nintendo/DataFormats/FileSystem/Nintendo/Optical/NintendoOpticalDiscSystemType.cs new file mode 100644 index 00000000..23bdf7b2 --- /dev/null +++ b/CSharp/Plugins/UniversalEditor.Plugins.Nintendo/DataFormats/FileSystem/Nintendo/Optical/NintendoOpticalDiscSystemType.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UniversalEditor.DataFormats.FileSystem.Nintendo.Optical +{ + [Flags()] + public enum NintendoOpticalDiscSystemType + { + Unknown = 0, + GameCube = 1, + Wii = 2, + } +} diff --git a/CSharp/Plugins/UniversalEditor.Plugins.Nintendo/UniversalEditor.Plugins.Nintendo.csproj b/CSharp/Plugins/UniversalEditor.Plugins.Nintendo/UniversalEditor.Plugins.Nintendo.csproj index a1545d13..a34da6f3 100644 --- a/CSharp/Plugins/UniversalEditor.Plugins.Nintendo/UniversalEditor.Plugins.Nintendo.csproj +++ b/CSharp/Plugins/UniversalEditor.Plugins.Nintendo/UniversalEditor.Plugins.Nintendo.csproj @@ -46,6 +46,10 @@ + + + +