/// <summary> /// Applies the given filename patch to the in-memory RomData /// </summary> /// <param name="filename"></param> /// <returns>SHA256 hash of the patch.</returns> public static byte[] ApplyPatch(string filename) { var hashAlg = new SHA256Managed(); using (var filestream = File.Open(filename, FileMode.Open)) using (var cryptoStream = new CryptoStream(filestream, hashAlg, CryptoStreamMode.Read)) using (var decompressStream = new GZipStream(cryptoStream, CompressionMode.Decompress)) using (var memoryStream = new MemoryStream()) { decompressStream.CopyTo(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); using (var reader = new BinaryReader(memoryStream)) { while (reader.BaseStream.Position != reader.BaseStream.Length) { var fileIndex = ReadWriteUtils.ReadS32(reader); var index = ReadWriteUtils.ReadS32(reader); var length = ReadWriteUtils.ReadS32(reader); var data = reader.ReadBytes(length); if (fileIndex >= RomData.MMFileList.Count) { var start = RomData.MMFileList[RomData.MMFileList.Count - 1].End; var newFile = new MMFile { Addr = start, IsCompressed = false, Data = data, End = start + data.Length }; RomData.MMFileList.Add(newFile); } if (index == -1) { RomData.MMFileList[fileIndex].Data = data; if (data.Length == 0) { RomData.MMFileList[fileIndex].Cmp_Addr = -1; RomData.MMFileList[fileIndex].Cmp_End = -1; } } else { CheckCompressed(fileIndex); ReadWriteUtils.Arr_Insert(data, 0, data.Length, RomData.MMFileList[fileIndex].Data, index); } } } return(hashAlg.Hash); } }
public static void ApplyHack(string name) { BinaryReader hack_file = new BinaryReader(File.Open(name, FileMode.Open)); int hack_len = (int)hack_file.BaseStream.Length; byte[] hack_content = new byte[hack_len]; hack_file.Read(hack_content, 0, hack_len); hack_file.Close(); if (name.EndsWith("title-screen")) { Random R = new Random(); int rot = R.Next(360); Color l; float h; for (int i = 0; i < 144 * 64; i++) { int p = (i * 4) + 8; l = Color.FromArgb(hack_content[p + 3], hack_content[p], hack_content[p + 1], hack_content[p + 2]); h = l.GetHue(); h += rot; h %= 360f; l = ColorUtils.FromAHSB(l.A, h, l.GetSaturation(), l.GetBrightness()); hack_content[p] = l.R; hack_content[p + 1] = l.G; hack_content[p + 2] = l.B; hack_content[p + 3] = l.A; } l = Color.FromArgb(hack_content[0x1FE72], hack_content[0x1FE73], hack_content[0x1FE76]); h = l.GetHue(); h += rot; h %= 360f; l = ColorUtils.FromAHSB(255, h, l.GetSaturation(), l.GetBrightness()); hack_content[0x1FE72] = l.R; hack_content[0x1FE73] = l.G; hack_content[0x1FE76] = l.B; } int addr = 0; while (hack_content[addr] != 0xFF) { //Debug.WriteLine(addr.ToString("X4")); uint dest = ReadWriteUtils.Arr_ReadU32(hack_content, addr); addr += 4; uint len = ReadWriteUtils.Arr_ReadU32(hack_content, addr); addr += 4; int f = RomUtils.GetFileIndexForWriting((int)dest); dest -= (uint)RomData.MMFileList[f].Addr; ReadWriteUtils.Arr_Insert(hack_content, addr, (int)len, RomData.MMFileList[f].Data, (int)dest); addr += (int)len; } }
private static void WriteColours(int file, int addr, int count, Color[] c) { for (int i = 0; i < count; i++) { int ca = addr + (i * 2); ushort rgba = ToRGBA5551(c[i]); var data = new byte[] { (byte)(rgba >> 8), (byte)(rgba & 0xFF), }; ReadWriteUtils.Arr_Insert(data, 0, data.Length, RomData.MMFileList[file].Data, ca); } }
public static byte[] BuildROM(string FileName) { Parallel.ForEach(RomData.MMFileList, file => { if (file.IsCompressed && file.WasEdited) { byte[] result; var newSize = Yaz.Encode(file.Data, file.Data.Length, out result); if (newSize >= 0) { file.Data = new byte[newSize]; ReadWriteUtils.Arr_Insert(result, 0, newSize, file.Data, 0); } } }); byte[] ROM = new byte[0x2000000]; int ROMAddr = 0; for (int i = 0; i < RomData.MMFileList.Count; i++) { if (RomData.MMFileList[i].Cmp_Addr == -1) { continue; } RomData.MMFileList[i].Cmp_Addr = ROMAddr; int file_len = RomData.MMFileList[i].Data.Length; if (RomData.MMFileList[i].IsCompressed) { RomData.MMFileList[i].Cmp_End = ROMAddr + file_len; } ReadWriteUtils.Arr_Insert(RomData.MMFileList[i].Data, 0, file_len, ROM, ROMAddr); ROMAddr += file_len; } UpdateFileTable(ROM); SignROM(ROM); FixCRC(ROM); using (BinaryWriter NewROM = new BinaryWriter(File.Open(FileName, FileMode.Create))) { NewROM.Write(ROM, 0, ROM.Length); } return(ROM); }
public static void WriteNewBottle(int ItemSlot, int NewItem) { int f = RomUtils.GetFileIndexForWriting(Addresses.BottleCatchTable); int baseaddr = Addresses.BottleCatchTable - RomData.MMFileList[f].Addr; var fileData = RomData.MMFileList[f].Data; for (int i = 0; i < RomData.BottleIndices[ItemSlot].Length; i++) { int offset = RomData.BottleIndices[ItemSlot][i] * 6 + baseaddr; var newBottle = RomData.BottleList[NewItem][0]; var data = new byte[] { newBottle.ItemGained, newBottle.Index, newBottle.Message, }; ReadWriteUtils.Arr_Insert(data, 0, data.Length, fileData, offset + 3); } }
public static void SetStrings(string filename, string ver, string setting) { ResourceUtils.ApplyHack(filename); int veraddr = 0xC44E30; int settingaddr = 0xC44E70; string verstring = $"MM Rando {ver}\x00"; string settingstring = $"{setting}\x00"; int f = GetFileIndexForWriting(veraddr); var file = RomData.MMFileList[f]; byte[] buffer = Encoding.ASCII.GetBytes(verstring); int addr = veraddr - file.Addr; ReadWriteUtils.Arr_Insert(buffer, 0, buffer.Length, file.Data, addr); buffer = Encoding.ASCII.GetBytes(settingstring); addr = settingaddr - file.Addr; ReadWriteUtils.Arr_Insert(buffer, 0, buffer.Length, file.Data, addr); }
public static void WriteNewBottle(Item location, Item item) { System.Diagnostics.Debug.WriteLine($"Writing {item.Name()} --> {location.Location()}"); int f = RomUtils.GetFileIndexForWriting(BOTTLE_CATCH_TABLE); int baseaddr = BOTTLE_CATCH_TABLE - RomData.MMFileList[f].Addr; var fileData = RomData.MMFileList[f].Data; foreach (var index in location.GetBottleItemIndices()) { var offset = index * 6 + baseaddr; var newBottle = RomData.BottleList[item.GetBottleItemIndices()[0]]; var data = new byte[] { newBottle.ItemGained, newBottle.Index, newBottle.Message, }; ReadWriteUtils.Arr_Insert(data, 0, data.Length, fileData, offset + 3); } }
public static void ApplyHack_File(string name, byte[] data) { BinaryReader hack_file = new BinaryReader(File.Open(name, FileMode.Open)); int hack_len = (int)hack_file.BaseStream.Length; byte[] hack_content = new byte[hack_len]; hack_file.Read(hack_content, 0, hack_len); hack_file.Close(); int addr = 0; while (hack_content[addr] != 0xFF) { //Debug.WriteLine(addr.ToString("X4")); uint dest = ReadWriteUtils.Arr_ReadU32(hack_content, addr); addr += 4; uint len = ReadWriteUtils.Arr_ReadU32(hack_content, addr); addr += 4; ReadWriteUtils.Arr_Insert(hack_content, addr, (int)len, data, (int)dest); addr += (int)len; } }
public static void WriteNewBottle(int location, int item) { System.Diagnostics.Debug.WriteLine($"Writing {Items.ITEM_NAMES[item]} --> {Items.ITEM_NAMES[location]}"); location = ItemUtils.SubtractItemOffset(location); item = ItemUtils.SubtractItemOffset(item); int f = RomUtils.GetFileIndexForWriting(BOTTLE_CATCH_TABLE); int baseaddr = BOTTLE_CATCH_TABLE - RomData.MMFileList[f].Addr; var fileData = RomData.MMFileList[f].Data; for (int i = 0; i < RomData.BottleIndices[location].Length; i++) { int offset = RomData.BottleIndices[location][i] * 6 + baseaddr; var newBottle = RomData.BottleList[item][0]; var data = new byte[] { newBottle.ItemGained, newBottle.Index, newBottle.Message, }; ReadWriteUtils.Arr_Insert(data, 0, data.Length, fileData, offset + 3); } }
public static void WriteNewItem(int location, int item) { System.Diagnostics.Debug.WriteLine($"Writing {Items.ITEM_NAMES[item]} --> {Items.ITEM_NAMES[location]}"); bool isRepeatable = Items.REPEATABLE.Contains(item); bool isCycleRepeatable = Items.CYCLE_REPEATABLE.Contains(item); location = ItemUtils.SubtractItemOffset(location); item = ItemUtils.SubtractItemOffset(item); int f = RomUtils.GetFileIndexForWriting(GET_ITEM_TABLE); int baseaddr = GET_ITEM_TABLE - RomData.MMFileList[f].Addr; var getItemIndex = RomData.GetItemIndices[location]; if (location == Items.ItemGoldDust) { getItemIndex = 0x6A; // Place items intended for Gold Dust at the Goron Race Bottle location. } int offset = (getItemIndex - 1) * 8 + baseaddr; var newItem = RomData.GetItemList[item]; var fileData = RomData.MMFileList[f].Data; var data = new byte[] { newItem.ItemGained, newItem.Flag, newItem.Index, newItem.Type, (byte)(newItem.Message >> 8), (byte)(newItem.Message & 0xFF), (byte)(newItem.Object >> 8), (byte)(newItem.Object & 0xFF), }; ReadWriteUtils.Arr_Insert(data, 0, data.Length, fileData, offset); if (isCycleRepeatable) { ReadWriteUtils.WriteToROM(cycle_repeat, (ushort)getItemIndex); cycle_repeat += 2; } if (!isRepeatable) { SceneUtils.UpdateSceneFlagMask(getItemIndex); } if (item == Items.ItemBottleWitch) { ReadWriteUtils.WriteToROM(0xB49982, (ushort)getItemIndex); ReadWriteUtils.WriteToROM(0xC72B42, (ushort)getItemIndex); } if (item == Items.ItemBottleMadameAroma) { ReadWriteUtils.WriteToROM(0xB4999A, (ushort)getItemIndex); ReadWriteUtils.WriteToROM(0xC72B4E, (ushort)getItemIndex); } if (item == Items.ItemBottleAliens) { ReadWriteUtils.WriteToROM(0xB499A6, (ushort)getItemIndex); ReadWriteUtils.WriteToROM(0xC72B5A, (ushort)getItemIndex); } // Goron Race Bottle now rewards a plain Gold Dust, so this is unnecessary until a proper fix for Goron Dust is found. //if (NewItem == Items.ItemBottleGoronRace) //{ // WriteToROM(0xB499B2, (ushort)getItemIndex); // WriteToROM(0xC72B66, (ushort)getItemIndex); //} }
/// <summary> /// Applies the given filename patch to the in-memory RomData /// </summary> /// <param name="filename"></param> /// <returns>SHA256 hash of the patch.</returns> public static byte[] ApplyPatch(string filename) { var hashAlg = new SHA256Managed(); using (var filestream = File.Open(filename, FileMode.Open)) using (var cryptoStream = new CryptoStream(filestream, hashAlg, CryptoStreamMode.Read)) using (var decompressStream = new GZipStream(cryptoStream, CompressionMode.Decompress)) using (var memoryStream = new MemoryStream()) { decompressStream.CopyTo(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); using (var reader = new BinaryReader(memoryStream)) { var magic = ReadWriteUtils.ReadU32(reader); var version = ReadWriteUtils.ReadU32(reader); // Make sure this is a patch file by checking the magic value if (magic != PatchUtil.PATCH_MAGIC) { throw new PatchMagicException(magic); } // Check that this patch version is supported if (version != (uint)PatchUtil.PATCH_VERSION) { throw new PatchVersionException(PatchUtil.PATCH_VERSION, (PatchVersion)version); } while (reader.BaseStream.Position != reader.BaseStream.Length) { var fileIndex = ReadWriteUtils.ReadS32(reader); var fileAddr = ReadWriteUtils.ReadS32(reader); var index = ReadWriteUtils.ReadS32(reader); var isStatic = ReadWriteUtils.ReadS32(reader) != 0 ? true : false; var length = ReadWriteUtils.ReadS32(reader); var data = reader.ReadBytes(length); if (fileIndex >= RomData.MMFileList.Count) { var newFile = new MMFile { Addr = fileAddr, IsCompressed = false, Data = data, End = fileAddr + data.Length, IsStatic = isStatic, }; RomUtils.AppendFile(newFile); } if (index == -1) { RomData.MMFileList[fileIndex].Data = data; if (data.Length == 0) { RomData.MMFileList[fileIndex].Cmp_Addr = -1; RomData.MMFileList[fileIndex].Cmp_End = -1; } } else { CheckCompressed(fileIndex); ReadWriteUtils.Arr_Insert(data, 0, data.Length, RomData.MMFileList[fileIndex].Data, index); } } } return(hashAlg.Hash); } }
public static void WriteNewItem(Item location, Item item, List <MessageEntry> newMessages, bool updateShop, bool preventDowngrades, bool updateChest, ChestTypeAttribute.ChestType?overrideChestType) { System.Diagnostics.Debug.WriteLine($"Writing {item.Name()} --> {location.Location()}"); int f = RomUtils.GetFileIndexForWriting(GET_ITEM_TABLE); int baseaddr = GET_ITEM_TABLE - RomData.MMFileList[f].Addr; var getItemIndex = location.GetItemIndex().Value; if (location == Item.ItemGoldDust) { getItemIndex = 0x6A; // Place items intended for Gold Dust at the Goron Race Bottle location. } int offset = (getItemIndex - 1) * 8 + baseaddr; var newItem = RomData.GetItemList[item.GetItemIndex().Value]; var fileData = RomData.MMFileList[f].Data; var data = new byte[] { newItem.ItemGained, newItem.Flag, newItem.Index, newItem.Type, (byte)(newItem.Message >> 8), (byte)(newItem.Message & 0xFF), (byte)(newItem.Object >> 8), (byte)(newItem.Object & 0xFF), }; ReadWriteUtils.Arr_Insert(data, 0, data.Length, fileData, offset); if (item.IsCycleRepeatable()) { ReadWriteUtils.WriteToROM(cycle_repeat, (ushort)getItemIndex); cycle_repeat += 2; } var isRepeatable = item.IsRepeatable() || (!preventDowngrades && item.IsDowngradable()); if (!isRepeatable) { SceneUtils.UpdateSceneFlagMask(getItemIndex); } if (item == Item.ItemBottleWitch) { ReadWriteUtils.WriteToROM(0xB49982, (ushort)getItemIndex); ReadWriteUtils.WriteToROM(0xC72B42, (ushort)getItemIndex); } if (item == Item.ItemBottleMadameAroma) { ReadWriteUtils.WriteToROM(0xB4999A, (ushort)getItemIndex); ReadWriteUtils.WriteToROM(0xC72B4E, (ushort)getItemIndex); } if (item == Item.ItemBottleAliens) { ReadWriteUtils.WriteToROM(0xB499A6, (ushort)getItemIndex); ReadWriteUtils.WriteToROM(0xC72B5A, (ushort)getItemIndex); } // Goron Race Bottle now rewards a plain Gold Dust, so this is unnecessary until a proper fix for Goron Dust is found. //if (item == Item.ItemBottleGoronRace) //{ // WriteToROM(0xB499B2, (ushort)getItemIndex); // WriteToROM(0xC72B66, (ushort)getItemIndex); //} if (updateChest) { UpdateChest(location, item, overrideChestType); } if (location != item) { if (updateShop) { UpdateShop(location, item, newMessages); } if (location == Item.StartingSword) { ResourceUtils.ApplyHack(Values.ModsDirectory + "fix-sword-song-of-time"); } } }
public static void WriteNewItem(int ItemSlot, int NewItem, bool IsRepeatable, bool RepeatCycle) { int f = RomUtils.GetFileIndexForWriting(Addresses.GetItemTable); int baseaddr = Addresses.GetItemTable - RomData.MMFileList[f].Addr; var itemIndex = RomData.GetItemIndices[ItemSlot]; if (ItemSlot == Items.ItemGoldDust) { itemIndex = 0x6A; // Place items intended for Gold Dust at the Goron Race Bottle location. } int offset = (itemIndex - 1) * 8 + baseaddr; var newItem = RomData.GetItemList[NewItem]; var fileData = RomData.MMFileList[f].Data; var data = new byte[] { newItem.ItemGained, newItem.Flag, newItem.Index, newItem.Type, (byte)(newItem.Message >> 8), (byte)(newItem.Message & 0xFF), (byte)(newItem.Object >> 8), (byte)(newItem.Object & 0xFF), }; ReadWriteUtils.Arr_Insert(data, 0, data.Length, fileData, offset); if (RepeatCycle) { ReadWriteUtils.WriteToROM(cycle_repeat, (ushort)itemIndex); cycle_repeat += 2; } if (!IsRepeatable) { SceneUtils.UpdateSceneFlagMask(itemIndex); } if (NewItem == Items.ItemBottleWitch) { ReadWriteUtils.WriteToROM(0xB49982, (ushort)itemIndex); ReadWriteUtils.WriteToROM(0xC72B42, (ushort)itemIndex); } if (NewItem == Items.ItemBottleMadameAroma) { ReadWriteUtils.WriteToROM(0xB4999A, (ushort)itemIndex); ReadWriteUtils.WriteToROM(0xC72B4E, (ushort)itemIndex); } if (NewItem == Items.ItemBottleAliens) { ReadWriteUtils.WriteToROM(0xB499A6, (ushort)itemIndex); ReadWriteUtils.WriteToROM(0xC72B5A, (ushort)itemIndex); } ; // Goron Race Bottle now rewards a plain Gold Dust, so this is unnecessary until a proper fix for Goron Dust is found. //if (NewItem == Items.ItemBottleGoronRace) //{ // WriteToROM(0xB499B2, (ushort)GetItemIndices[ItemSlot]); // WriteToROM(0xC72B66, (ushort)GetItemIndices[ItemSlot]); //}; }
public static void WriteNewItem(Item location, Item item, List <MessageEntry> newMessages, bool updateShop, bool preventDowngrades, bool updateChest, ChestTypeAttribute.ChestType?overrideChestType, bool isExtraStartingItem) { System.Diagnostics.Debug.WriteLine($"Writing {item.Name()} --> {location.Location()}"); int f = RomUtils.GetFileIndexForWriting(GET_ITEM_TABLE); int baseaddr = GET_ITEM_TABLE - RomData.MMFileList[f].Addr; var getItemIndex = location.GetItemIndex().Value; int offset = (getItemIndex - 1) * 8 + baseaddr; var newItem = isExtraStartingItem ? Items.RecoveryHeart // Warning: this will not work well for starting with Bottle contents (currently impossible), because you'll first have to acquire the Recovery Heart before getting the bottle-less version. Also may interfere with future implementation of progressive upgrades. : RomData.GetItemList[item.GetItemIndex().Value]; var fileData = RomData.MMFileList[f].Data; var data = new byte[] { newItem.ItemGained, newItem.Flag, newItem.Index, newItem.Type, (byte)(newItem.Message >> 8), (byte)(newItem.Message & 0xFF), (byte)(newItem.Object >> 8), (byte)(newItem.Object & 0xFF), }; ReadWriteUtils.Arr_Insert(data, 0, data.Length, fileData, offset); // todo use Logic Editor to handle which locations should be repeatable and which shouldn't. if ((item.IsCycleRepeatable() && location != Item.HeartPieceNotebookMayor) || (item.Name().Contains("Rupee") && location.IsRupeeRepeatable())) { ReadWriteUtils.WriteToROM(cycle_repeat, (ushort)getItemIndex); cycle_repeat += 2; cycle_repeat_count += 2; ReadWriteUtils.WriteToROM(cycle_repeat_count_address, cycle_repeat_count); } var isRepeatable = item.IsRepeatable() || (!preventDowngrades && item.IsDowngradable()); if (!isRepeatable) { SceneUtils.UpdateSceneFlagMask(getItemIndex); } if (item == Item.ItemBottleWitch) { ReadWriteUtils.WriteToROM(0xB4997E, (ushort)getItemIndex); ReadWriteUtils.WriteToROM(0xC72B42, (ushort)getItemIndex); } if (item == Item.ItemBottleMadameAroma) { ReadWriteUtils.WriteToROM(0xB4998A, (ushort)getItemIndex); ReadWriteUtils.WriteToROM(0xC72B4E, (ushort)getItemIndex); } if (item == Item.ItemBottleAliens) { ReadWriteUtils.WriteToROM(0xB49996, (ushort)getItemIndex); ReadWriteUtils.WriteToROM(0xC72B5A, (ushort)getItemIndex); } if (item == Item.ItemBottleGoronRace) { ReadWriteUtils.WriteToROM(0xB499A2, (ushort)getItemIndex); ReadWriteUtils.WriteToROM(0xC72B66, (ushort)getItemIndex); } if (updateChest) { UpdateChest(location, item, overrideChestType); } if (location != item) { if (updateShop) { UpdateShop(location, item, newMessages); } if (location == Item.StartingSword) { ResourceUtils.ApplyHack(Values.ModsDirectory + "fix-sword-song-of-time"); } if (location == Item.MundaneItemSeahorse) { ResourceUtils.ApplyHack(Values.ModsDirectory + "fix-fisherman"); } if (location == Item.MaskFierceDeity) { ResourceUtils.ApplyHack(Values.ModsDirectory + "fix-fd-mask-reset"); } } }
//todo - allow rebuilding text file private static void WriteMessage(int addr, byte[] msg) { int fileIndex = RomUtils.GetFileIndexForWriting(MESSAGE_DATA_ADDRESS); ReadWriteUtils.Arr_Insert(msg, 0, msg.Length, MMFileList[fileIndex].Data, addr); }