private List <UInt32> writeSubroutineForceFetchFakeVentureCard(VAVAddr fakeVentureCard)
        {
            PowerPcAsm.Pair16Bit v = PowerPcAsm.make16bitValuePair((UInt32)fakeVentureCard);

            // precondition: r3 is ChanceCardUI *
            // ChanceCardUI->field_0x34 is ChanceBoard *
            // ChanceBoard->field_0x158 is current venture card id

            var asm = new List <UInt32>();

            asm.Add(PowerPcAsm.lis(6, v.upper16Bit));      // \
            asm.Add(PowerPcAsm.addi(6, 6, v.lower16Bit));  // / r6 <- forceVentureCardVariable
            asm.Add(PowerPcAsm.lwz(4, 0x0, 6));            // | r4 <- forceVentureCard
            asm.Add(PowerPcAsm.cmpwi(4, 0x0));             // | if(forceVentureCard != 0)
            asm.Add(PowerPcAsm.beq(7));                    // | {
            asm.Add(PowerPcAsm.lwz(5, 0x34, 3));           // |   r5 <- ChanceCardUI.ChanceBoard
            asm.Add(PowerPcAsm.stw(4, 0x158, 5));          // |   ChanceBoard.currentVentureCardId <- r4
            asm.Add(PowerPcAsm.li(5, 0));                  // |\  forceVentureCard <- 0
            asm.Add(PowerPcAsm.stw(5, 0x0, 6));            // |/
            asm.Add(PowerPcAsm.li(8, 0x0));                // |   r8 <- 0 (the venture card is initialized)
            asm.Add(PowerPcAsm.blr());                     // |   return r4 and r8
                                                           // | }
            asm.Add(PowerPcAsm.li(4, -0x1));               // | r4 <- -1
            asm.Add(PowerPcAsm.li(8, 0x3));                // | r8 <- 3 (the venture card is continued to be executed)
            asm.Add(PowerPcAsm.blr());                     // | return r4 and r8


            return(asm);
        }
Example #2
0
        protected override bool readIsVanilla(EndianBinaryReader stream, AddressMapper addressMapper)
        {
            stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8007e130), SeekOrigin.Begin);
            var opcode = stream.ReadUInt32();

            return(opcode == PowerPcAsm.li(5, 0));
        }
        private List <UInt32> writeSubroutineMakeNoneMapIconsInvisible(AddressMapper addressMapper, VAVAddr entryAddr, VAVAddr returnContinueAddr, VAVAddr returnMakeInvisibleAddr)
        {
            var Scene_Layout_Obj_SetVisible = addressMapper.toVersionAgnosticAddress((BSVAddr)0x8006f854);
            // precondition:  r31  MapIconButton*
            //                 r5  is unused
            // postcondition:  r0  is map icon type
            //                 r5  is 0
            var asm = new List <UInt32>();

            asm.Add(PowerPcAsm.lwz(5, 0x188, 31));                                     // get current map id into r5
            asm.Add(PowerPcAsm.cmpwi(5, -1));                                          // map id == -1 ?
            asm.Add(PowerPcAsm.bne(8));                                                // {
            asm.Add(PowerPcAsm.lwz(3, 0x28, 31));                                      //   \ 
            asm.Add(PowerPcAsm.li(5, 0));                                              //   |
            asm.Add(PowerPcAsm.lwz(4, -0x6600, 13));                                   //   | make "NEW" text invisible
            asm.Add(PowerPcAsm.bl(entryAddr, asm.Count, Scene_Layout_Obj_SetVisible)); //   /
            asm.Add(PowerPcAsm.lwz(3, 0x28, 31));                                      //   \ 
            asm.Add(PowerPcAsm.li(5, 0));                                              //   / make Locked Map Icon "(?)" invisible
            asm.Add(PowerPcAsm.b(entryAddr, asm.Count, returnMakeInvisibleAddr));      //   returnMakeInvisibleAddr
                                                                                       // } else {
            asm.Add(PowerPcAsm.lwz(0, 0x184, 3));                                      //   get map icon type (replaced opcode)
            asm.Add(PowerPcAsm.b(entryAddr, asm.Count, returnContinueAddr));           //   returnContinueAddr
                                                                                       // }
            return(asm);
        }
        private List <UInt32> writeProcStopEventSquareRoutine(AddressMapper addressMapper, VAVAddr forceVentureCardVariable, VAVAddr routineStartAddress)
        {
            PowerPcAsm.Pair16Bit v            = PowerPcAsm.make16bitValuePair((UInt32)forceVentureCardVariable);
            var gameProgressChangeModeRoutine = addressMapper.toVersionAgnosticAddress((BSVAddr)0x800c093c);
            var endOfSwitchCase = addressMapper.toVersionAgnosticAddress((BSVAddr)0x800fac38);

            var asm = new List <UInt32>();

            asm.Add(PowerPcAsm.lwz(3, 0x188, 28));                                                 // \
            asm.Add(PowerPcAsm.lwz(3, 0x74, 3));                                                   // / r3_place = gameChara.currentPlace
            asm.Add(PowerPcAsm.lbz(6, 0x18, 3));                                                   // | r6_ventureCardId = r3_place.districtId

            asm.Add(PowerPcAsm.lis(3, v.upper16Bit));                                              // \
            asm.Add(PowerPcAsm.addi(3, 3, v.lower16Bit));                                          // | forceVentureCardVariable <- r6_ventureCardId
            asm.Add(PowerPcAsm.stw(6, 0x0, 3));                                                    // /

            asm.Add(PowerPcAsm.lwz(3, 0x18, 20));                                                  // \ lwz r3,0x18(r20)
            asm.Add(PowerPcAsm.li(4, 0x1f));                                                       // | li r4,0x1f  (the GameProgress mode id 0x1f is for executing a venture card)
            asm.Add(PowerPcAsm.li(5, -0x1));                                                       // | li r5,-0x1
            asm.Add(PowerPcAsm.li(6, -0x1));                                                       // | li r6,-0x1
            asm.Add(PowerPcAsm.li(7, 0x0));                                                        // | li r7,0x0
            asm.Add(PowerPcAsm.bl(routineStartAddress, asm.Count, gameProgressChangeModeRoutine)); // | bl Game::GameProgress::changeMode
            asm.Add(PowerPcAsm.b(routineStartAddress, asm.Count, endOfSwitchCase));                // / goto end of switch case
            return(asm);
        }
        private List <UInt32> writeSubroutineGetMapsInZone(AddressMapper addressMapper, List <MapDescriptor> mapDescriptors, VAVAddr mapSetZoneOrderTable, VAVAddr entryAddr, VAVAddr returnAddr)
        {
            PowerPcAsm.Pair16Bit v = PowerPcAsm.make16bitValuePair((UInt32)mapSetZoneOrderTable);
            // precondition:  r5  MapSet
            //               r29  _ZONE_TYPE
            //               r30  int* array containing map ids
            //                r3,r4,r6,r7,r31  unused
            // postcondition: r3  num maps (must be same as in SubroutineGetNumMapsInZone)
            var asm    = new List <UInt32>();
            var asm_l2 = new List <UInt32>();
            var asm_l3 = new List <UInt32>();

            asm.Add(PowerPcAsm.li(3, 0));
            var mapSets = (from m in mapDescriptors where m.MapSet != -1 orderby m.MapSet select m.MapSet).Distinct();

            foreach (var mapSet in mapSets)
            {
                asm.Add(PowerPcAsm.cmpwi(5, mapSet));
                var zones = (from m in mapDescriptors where m.MapSet == mapSet && m.Zone != -1 orderby m.Zone select m.Zone).Distinct();
                asm_l2.Clear();
                foreach (var zone in zones)
                {
                    asm_l2.Add(PowerPcAsm.cmpwi(29, zone));
                    IOrderedEnumerable <MapDescriptor> maps;
                    if (zone == 0)
                    {
                        maps = from m in mapDescriptors where m.MapSet == mapSet && m.Zone == 1 orderby m.Order select m;
                    }
                    else if (zone == 1)
                    {
                        maps = from m in mapDescriptors where m.MapSet == mapSet && m.Zone == 0 orderby m.Order select m;
                    }
                    else
                    {
                        maps = from m in mapDescriptors where m.MapSet == mapSet && m.Zone == zone orderby m.Order select m;
                    }
                    short i = 0;
                    asm_l3.Clear();
                    asm_l3.Add(PowerPcAsm.li(3, (short)maps.Count()));
                    foreach (var map in maps)
                    {
                        short mapId         = (short)mapDescriptors.IndexOf(map);
                        var   mapDescriptor = mapDescriptors[i];
                        asm_l3.Add(PowerPcAsm.li(4, mapId));
                        asm_l3.Add(PowerPcAsm.stw(4, i, 30));
                        i += 4;
                    }
                    asm_l2.Add(PowerPcAsm.bne(asm_l3.Count + 1));
                    asm_l2.AddRange(asm_l3);
                }
                asm.Add(PowerPcAsm.bne(asm_l2.Count + 1));
                asm.AddRange(asm_l2);
            }
            asm.Add(PowerPcAsm.b(entryAddr, asm.Count, returnAddr));
            return(asm);
        }
Example #6
0
        /// <summary>
        /// Write the subroutine which takes a compressed venture card table as input and writes it into ventureCardDecompressedTableAddr
        /// </summary>
        /// <param name="ventureCardDecompressedTableAddr">The address for the reserved memory space to store the decompressed venture card table in</param>
        private List <UInt32> writeSubroutine(VAVAddr ventureCardDecompressedTableAddr)
        {
            var asm = new List <UInt32>();

            PowerPcAsm.Pair16Bit ventureCardTableAddrPair = PowerPcAsm.make16bitValuePair((UInt32)ventureCardDecompressedTableAddr);
            ///
            /// assume:
            /// r6 = ventureCardCompressedTableAddr
            ///
            /// variables:
            /// r4 = ventureCardId
            /// r5 = ventureCardCompressedTableAddr
            /// r6 = ventureCardDecompressedTableAddr
            /// r7 = ventureCardCompressedWord
            /// r8 = bitIndex
            /// r0 = tmp / currentByte
            ///
            /// return:
            /// r6 = ventureCardDecompressedTableAddr
            ///
            asm.Add(PowerPcAsm.li(4, 0));                                                           // ventureCardId = 0
            asm.Add(PowerPcAsm.mr(5, 6));                                                           // r6 is ventureCardCompressedTableAddr at this point. Copy it to r5 with which we will be working
            asm.Add(PowerPcAsm.lis(6, ventureCardTableAddrPair.upper16Bit));                        // \ load the ventureCardDecompressedTableAddr into r6. This address is
            asm.Add(PowerPcAsm.addi(6, 6, ventureCardTableAddrPair.lower16Bit));                    // /  where we will store the decompressed venture card table.
            int whileVentureCardIdSmaller128 = asm.Count;                                           // do {

            {                                                                                       //
                asm.Add(PowerPcAsm.li(0, 0));                                                       //     \ load the next compressed word from ventureCardCompressedTableAddr
                asm.Add(PowerPcAsm.lwzx(7, 5, 0));                                                  //     /  into r7. We will decompress the venture card table word by word.
                asm.Add(PowerPcAsm.li(8, 31));                                                      //     bitIndex = 31
                int whileBitIndexGreaterEqual32 = asm.Count;                                        //     do
                {                                                                                   //     {
                    asm.Add(PowerPcAsm.mr(0, 7));                                                   //         get the current compressed word
                    asm.Add(PowerPcAsm.srw(0, 0, 8));                                               //         shift it bitIndex times to the right
                    asm.Add(PowerPcAsm.andi(0, 0, 1));                                              //         retrieve the lowest bit of it -> r0 contains the decompressed venture card byte now.
                    asm.Add(PowerPcAsm.stbx(0, 4, 6));                                              //         store it into ventureCardDecompressedTableAddr[ventureCardId]
                    asm.Add(PowerPcAsm.subi(8, 8, 1));                                              //         bitIndex--
                    asm.Add(PowerPcAsm.addi(4, 4, 1));                                              //         ventureCardId++
                    asm.Add(PowerPcAsm.cmpwi(8, 0));                                                //
                    asm.Add(PowerPcAsm.bge(asm.Count, whileBitIndexGreaterEqual32));                //     } while(bitIndex >= 0)
                }                                                                                   //
                asm.Add(PowerPcAsm.addi(5, 5, 4));                                                  //     ventureCardCompressedTableAddr += 4
                asm.Add(PowerPcAsm.cmpwi(4, 128));                                                  //
                asm.Add(PowerPcAsm.blt(asm.Count, whileVentureCardIdSmaller128));                   // } while(ventureCardId < 128)
            }                                                                                       //
            asm.Add(PowerPcAsm.li(4, 0));                                                           // \ reset r4 = 0
            asm.Add(PowerPcAsm.li(5, 0));                                                           // / reset r5 = 0
            asm.Add(PowerPcAsm.blr());                                                              // return

            return(asm);
        }
        private List <UInt32> writeGetTextureForCustomSquareRoutine(byte register_textureType, byte register_squareType)
        {
            var asm = new List <UInt32>();

            asm.Add(PowerPcAsm.li(register_textureType, 0x1));    // textureType = 1
            asm.Add(PowerPcAsm.cmpwi(register_squareType, 0x2e)); // if(squareType == 0x2e)
            asm.Add(PowerPcAsm.beq(2));                           // {
            asm.Add(PowerPcAsm.blr());                            //   return textureType;
                                                                  // } else {
            asm.Add(PowerPcAsm.li(register_textureType, 0x5));    //   textureType = 5
            asm.Add(PowerPcAsm.blr());                            //   return textureType;
                                                                  // }
            return(asm);
        }
Example #8
0
        public List <MapDescriptor> writeMainDol(EndianBinaryWriter stream, List <MapDescriptor> mapDescriptors, IProgress <ProgressInfo> progress)
        {
            foreach (var patch in patches)
            {
                patch.write(stream, addressMapper, mapDescriptors, freeSpaceManager, progress);
            }

            freeSpaceManager.nullTheFreeSpace(stream, addressMapper);

            // Write the Map Count
            stream.Seek(addressMapper.toFileAddress((BSVAddr)0x801cca30), SeekOrigin.Begin);
            stream.Write(PowerPcAsm.li(3, (short)mapDescriptors.Count));

            return(mapDescriptors);
        }
        private List <UInt32> writeSubroutineGetNumMapsInZone(List <MapDescriptor> mapDescriptors)
        {
            // precondition:  r3  _ZONE_TYPE
            // postcondition: r3  num maps
            var asm = new List <UInt32>();

            for (short i = 0; i < 6; i++)
            {
                asm.Add(PowerPcAsm.cmpwi(3, i));
                asm.Add(PowerPcAsm.bne(3));
                asm.Add(PowerPcAsm.li(3, (short)(from m in mapDescriptors where m.Zone == i && m.MapSet == 0 select m).Count()));
                asm.Add(PowerPcAsm.blr());
            }
            asm.Add(PowerPcAsm.blr());
            return(asm);
        }
        private List <UInt32> writeSubroutineInitMapIdsForMapIcons(AddressMapper addressMapper, VAVAddr entryAddr)
        {
            var JUtility_memset = addressMapper.toVersionAgnosticAddress((BSVAddr)0x80004714);
            // precondition: r3 is newly created map icon array
            //               r16 is the amount of map ids in the array (size / 4)
            //               r24 is unused
            // postcondition: r24 is the map icon array
            var asm = new List <UInt32>();

            asm.Add(PowerPcAsm.mflr(24));                                     // save the link register
            asm.Add(PowerPcAsm.li(4, -1));                                    // fill with 0xff
            asm.Add(PowerPcAsm.rlwinm(5, 16, 0x3, 0x0, 0x1d));                // get the size of the array
            asm.Add(PowerPcAsm.bl(entryAddr, asm.Count, JUtility_memset));    // call JUtility_memset(array*, 0xff, array.size)
            asm.Add(PowerPcAsm.mtlr(24));                                     // restore the link register
            asm.Add(PowerPcAsm.mr(24, 3));                                    // move array* to r24
            asm.Add(PowerPcAsm.blr());                                        // return
            return(asm);
        }
        private List <UInt32> writeUploadSimulatedButtonPress(AddressMapper addressMapper, VAVAddr routineStartAddress, VAVAddr returnAddr)
        {
            var asm = new List <UInt32>();
            // 0x804363b4 (4 bytes): force simulated button press
            var forceSimulatedButtonPress = PowerPcAsm.make16bitValuePair((UInt32)addressMapper.toVersionAgnosticAddress((BSVAddr)0x804363b4));
            var pressedButtonsBitArray    = PowerPcAsm.make16bitValuePair((UInt32)addressMapper.toVersionAgnosticAddress((BSVAddr)0x8078C880));

            asm.Add(PowerPcAsm.lis(6, forceSimulatedButtonPress.upper16Bit));                // \
            asm.Add(PowerPcAsm.addi(6, 6, forceSimulatedButtonPress.lower16Bit));            // / r6 <- &forceSimulatedButtonPress
            asm.Add(PowerPcAsm.lis(7, pressedButtonsBitArray.upper16Bit));                   // \
            asm.Add(PowerPcAsm.addi(7, 7, pressedButtonsBitArray.lower16Bit));               // / r7 <- &pressedButtonsBitArray
            asm.Add(PowerPcAsm.lwz(0, 0x0, 6));                                              // r0 <- forceSimulatedButtonPress
            asm.Add(PowerPcAsm.cmpwi(0, 0x0));                                               // if (forceSimulatedButtonPress != 0)
            asm.Add(PowerPcAsm.beq(4));                                                      // {
            asm.Add(PowerPcAsm.stw(0, 0x0, 7));                                              //   pressedButtonsBitArray <- forceSimulatedButtonPress
            asm.Add(PowerPcAsm.li(0, 0x0));                                                  //   \ 
            asm.Add(PowerPcAsm.stw(0, 0x0, 6));                                              //   / forceSimulatedButtonPress <- 0
                                                                                             // }
            asm.Add(PowerPcAsm.lwz(0, 0x4, 3));                                              // *replaced opcode*
            asm.Add(PowerPcAsm.b(routineStartAddress, asm.Count, returnAddr));               // return

            return(asm);
        }
        private List <UInt32> writeGetDescriptionForCustomSquareRoutine(AddressMapper addressMapper, VAVAddr routineStartAddress)
        {
            var asm = new List <UInt32>();
            var gameUiTextGetString  = addressMapper.toVersionAgnosticAddress((BSVAddr)0x800f78dc);
            var gameUiTextGetCardMsg = addressMapper.toVersionAgnosticAddress((BSVAddr)0x800f837c);
            var gameBoard            = PowerPcAsm.make16bitValuePair((UInt32)addressMapper.toVersionAgnosticAddress((BSVAddr)0x8054d018));

            asm.Add(PowerPcAsm.lis(7, gameBoard.upper16Bit));                                //
            asm.Add(PowerPcAsm.addi(7, 7, gameBoard.lower16Bit));                            // r7 <- start of gameboard table containing all squares
            asm.Add(PowerPcAsm.mulli(8, 24, 0x54));                                          // r8 <- squareId * 0x54 (the size of each square)
            asm.Add(PowerPcAsm.add(6, 7, 8));                                                // r6 <- the current square
            asm.Add(PowerPcAsm.lbz(8, 0x4d, 6));                                             // r8 <- square.squareType
            asm.Add(PowerPcAsm.cmpwi(8, 0x2e));                                              // if(square.squareType == 0x2e)
            asm.Add(PowerPcAsm.bne(3));                                                      // {
            asm.Add(PowerPcAsm.lbz(4, 0x18, 6));                                             //   r4 <- square.district_color
            asm.Add(PowerPcAsm.b(routineStartAddress, asm.Count, gameUiTextGetCardMsg));     //   goto Game::uitext::get_card_message(r4)
                                                                                             // }
            asm.Add(PowerPcAsm.li(6, 0x0));                                                  // \
            asm.Add(PowerPcAsm.li(7, 0x0));                                                  // | No message arguments
            asm.Add(PowerPcAsm.li(8, 0x0));                                                  // /
            asm.Add(PowerPcAsm.b(routineStartAddress, asm.Count, gameUiTextGetString));      // goto Game::uitext::get_string(r4, 0, 0, 0)

            return(asm);
        }
        protected override void writeAsm(EndianBinaryWriter stream, AddressMapper addressMapper, List <MapDescriptor> mapDescriptors)
        {
            var tableAddr = writeTable(mapDescriptors);

            // --- Game::GameSequenceDataAdapter::GetNumMapsInZone ---
            var subroutineGetNumMapsInZone = allocate(writeSubroutineGetNumMapsInZone(mapDescriptors), "SubroutineGetNumMapsInZone");
            var hijackAddr = addressMapper.toVersionAgnosticAddress((BSVAddr)0x8020f3ac);

            stream.Seek(addressMapper.toFileAddress(hijackAddr), SeekOrigin.Begin);
            // li r3,0x6 ->  b subroutineGetNumMapsInZone
            stream.Write(PowerPcAsm.b(hijackAddr, subroutineGetNumMapsInZone));

            // --- Game::GameSequenceDataAdapter::GetMapsInZone ---
            hijackAddr = addressMapper.toVersionAgnosticAddress((BSVAddr)0x8020f454);
            var returnAddr = addressMapper.toVersionAgnosticAddress((BSVAddr)0x8020f554);
            var subroutineGetMapsInZone = allocate(writeSubroutineGetMapsInZone(addressMapper, mapDescriptors, tableAddr, VAVAddr.NullAddress, returnAddr), "SubroutineGetMapsInZone");

            stream.Seek(addressMapper.toFileAddress(subroutineGetMapsInZone), SeekOrigin.Begin);
            stream.Write(writeSubroutineGetMapsInZone(addressMapper, mapDescriptors, tableAddr, subroutineGetMapsInZone, returnAddr)); // re-write the routine again since now we know where it is located in the main dol
            stream.Seek(addressMapper.toFileAddress(hijackAddr), SeekOrigin.Begin);
            // cmpwi r29,0x0 ->  b subroutineGetMapsInZone
            stream.Write(PowerPcAsm.b(hijackAddr, subroutineGetMapsInZone));

            // --- Write Table Meta Information ---
            stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020f458), SeekOrigin.Begin);
            stream.Write((uint)tableAddr);
            stream.Write((short)0);
            stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020F45E), SeekOrigin.Begin);
            stream.Write((short)mapDescriptors.Count);

            // --- Fix Wifi Map Selection ---
            // bl GameSequenceDataAdapter::GetMapOrigin(r3)                               -> nop
            stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80185ac4), SeekOrigin.Begin); stream.Write(PowerPcAsm.nop());
            // or r31,r3,r3                                                               -> or r31,r4,r4
            stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80185ac8), SeekOrigin.Begin); stream.Write(PowerPcAsm.or(31, 4, 4));
            // li r5,0x1                                                                  -> li r5,0x2
            stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80185b10), SeekOrigin.Begin); stream.Write(PowerPcAsm.li(5, 2));
            // bl GameSequenceDataAdapter::GetMapOrigin(r3)                               -> nop
            stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8024b1b8), SeekOrigin.Begin); stream.Write(PowerPcAsm.nop());
            // bl GameSequenceDataAdapter::GetMapOrigin(r3)                               -> nop
            stream.Seek(addressMapper.toFileAddress((BSVAddr)0x802498a8), SeekOrigin.Begin);
            for (int i = 0; i < 8; i++)
            {
                stream.Write(PowerPcAsm.nop());
            }
            // --- Default selected map button in wifi ---
            // since Standard Mode is selected on default, we use this mapset to find the standard map
            // TODO need asm hack to determine currently selected MapSet and use rather use that ID
            short defaultMap = 0;

            for (short i = 0; i < mapDescriptors.Count; i++)
            {
                var mapDescriptor = mapDescriptors[i];
                if (mapDescriptor.MapSet == 1 && mapDescriptor.Zone == 0 && mapDescriptor.Order == 0)
                {
                    defaultMap = i;
                }
            }
            // 0x9 = Castle Trodain
            // li r3,0x9                                                                  -> li r3,0x9
            stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8024afc8), SeekOrigin.Begin); stream.Write(PowerPcAsm.li(3, defaultMap));
            // TODO 0x13 is some special handling map id. Need to check what is going on with it
            // li r3,0x13                                                                 -> li r3,0x13
            stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80243ae4), SeekOrigin.Begin); stream.Write(PowerPcAsm.li(5, 0x13));
            // --- Fix out of array bounds error when opening tour mode and viewing the zones and having more than 6 maps in a zone ---
            // bl Game::GameSequenceDataAdapter::GetNumMapsInZone    -> li r3,0x6
            stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8021f880), SeekOrigin.Begin); stream.Write(PowerPcAsm.li(3, 0x6));
            // bl Game::GameSequenceDataAdapter::GetNumMapsInZone    -> li r3,0x6
            stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8021ff4c), SeekOrigin.Begin); stream.Write(PowerPcAsm.li(3, 0x6));
        }
        protected override void writeAsm(EndianBinaryWriter stream, AddressMapper addressMapper, List <MapDescriptor> mapDescriptors)
        {
            short easyPracticeBoard     = -1;
            short standardPracticeBoard = -1;
            var   validation            = MapDescriptor.getPracticeBoards(mapDescriptors, out easyPracticeBoard, out standardPracticeBoard);

            if (!validation.Passed)
            {
                throw new ArgumentException(validation.GetMessage("\n"));
            }
            // li r0,0x29                                                                 -> li r0,easyPracticeBoard
            stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80173bf8), SeekOrigin.Begin); stream.Write(PowerPcAsm.li(0, easyPracticeBoard));
            // li r0,0x14                                                                 -> li r0,standardPracticeBoard
            stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80173c04), SeekOrigin.Begin); stream.Write(PowerPcAsm.li(0, standardPracticeBoard));
        }