Example #1
0
        public static byte[] BuildROM()
        {
            // yaz0 encode all of the files for the rom
            Parallel.ForEach(RomData.MMFileList, file =>
            {
                if (file.IsCompressed && file.WasEdited)
                {
                    // lower priority so that the rando can't lock a badly scheduled CPU by using 100%
                    var previousThreadPriority    = Thread.CurrentThread.Priority;
                    Thread.CurrentThread.Priority = ThreadPriority.Lowest;
                    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);
                    }
                    // this thread is borrowed, we don't want it to always be the lowest priority, return to previous state
                    Thread.CurrentThread.Priority = previousThreadPriority;
                }
            });
            byte[] ROM     = new byte[0x2000000];
            int    ROMAddr = 0;

            // write all files to rom
            for (int i = 0; i < RomData.MMFileList.Count; i++)
            {
                if (RomData.MMFileList[i].Cmp_Addr == -1)
                {
                    continue;
                }
                RomData.MMFileList[i].Cmp_Addr = ROMAddr;
                int fileLength = RomData.MMFileList[i].Data.Length;
                if (RomData.MMFileList[i].IsCompressed)
                {
                    RomData.MMFileList[i].Cmp_End = ROMAddr + fileLength;
                }
                if (ROMAddr + fileLength > ROM.Length) // rom too small
                {
                    // assuming the largest file isn't the last one, we still want some extra space for further files
                    //  padding will reduce the requirements for further resizes
                    int    expansionIncrementSize = 0x40000; // 1mb might be too large, not sure if there is a hardware compatiblity issue here
                    int    expansionLength        = (((ROMAddr + fileLength - ROM.Length) / expansionIncrementSize) + 1) * expansionIncrementSize;
                    byte[] newROM = new byte[ROM.Length + expansionLength];
                    Buffer.BlockCopy(ROM, 0, newROM, 0, ROM.Length);
                    Buffer.BlockCopy(new byte[expansionLength], 0, newROM, ROM.Length, expansionLength);
                    ROM = newROM;
                    Debug.WriteLine("*** Expanding rom to size 0x" + ROM.Length.ToString("X2") + "***");
                }

                ReadWriteUtils.Arr_Insert(RomData.MMFileList[i].Data, 0, fileLength, ROM, ROMAddr);
                ROMAddr += fileLength;
            }
            SequenceUtils.UpdateBankInstrumentPointers(ROM);
            UpdateFileTable(ROM);
            SignROM(ROM);
            FixCRC(ROM);

            return(ROM);
        }
Example #2
0
        /// <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.OpenRead(filename))
                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);

                                // Validate patch magic and version values
                                PatchUtils.Validate(magic, 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);
                        }
        }
Example #3
0
        public static void ApplyHack(string path, string name)
        {
            BinaryReader hack_file = new BinaryReader(File.Open(Path.Combine(path, 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;
            }
        }
Example #4
0
 private static void WriteColours(int fileNumber, int addressInFile, int count, Color[] colorArray)
 {
     for (int i = 0; i < count; i++)
     {
         int    colorAddress = addressInFile + (i * 2);
         ushort rgba         = ColorUtils.ToRGBA5551(colorArray[i]);
         var    data         = new byte[]
         {
             (byte)(rgba >> 8),
             (byte)(rgba & 0xFF),
         };
         ReadWriteUtils.Arr_Insert(data, 0, data.Length, RomData.MMFileList[fileNumber].Data, colorAddress);
     }
 }
Example #5
0
 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);
     }
 }
Example #6
0
        public static byte[] BuildROM()
        {
            // yaz0 encode all of the files for the rom
            Parallel.ForEach(RomData.MMFileList, file =>
            {
                if (file.IsCompressed && file.WasEdited)
                {
                    // lower priority so that the rando can't lock a badly scheduled CPU by using 100%
                    var previous_thread_priority  = Thread.CurrentThread.Priority;
                    Thread.CurrentThread.Priority = ThreadPriority.Lowest;
                    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);
                    }
                    // this thread is borrowed, we don't want it to always be the lowest priority, return to previous state
                    Thread.CurrentThread.Priority = previous_thread_priority;
                }
            });
            byte[] ROM     = new byte[0x2000000];
            int    ROMAddr = 0;

            // write all files to rom
            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);

            return(ROM);
        }
Example #7
0
        public static void ApplyHack(byte[] hack_content)
        {
            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;
            }
        }
Example #8
0
        public static byte[] BuildROM()
        {
            CompressMMFiles();

            byte[] ROM     = new byte[0x2000000];
            int    ROMAddr = 0;

            // write all files to rom
            for (int i = 0; i < RomData.MMFileList.Count; i++)
            {
                if (RomData.MMFileList[i].Cmp_Addr == -1)
                {
                    continue;
                }
                RomData.MMFileList[i].Cmp_Addr = ROMAddr;
                int fileLength = RomData.MMFileList[i].Data.Length;
                if (RomData.MMFileList[i].IsCompressed)
                {
                    RomData.MMFileList[i].Cmp_End = ROMAddr + fileLength;
                }
                if (ROMAddr + fileLength > ROM.Length) // rom too small
                {
                    // assuming the largest file isn't the last one, we still want some extra space for further files
                    //  padding will reduce the requirements for further resizes
                    int    expansionIncrementSize = 0x40000; // 1mb might be too large, not sure if there is a hardware compatiblity issue here
                    int    expansionLength        = (((ROMAddr + fileLength - ROM.Length) / expansionIncrementSize) + 1) * expansionIncrementSize;
                    byte[] newROM = new byte[ROM.Length + expansionLength];
                    Buffer.BlockCopy(ROM, 0, newROM, 0, ROM.Length);
                    Buffer.BlockCopy(new byte[expansionLength], 0, newROM, ROM.Length, expansionLength);
                    ROM = newROM;
                    Debug.WriteLine("*** Expanding rom to size 0x" + ROM.Length.ToString("X2") + "***");
                }

                ReadWriteUtils.Arr_Insert(RomData.MMFileList[i].Data, 0, fileLength, ROM, ROMAddr);
                ROMAddr += fileLength;
            }
            SequenceUtils.UpdateBankInstrumentPointers(ROM);
            UpdateFileTable(ROM);
            SignROM(ROM);
            FixCRC(ROM);

            return(ROM);
        }
Example #9
0
        public static void SetStrings(string path, string filename, string ver, string setting)
        {
            ResourceUtils.ApplyHack(path, 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);
        }
Example #10
0
        public static List <byte[]> GetFilesFromArchive(int fileIndex)
        {
            CheckCompressed(fileIndex);
            var data         = RomData.MMFileList[fileIndex].Data;
            var headerLength = ReadWriteUtils.Arr_ReadS32(data, 0);
            var pointer      = headerLength;
            var files        = new List <byte[]>();

            for (var i = 4; i < headerLength; i += 4)
            {
                var nextFileOffset = headerLength + ReadWriteUtils.Arr_ReadS32(data, i);
                var fileLength     = nextFileOffset - pointer;
                var dest           = new byte[fileLength];
                ReadWriteUtils.Arr_Insert(data, pointer, fileLength, dest, 0);
                pointer += fileLength;
                var decompressed = Yaz.Decode(dest);
                files.Add(decompressed);
            }
            return(files);
        }
Example #11
0
        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;
            }
        }
Example #12
0
        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);
            }
        }
Example #13
0
        public static void WriteNewItem(ItemObject itemObject, List <MessageEntry> newMessages, GameplaySettings settings, ChestTypeAttribute.ChestType?overrideChestType, MessageTable messageTable, ExtendedObjects extendedObjects)
        {
            var item     = itemObject.Item;
            var location = itemObject.NewLocation.Value;

            System.Diagnostics.Debug.WriteLine($"Writing {item.Name()} --> {location.Location()}");

            if (!itemObject.IsRandomized)
            {
                var indices = location.GetCollectableIndices();
                if (indices.Any())
                {
                    foreach (var collectableIndex in location.GetCollectableIndices())
                    {
                        ReadWriteUtils.Arr_WriteU16(RomData.MMFileList[COLLECTABLE_TABLE_FILE_INDEX].Data, collectableIndex * 2, 0);
                    }
                    return;
                }
            }

            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 fileData     = RomData.MMFileList[f].Data;

            GetItemEntry newItem;

            if (!itemObject.IsRandomized && location.IsNullableItem())
            {
                newItem = new GetItemEntry();
            }
            else if (item.IsExclusiveItem())
            {
                newItem = item.ExclusiveItemEntry();
            }
            else
            {
                newItem = RomData.GetItemList[item.GetItemIndex().Value];
            }

            // Attempt to resolve extended object Id, which should affect "Exclusive Items" as well.
            var graphics = extendedObjects.ResolveGraphics(newItem);

            if (graphics.HasValue)
            {
                newItem.Object = graphics.Value.objectId;
                newItem.Index  = graphics.Value.graphicId;
            }

            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);

            int?refillGetItemIndex = item switch
            {
                Item.ItemBottleMadameAroma => 0x91,
                Item.ItemBottleAliens => 0x92,
                _ => null,
            };

            if (refillGetItemIndex.HasValue)
            {
                var refillItem     = RomData.GetItemList[refillGetItemIndex.Value];
                var refillGraphics = extendedObjects.ResolveGraphics(refillItem);
                if (refillGraphics.HasValue)
                {
                    refillItem.Object = refillGraphics.Value.objectId;
                    refillItem.Index  = refillGraphics.Value.graphicId;
                }
                var refillData = new byte[]
                {
                    refillItem.ItemGained,
                    refillItem.Flag,
                    refillItem.Index,
                    refillItem.Type,
                    (byte)(refillItem.Message >> 8),
                    (byte)(refillItem.Message & 0xFF),
                    (byte)(refillItem.Object >> 8),
                    (byte)(refillItem.Object & 0xFF),
                };
                var refillOffset = (refillGetItemIndex.Value - 1) * 8 + baseaddr;
                ReadWriteUtils.Arr_Insert(refillData, 0, refillData.Length, fileData, refillOffset);
            }

            if (location.IsRupeeRepeatable())
            {
                settings.AsmOptions.MMRConfig.RupeeRepeatableLocations.Add(getItemIndex);
            }

            var isRepeatable = item.IsRepeatable(settings) || (!settings.PreventDowngrades && item.IsDowngradable());

            if (settings.ProgressiveUpgrades && item.HasAttribute <ProgressiveAttribute>())
            {
                isRepeatable = false;
            }
            if (item.IsReturnable(settings))
            {
                isRepeatable = false;
                settings.AsmOptions.MMRConfig.ItemsToReturnIds.Add(getItemIndex);
            }
            if (!isRepeatable)
            {
                SceneUtils.UpdateSceneFlagMask(getItemIndex);
            }

            if (settings.UpdateChests)
            {
                UpdateChest(location, item, overrideChestType);
            }

            if (settings.UpdateShopAppearance)
            {
                UpdateShop(itemObject, newMessages, messageTable);
            }

            if (itemObject.IsRandomized)
            {
                var hackContentAttributes = location.GetAttributes <HackContentAttribute>();
                if (location == item)
                {
                    hackContentAttributes = hackContentAttributes.Where(h => !h.ApplyOnlyIfItemIsDifferent);
                }
                foreach (var hackContent in hackContentAttributes.Select(h => h.HackContent))
                {
                    ResourceUtils.ApplyHack(hackContent);
                }
            }
        }
Example #14
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");
                }
            }
        }
Example #15
0
        public static void WriteNewItem(ItemObject itemObject, List <MessageEntry> newMessages, GameplaySettings settings, ChestTypeAttribute.ChestType?overrideChestType)
        {
            var item     = itemObject.Item;
            var location = itemObject.NewLocation.Value;

            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 fileData     = RomData.MMFileList[f].Data;

            GetItemEntry newItem;

            if (item.IsExclusiveItem())
            {
                newItem = item.ExclusiveItemEntry();
            }
            else
            {
                newItem = RomData.GetItemList[item.GetItemIndex().Value];
            }

            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.
            var isCycleRepeatable = item.IsCycleRepeatable();

            if (item.Name().Contains("Rupee") && location.IsRupeeRepeatable())
            {
                isCycleRepeatable = true;
            }
            if (item.ToString().StartsWith("Trade") && settings.QuestItemStorage)
            {
                isCycleRepeatable = false;
            }
            if (isCycleRepeatable)
            {
                settings.AsmOptions.MMRConfig.CycleRepeatableLocations.Add(getItemIndex);
            }

            var isRepeatable = item.IsRepeatable() || (!settings.PreventDowngrades && item.IsDowngradable());

            if (settings.ProgressiveUpgrades && item.HasAttribute <ProgressiveAttribute>())
            {
                isRepeatable = false;
            }
            if (!isRepeatable)
            {
                SceneUtils.UpdateSceneFlagMask(getItemIndex);
            }

            if (settings.UpdateChests)
            {
                UpdateChest(location, item, overrideChestType);
            }

            if (settings.UpdateShopAppearance)
            {
                UpdateShop(itemObject, newMessages);
            }

            if (location != item)
            {
                if (location == Item.StartingSword)
                {
                    ResourceUtils.ApplyHack(Resources.mods.fix_sword_song_of_time);
                }

                if (location == Item.MundaneItemSeahorse)
                {
                    ResourceUtils.ApplyHack(Resources.mods.fix_fisherman);
                }

                if (location == Item.MaskFierceDeity)
                {
                    ResourceUtils.ApplyHack(Resources.mods.fix_fd_mask_reset);
                }
            }
        }