public static void FixDualizeResultMessages(MemoryStream ms, Dol dol, ReservedMemchunk memoryAreaSprintfStringDualizeResult, ReservedMemchunk memoryAreaSprintfStringNewDualizeResult)
        {
            var be = EndianUtils.Endianness.BigEndian;

            // adjust sprintf parameters for dualize result
            {
                uint address = memoryAreaSprintfStringDualizeResult.AddressRam;
                var  imm     = GenerateHighLowImmediatesFor32BitLoad(address);

                ms.Position = dol.MapRamToRom(0x8027bc58);
                ms.WriteUInt32(0x3c800000u | (imm.high & 0xffffu), be);                 // lis r4, imm.high
                ms.WriteUInt32(AssembleMR(5, 3), be);                                   // mr r5,r3
                ms.WriteUInt32(0x38840000u | (imm.low & 0xffffu), be);                  // addi r4, r4, imm.low
                ms.Position += 4;
                ms.WriteUInt32(0x60000000, be);                                         // nop
                ms.WriteUInt32((ms.PeekUInt32(be) & 0xfc1fffff) | (6 << 21), be);       // replace target register with r6
            }

            // adjust sprintf parameters for dualize result
            {
                uint address = memoryAreaSprintfStringNewDualizeResult.AddressRam;
                var  imm     = GenerateHighLowImmediatesFor32BitLoad(address);

                ms.Position = dol.MapRamToRom(0x8027bc1c);
                ms.WriteUInt32(0x3c800000u | (imm.high & 0xffffu), be);                 // lis r4, imm.high
                ms.WriteUInt32(AssembleMR(9, 3), be);                                   // mr r9,r3
                ms.WriteUInt32(0x38840000u | (imm.low & 0xffffu), be);                  // addi r4, r4, imm.low
                ms.WriteUInt32(AssembleMR(5, 30), be);                                  // mr r5,r30
                ms.Position += 8;
                ms.WriteUInt32(0x60000000, be);                                         // nop
            }
        }
        public static void FixRequestRewardMesssages(MemoryStream ms, Dol dol, ReservedMemchunk memoryAreaSprintfStringItemReward, ReservedMemchunk memoryAreaSprintfStringGaldReward, ReservedMemchunk memoryAreaSprintfStringNotEnough)
        {
            var be = EndianUtils.Endianness.BigEndian;
            // function at 80290d44 builds these

            // adjust sprintf for message with item reward
            {
                uint addressSprintfStringItemReward = memoryAreaSprintfStringItemReward.AddressRam;
                var  imm = GenerateHighLowImmediatesFor32BitLoad(addressSprintfStringItemReward);

                ms.Position = dol.MapRamToRom(0x80291118);
                ms.WriteUInt32((ms.PeekUInt32(be) & 0xfc1fffff) | (9 << 21), be);       // replace target register with r9
                ms.WriteUInt32(0x3c800000u | (imm.high & 0xffffu), be);                 // lis r4, imm.high
                ms.WriteUInt32(0x60000000, be);                                         // nop
                ms.WriteUInt32(0x38840000u | (imm.low & 0xffffu), be);                  // addi r4, r4, imm.low
                ms.WriteUInt32(AssembleMR(5, 27), be);                                  // mr r5,r27
                ms.Position += 4;
                ms.WriteUInt32(0x60000000, be);                                         // nop
                ms.WriteUInt32(AssembleMR(8, 29), be);                                  // mr r8,r29
                ms.WriteUInt32(0x60000000, be);                                         // nop
                ms.Position += 4;
                ms.Position += 4;
                ms.Position += 4;
                ms.WriteUInt32(0x60000000, be);                                         // nop
            }

            // adjust sprintf parameters for message with gald reward
            {
                uint addressSprintfStringGaldReward = memoryAreaSprintfStringGaldReward.AddressRam;
                var  imm = GenerateHighLowImmediatesFor32BitLoad(addressSprintfStringGaldReward);

                ms.Position = dol.MapRamToRom(0x8029106c);
                ms.WriteUInt32(0x60000000, be);                                         // nop
                ms.WriteUInt32(0x3c800000u | (imm.high & 0xffffu), be);                 // lis r4, imm.high
                ms.WriteUInt32(0x38840000u | (imm.low & 0xffffu), be);                  // addi r4, r4, imm.low
                ms.WriteUInt32(AssembleMR(5, 24), be);                                  // mr r5,r24
                ms.Position += 8;
                ms.WriteUInt32((ms.PeekUInt32(be) & 0xfc1fffff) | (9 << 21), be);       // replace target register with r9
                ms.WriteUInt32(AssembleMR(8, 28), be);                                  // mr r8,r28
                ms.WriteUInt32(AssembleMR(10, 27), be);                                 // mr r10,r27
                ms.Position += 4;
                ms.WriteUInt32(0x60000000, be);                                         // nop
            }

            // adjust sprintf parameters for message when you don't have the necessary items
            {
                uint address = memoryAreaSprintfStringNotEnough.AddressRam;
                var  imm     = GenerateHighLowImmediatesFor32BitLoad(address);

                ms.Position = dol.MapRamToRom(0x80290e6c);
                ms.WriteUInt32(0x3c800000u | (imm.high & 0xffffu), be);                 // lis r4, imm.high
                ms.Position += 4;
                ms.WriteUInt32(0x38840000u | (imm.low & 0xffffu), be);                  // addi r4, r4, imm.low
                ms.Position += 20;
                ms.WriteUInt32(0x60000000, be);                                         // nop
            }
        }
        public static void ApplyFontTexPointerFix(MemoryStream ms, Dol dol, ReservedMemchunk chunk)
        {
            // code snippet for actually repointing
            // looks at all pointers that start with bitpattern 1110 (0xE) and readjusts them to actually point at the allocated memory
            // input:
            // - r3: address of first pointer to fix
            // - r4: amount of pointers to fix
            // - r5: bytes to increment per iteration to get to the next pointer
            // - r6: address font texture has been allocated at
            uint repointingEntryPoint = chunk.AddressRam;

            ms.Position = chunk.AddressRom;
            ms.WriteUInt32(0x7c8903a6u.ToEndian(EndianUtils.Endianness.BigEndian));             // mtctr r4
            ms.WriteUInt32(0x3d00e000u.ToEndian(EndianUtils.Endianness.BigEndian));             // lis r8, 0xE000
            ms.WriteUInt32(0x81430000u.ToEndian(EndianUtils.Endianness.BigEndian));             // lwz r10, 0 (r3)
            ms.WriteUInt32(0x55490006u.ToEndian(EndianUtils.Endianness.BigEndian));             // rlwinm r9, r10, 0, 0, 3 (f0000000)
            ms.WriteUInt32(0x7f894000u.ToEndian(EndianUtils.Endianness.BigEndian));             // cmpw cr7, r9, r8
            ms.WriteUInt32(0x40be0010u.ToEndian(EndianUtils.Endianness.BigEndian));             // bne+ cr7 --> add r3, r3, r5
            ms.WriteUInt32(0x554a013eu.ToEndian(EndianUtils.Endianness.BigEndian));             // rlwinm r10, r10, 0, 4, 31, (0fffffff)
            ms.WriteUInt32(0x7d4a3214u.ToEndian(EndianUtils.Endianness.BigEndian));             // add r10, r10, r6
            ms.WriteUInt32(0x91430000u.ToEndian(EndianUtils.Endianness.BigEndian));             // stw r10, 0 (r3)
            ms.WriteUInt32(0x7c632a14u.ToEndian(EndianUtils.Endianness.BigEndian));             // add r3, r3, r5
            ms.WriteUInt32(0x4200ffe0u.ToEndian(EndianUtils.Endianness.BigEndian));             // bdnz+ --> lwz r10, 0 (r3)
            ms.WriteUInt32(0x4e800020u.ToEndian(EndianUtils.Endianness.BigEndian));             // blr

            uint pointerFixCodeEntryPoint = dol.MapRomToRam((uint)ms.Position);

            ms.WriteUInt32(0x9421ffb0u.ToEndian(EndianUtils.Endianness.BigEndian));             // stwu sp, -0x50 (sp)
            ms.WriteUInt32(0x7c0802a6u.ToEndian(EndianUtils.Endianness.BigEndian));             // mflr r0
            ms.WriteUInt32(0x91010014u.ToEndian(EndianUtils.Endianness.BigEndian));             // stw r8,  0x0014 (sp)
            ms.WriteUInt32(0x91210018u.ToEndian(EndianUtils.Endianness.BigEndian));             // stw r9,  0x0018 (sp)
            ms.WriteUInt32(0x9141001Cu.ToEndian(EndianUtils.Endianness.BigEndian));             // stw r10, 0x001C (sp)

            foreach (var data in GetChunksForTexPointersFix(dol))
            {
                var imm = GenerateHighLowImmediatesFor32BitLoad(data.where);
                ms.WriteUInt32((0x3c600000u | (imm.high & 0xffffu)).ToEndian(EndianUtils.Endianness.BigEndian));   // lis r3, imm.high
                ms.WriteUInt32((0x38630000u | (imm.low & 0xffffu)).ToEndian(EndianUtils.Endianness.BigEndian));    // addi r3, r3, imm.low
                ms.WriteUInt32((0x38800000u | (data.count & 0xffffu)).ToEndian(EndianUtils.Endianness.BigEndian)); // li r4, data.count
                ms.WriteUInt32((0x38a00000u | (data.skip & 0xffffu)).ToEndian(EndianUtils.Endianness.BigEndian));  // li r5, data.skip
                ms.WriteUInt32(0x80de0954u.ToEndian(EndianUtils.Endianness.BigEndian));                            // lwz r6, 0x0954 (r30)
                uint here = dol.MapRomToRam((uint)ms.Position);
                uint diff = repointingEntryPoint - here;
                ms.WriteUInt32((0x48000001u | (diff & 0x3fffffcu)).ToEndian(EndianUtils.Endianness.BigEndian));                 // bl --> repointingEntryPoint
            }

            ms.WriteUInt32(0x8141001Cu.ToEndian(EndianUtils.Endianness.BigEndian));             // lwz r10, 0x001C (sp)
            ms.WriteUInt32(0x81210018u.ToEndian(EndianUtils.Endianness.BigEndian));             // lwz r9,  0x0018 (sp)
            ms.WriteUInt32(0x81010014u.ToEndian(EndianUtils.Endianness.BigEndian));             // lwz r8,  0x0014 (sp)
            ms.WriteUInt32(0x7c0803a6u.ToEndian(EndianUtils.Endianness.BigEndian));             // mtlr r0
            ms.WriteUInt32(0x38210050u.ToEndian(EndianUtils.Endianness.BigEndian));             // addi sp, sp, 0x50
            long fixupPos = ms.Position;

            ms.WriteUInt32(0u);                                                     // placeholder for the instruction we overwrite to jump to our code
            ms.WriteUInt32(0x4e800020u.ToEndian(EndianUtils.Endianness.BigEndian)); // blr

            // and finally, actually hook code
            ms.Position = dol.MapRamToRom(0x8006be48u);             // right after font texture is loaded into memory
            uint replacedInstruction = ms.PeekUInt32();

            {
                uint here = dol.MapRomToRam((uint)ms.Position);
                uint diff = pointerFixCodeEntryPoint - here;
                ms.WriteUInt32((0x48000001u | (diff & 0x3fffffcu)).ToEndian(EndianUtils.Endianness.BigEndian));                 // bl --> pointerFixCodeEntryPoint
            }
            ms.Position = fixupPos;
            ms.WriteUInt32(replacedInstruction);
        }