Beispiel #1
0
        public static void ReplaceGetItemTable()
        {
            ResourceUtils.ApplyHack(Resources.mods.replace_gi_table);
            int last_file = RomData.MMFileList.Count - 1;

            GET_ITEM_TABLE = RomUtils.AddNewFile(Resources.mods.gi_table);
            ReadWriteUtils.WriteToROM(0xBDAEAC, (uint)last_file + 1);
            ResourceUtils.ApplyHack(Resources.mods.update_chests);
            RomUtils.AddNewFile(Resources.mods.chest_table);
            ReadWriteUtils.WriteToROM(0xBDAEA8, (uint)last_file + 2);
            ResourceUtils.ApplyHack(Resources.mods.standing_hearts);
            ResourceUtils.ApplyHack(Resources.mods.fix_item_checks);
            SceneUtils.ResetSceneFlagMask();
            SceneUtils.UpdateSceneFlagMask(0x5B); // red potion
            SceneUtils.UpdateSceneFlagMask(0x91); // chateau romani
            SceneUtils.UpdateSceneFlagMask(0x92); // milk
            SceneUtils.UpdateSceneFlagMask(0x93); // gold dust
        }
Beispiel #2
0
        public static void ReplaceGetItemTable()
        {
            ResourceUtils.ApplyHack(Values.ModsDirectory, "replace-gi-table");
            int last_file = RomData.MMFileList.Count - 1;

            GET_ITEM_TABLE = RomUtils.AddNewFile(Values.ModsDirectory, "gi-table");
            ReadWriteUtils.WriteToROM(0xBDAEAC, (uint)last_file + 1);
            ResourceUtils.ApplyHack(Values.ModsDirectory, "update-chests");
            RomUtils.AddNewFile(Values.ModsDirectory, "chest-table");
            ReadWriteUtils.WriteToROM(0xBDAEA8, (uint)last_file + 2);
            ResourceUtils.ApplyHack(Values.ModsDirectory, "standing-hearts");
            ResourceUtils.ApplyHack(Values.ModsDirectory, "fix-item-checks");
            cycle_repeat = 0xC72DF4;
            SceneUtils.ResetSceneFlagMask();
            SceneUtils.UpdateSceneFlagMask(0x5B); // red potion
            SceneUtils.UpdateSceneFlagMask(0x91); // chateau romani
            SceneUtils.UpdateSceneFlagMask(0x92); // milk
            SceneUtils.UpdateSceneFlagMask(0x93); // gold dust
        }
Beispiel #3
0
        private static void UpdateChest(Item location, Item item, ChestTypeAttribute.ChestType?overrideChestType)
        {
            var chestType = item.GetAttribute <ChestTypeAttribute>().Type;

            if (overrideChestType.HasValue)
            {
                chestType = overrideChestType.Value;
            }
            var chestAttribute = location.GetAttribute <ChestAttribute>();

            if (chestAttribute != null)
            {
                foreach (var address in chestAttribute.Addresses)
                {
                    var chestVariable = ReadWriteUtils.Read(address);
                    chestVariable &= 0x0F; // remove existing chest type
                    var newChestType = ChestAttribute.GetType(chestType, chestAttribute.Type);
                    newChestType <<= 4;
                    chestVariable |= newChestType;
                    ReadWriteUtils.WriteToROM(address, chestVariable);
                }
            }

            var grottoChestAttribute = location.GetAttribute <GrottoChestAttribute>();

            if (grottoChestAttribute != null)
            {
                foreach (var address in grottoChestAttribute.Addresses)
                {
                    var grottoVariable = ReadWriteUtils.Read(address);
                    grottoVariable &= 0x1F; // remove existing chest type
                    var newChestType = (byte)chestType;
                    newChestType  <<= 5;
                    grottoVariable |= newChestType; // add new chest type
                    ReadWriteUtils.WriteToROM(address, grottoVariable);
                }
            }
        }
Beispiel #4
0
        private static void UpdateShop(Item location, Item item, List <MessageEntry> newMessages)
        {
            var newItem = RomData.GetItemList[item.GetItemIndex().Value];

            var shopRooms = location.GetAttributes <ShopRoomAttribute>();

            foreach (var shopRoom in shopRooms)
            {
                ReadWriteUtils.WriteToROM(shopRoom.RoomObjectAddress, (ushort)newItem.Object);
            }

            var shopInventories = location.GetAttributes <ShopInventoryAttribute>();

            foreach (var shopInventory in shopInventories)
            {
                ReadWriteUtils.WriteToROM(shopInventory.ShopItemAddress, (ushort)newItem.Object);
                var index = newItem.Index > 0x7F ? (byte)(0xFF - newItem.Index) : (byte)(newItem.Index - 1);
                ReadWriteUtils.WriteToROM(shopInventory.ShopItemAddress + 0x03, index);

                var    shopTexts = item.ShopTexts();
                string description;
                switch (shopInventory.Keeper)
                {
                case ShopInventoryAttribute.ShopKeeper.WitchShop:
                    description = shopTexts.WitchShop;
                    break;

                case ShopInventoryAttribute.ShopKeeper.TradingPostMain:
                    description = shopTexts.TradingPostMain;
                    break;

                case ShopInventoryAttribute.ShopKeeper.TradingPostPartTimer:
                    description = shopTexts.TradingPostPartTimer;
                    break;

                case ShopInventoryAttribute.ShopKeeper.CuriosityShop:
                    description = shopTexts.CuriosityShop;
                    break;

                case ShopInventoryAttribute.ShopKeeper.BombShop:
                    description = shopTexts.BombShop;
                    break;

                case ShopInventoryAttribute.ShopKeeper.ZoraShop:
                    description = shopTexts.ZoraShop;
                    break;

                case ShopInventoryAttribute.ShopKeeper.GoronShop:
                    description = shopTexts.GoronShop;
                    break;

                case ShopInventoryAttribute.ShopKeeper.GoronShopSpring:
                    description = shopTexts.GoronShopSpring;
                    break;

                default:
                    description = null;
                    break;
                }
                if (description == null)
                {
                    description = shopTexts.Default;
                }

                var messageId = ReadWriteUtils.ReadU16(shopInventory.ShopItemAddress + 0x0A);
                newMessages.Add(new MessageEntry
                {
                    Id      = messageId,
                    Header  = null,
                    Message = MessageUtils.BuildShopDescriptionMessage(item.Name(), 20, description)
                });

                newMessages.Add(new MessageEntry
                {
                    Id      = (ushort)(messageId + 1),
                    Header  = null,
                    Message = MessageUtils.BuildShopPurchaseMessage(item.Name(), 20, item)
                });
            }
        }
Beispiel #5
0
        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");
                }
            }
        }
Beispiel #6
0
 public static void ResetSceneFlagMask()
 {
     ReadWriteUtils.WriteToROM(SCENE_FLAG_MASKS, (uint)0);
     ReadWriteUtils.WriteToROM(SCENE_FLAG_MASKS + 0xC, (uint)0);
 }
Beispiel #7
0
        /// <summary>
        /// Apply all patches for Adult Link mod provided by SkilarsArt.
        /// </summary>
        public static void ApplyAdultLinkPatches()
        {
            // Overwrite segmented address: 0x0601E244
            ReadWriteUtils.WriteU32ToROM(0xC56350, 0x060122C4); // Physical: 0xBA5E70

            // ???
            ReadWriteUtils.WriteU16ToROM(0xC5637E, 0x02BC);
            ReadWriteUtils.WriteU16ToROM(0xC56380, 0x0226);
            ReadWriteUtils.WriteU16ToROM(0xC56382, 0x010E);
            ReadWriteUtils.WriteU16ToROM(0xC56384, 0x02BC);
            ReadWriteUtils.WriteU16ToROM(0xC56386, 0x012C);
            ReadWriteUtils.WriteU16ToROM(0xC5638C, 0x0258);
            ReadWriteUtils.WriteU16ToROM(0xC56392, 0x024E);
            ReadWriteUtils.WriteU16ToROM(0xC56398, 0x00C8);
            ReadWriteUtils.WriteU16ToROM(0xC5639A, 0x0082);

            // Write chunk of segmented addresses for player model.
            ReadWriteUtils.WriteToROM(0xC5653C, Resources.models.adult_link_code_segaddrs);

            // Update sword hitbox?
            {
                // Kokiri Sword:        3000.0 => 4000.0
                ReadWriteUtils.WriteU32ToROM(0xC572BC, 0x457A0000);
                // Razor Sword:         3000.0 => 4000.0
                ReadWriteUtils.WriteU32ToROM(0xC572C0, 0x457A0000);
                // Gilded Sword:        4000.0 => 5500.0
                ReadWriteUtils.WriteU32ToROM(0xC572C4, 0x45ABE000);
                // Great Fairy's Sword: 5500.0 => 5500.0
                ReadWriteUtils.WriteU32ToROM(0xC572C8, 0x45ABE000);
            }

            // Overwrite segmented address: 0x06017818 => 0x0601BE60
            ReadWriteUtils.WriteU32ToROM(0xC572D4, 0x0601BE60);

            // Overwrite function pointer: 0x800B7078 => 0x800B7058
            // Last function in function pointer array (length 5) at RDRAM: 0x801DCA58
            // Old function: F0 = F2 + 44.0
            // New function: F0 = F2 + 68.0
            ReadWriteUtils.WriteU32ToROM(0xC72FA8, 0x800B7058); // Physical: 0xBC2AC8

            // Patch player_actor.
            {
                // Replace floats? Physical: 0xC25D38
                ReadWriteUtils.WriteU32ToROM(0xCD6218, 0x42600000); // 40.0   => 56.0
                ReadWriteUtils.WriteU32ToROM(0xCD621C, 0x42B40000); // 60.0   => 90.0
                ReadWriteUtils.WriteU32ToROM(0xCD6220, 0x3F800000); // 0.647  => 1.0
                ReadWriteUtils.WriteU32ToROM(0xCD6224, 0x42DE0000); // 71.0   => 111.0
                ReadWriteUtils.WriteU32ToROM(0xCD6228, 0x428C0000); // 50.0   => 70.0
                ReadWriteUtils.WriteU32ToROM(0xCD622C, 0x429ECCCD); // 49.0   => 79.4
                ReadWriteUtils.WriteU32ToROM(0xCD6230, 0x426C0000); // 39.0   => 59.0
                ReadWriteUtils.WriteU32ToROM(0xCD6234, 0x42240000); // 27.0   => 41.0
                ReadWriteUtils.WriteU32ToROM(0xCD6238, 0x41980000); // 19.0
                ReadWriteUtils.WriteU32ToROM(0xCD623C, 0x42100000); // 22.0   => 36.0
                ReadWriteUtils.WriteU32ToROM(0xCD6240, 0x42480000); // 32.4   => 50.0
                ReadWriteUtils.WriteU32ToROM(0xCD6244, 0x42600000); // 32.0   => 56.0
                ReadWriteUtils.WriteU32ToROM(0xCD6248, 0x42880000); // 48.0   => 68.0
                ReadWriteUtils.WriteU32ToROM(0xCD624C, 0x428C0000); // 45.294 => 70.0
                ReadWriteUtils.WriteU32ToROM(0xCD6250, 0x41900000); // 14.0   => 18.0
                ReadWriteUtils.WriteU32ToROM(0xCD6254, 0x41B80000); // 12.0   => 23.0
                ReadWriteUtils.WriteU32ToROM(0xCD6258, 0x428C0000); // 55.0   => 70.0

                // Replace unknown data chunk immediately following floats. Physical: 0xC25D7C
                // This affects Link's voice (child voice to adult voice).
                ReadWriteUtils.WriteToROM(0xCD625C, Resources.models.adult_link_player_actor_data_1);

                // Replace segmented addresses following previous chunk. Physical: 0xC25DD8
                ReadWriteUtils.WriteToROM(0xCD62B8, Resources.models.adult_link_player_actor_data_2);
            }

            // Patch Arms_Hook (Hookshot) actor.
            {
                // Update segmented address in code: 0x0602D960 => 0x06029D60
                // Old: addiu t0, 0x0602
                //      addiu t0, t0, 0xD960
                // New: addiu t0, 0x0602
                //      addiu t0, t0, 0x9D60
                ReadWriteUtils.WriteU32ToROM(0xD3BC50, 0x25089D60); // Physical: 0xC8B770
            }

            // Patch En_Zog (Mikau) actor.
            {
                // Update instruction to use new distance to allow pushing Mikau from: 40.0 => 96.0
                // Old: lui at, 0x4220
                // New: lui at, 0x42C0
                ReadWriteUtils.WriteU32ToROM(0xFFA188, 0x3C0142C0); // Physical: 0xF49CA8
            }

            // Patch vertex data and DLists in gameplay_keep.
            {
                // Write vertex buffer (1).
                ReadWriteUtils.WriteToROM(0x10B0510, Resources.models.adult_link_gameplay_keep_vtx_1); // Physical: 0xFFFFB0

                // Write vertex buffer (2).
                ReadWriteUtils.WriteToROM(0x10B0A90, Resources.models.adult_link_gameplay_keep_vtx_2); // Physical: 0x1000530

                // Write vertex buffer (3).
                ReadWriteUtils.WriteToROM(0x10B1810, Resources.models.adult_link_gameplay_keep_vtx_3); // Physical: 0x10012B0

                // Replace DList instruction: G_RDPPIPESYNC => G_DL
                ReadWriteUtils.WriteU64ToROM(0x10B18F0, 0xDE0000000405A2E0); // gsSPDisplayList(0x0405A2E0);

                // Write vertex buffer (4).
                ReadWriteUtils.WriteToROM(0x10E52A0, Resources.models.adult_link_gameplay_keep_vtx_4); // Physical: 0x1034D40

                // Replace vertex data with small DList (offset 0x5A2E0).
                ReadWriteUtils.WriteU64ToROM(0x10E52E0, 0xE700000000000000); // gsDPPipeSync();
                ReadWriteUtils.WriteU64ToROM(0x10E52E8, 0xDA3800010405A2A0); // gsSPMatrix(G_MTX_NOPUSH, 0x0405A2A0);
                ReadWriteUtils.WriteU64ToROM(0x10E52F0, 0xDF00000000000000); // gsSPEndDisplayList();
            }

            // Patch DLists for masks (field models).
            {
                // Update Keaton Mask to call gameplay_keep DList: gsDPPipeSync() => gsSPDisplayList(0x0405A2E0)
                ReadWriteUtils.WriteU64ToROM(0x11B14A8, 0xDE0000000405A2E0); // Physical: 0x10FB618

                // Update Bunny Hood to call gameplay_keep DLists.
                ReadWriteUtils.WriteU64ToROM(0x11B2620, 0xDE0000000405A2E0); // Physical: 0x10FC260
                ReadWriteUtils.WriteU64ToROM(0x11B2780, 0xDE0000000405A2E8); // Physical: 0x10FC3C0
                ReadWriteUtils.WriteU64ToROM(0x11B27B0, 0xDE0000000405A2E0); // Physical: 0x10FC3F0
                ReadWriteUtils.WriteU64ToROM(0x11B2890, 0xDE0000000405A2E8); // Physical: 0x10FC4D0
                ReadWriteUtils.WriteU64ToROM(0x11B28C0, 0xDE0000000405A2E0); // Physical: 0x10FC500

                // Update Mask of Scents to call gameplay_keep DList: gsDPPipeSync() => gsSPDisplayList(0x0405A2E0)
                ReadWriteUtils.WriteU64ToROM(0x11D6718, 0xDE0000000405A2E0); // Physical: 0x11175B8
            }

            // Replace En_Horse actor (Epona).
            ReadWriteUtils.WriteToROM(0xCF5950, Resources.models.adult_en_horse);

            // Insert player model object.
            ObjUtils.InsertObj(Resources.models.adult_obj_link, 0x11);

            // Insert adult Epona model object.
            ObjUtils.InsertObj(Resources.models.adult_obj_epona, 0x7D);
        }
Beispiel #8
0
        private static void UpdateShop(ItemObject itemObject, List <MessageEntry> newMessages)
        {
            var          location = itemObject.NewLocation.Value;
            GetItemEntry newItem;

            if (itemObject.Mimic != null)
            {
                newItem = RomData.GetItemList[itemObject.Mimic.Item.GetItemIndex().Value];
            }
            else if (itemObject.Item.IsExclusiveItem())
            {
                newItem = itemObject.Item.ExclusiveItemEntry();
            }
            else
            {
                newItem = RomData.GetItemList[itemObject.Item.GetItemIndex().Value];
            }

            var shopRooms = location.GetAttributes <ShopRoomAttribute>();

            foreach (var shopRoom in shopRooms)
            {
                ReadWriteUtils.WriteToROM(shopRoom.RoomObjectAddress, (ushort)newItem.Object);
            }

            var shopInventories = location.GetAttributes <ShopInventoryAttribute>();

            foreach (var shopInventory in shopInventories)
            {
                ReadWriteUtils.WriteToROM(shopInventory.ShopItemAddress, (ushort)newItem.Object);
                var index = newItem.Index > 0x7F ? (byte)(0xFF - newItem.Index) : (byte)(newItem.Index - 1);
                ReadWriteUtils.WriteToROM(shopInventory.ShopItemAddress + 0x03, index);

                var messageId = ReadWriteUtils.ReadU16(shopInventory.ShopItemAddress + 0x0A);
                newMessages.Add(new MessageEntryBuilder()
                                .Id(messageId)
                                .Message(it =>
                {
                    it.Red(() =>
                    {
                        it.RuntimeItemName(itemObject.DisplayName(), location).Text(": ").Text("20 Rupees").NewLine();
                    })
                    .RuntimeWrap(() =>
                    {
                        it.RuntimeItemDescription(itemObject.DisplayItem, shopInventory.Keeper, location);
                    })
                    .DisableTextBoxClose()
                    .EndFinalTextBox();
                })
                                .Build()
                                );

                newMessages.Add(new MessageEntryBuilder()
                                .Id((ushort)(messageId + 1))
                                .Message(it =>
                {
                    it.RuntimeItemName(itemObject.DisplayName(), location).Text(": ").Text("20 Rupees").NewLine()
                    .Text(" ").NewLine()
                    .StartGreenText()
                    .TwoChoices()
                    .Text("I'll buy ").RuntimePronoun(itemObject.DisplayItem, location).NewLine()
                    .Text("No thanks")
                    .EndFinalTextBox();
                })
                                .Build()
                                );
            }
        }