protected override void writeAsm(EndianBinaryWriter stream, AddressMapper addressMapper, List <MapDescriptor> mapDescriptors) { var tableRowCount = mapDescriptors.Count; var tableAddr = writeTable(mapDescriptors); PowerPcAsm.Pair16Bit v = PowerPcAsm.make16bitValuePair((UInt32)tableAddr); // subi r30,r30,0x15 -> nop stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020d0dc), SeekOrigin.Begin); stream.Write(PowerPcAsm.nop()); // cmpwi r30,0x12 -> cmpwi r30,tableElementsCount stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020d0e8), SeekOrigin.Begin); stream.Write(PowerPcAsm.cmpwi(30, (short)(tableRowCount))); // li r0,0x15 -> nop stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020d160), SeekOrigin.Begin); stream.Write(PowerPcAsm.nop()); // mulli r0,r0,0x24 -> mulli r0,r0,0x04 stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020d16c), SeekOrigin.Begin); stream.Write(PowerPcAsm.mulli(0, 0, 0x04)); // r4 <- 804363c8 -> r4 <- tableAddr stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020d170), SeekOrigin.Begin); stream.Write(PowerPcAsm.lis(4, v.upper16Bit)); stream.Write(PowerPcAsm.addi(4, 4, v.lower16Bit)); // mulli r3,r30,0x24 -> mulli r3,r30,0x04 stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020d178), SeekOrigin.Begin); stream.Write(PowerPcAsm.mulli(3, 30, 0x04)); // subi r31,r3,0x15 -> nop stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211c04), SeekOrigin.Begin); stream.Write(PowerPcAsm.nop()); // cmpwi r31,0x12 -> cmpwi r31,tableElementsCount stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211c10), SeekOrigin.Begin); stream.Write(PowerPcAsm.cmpwi(31, (short)(tableRowCount))); // li r3,0x15 -> nop stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211c88), SeekOrigin.Begin); stream.Write(PowerPcAsm.nop()); // mulli r0,r3,0x24 -> mulli r0,r3,0x04 stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211c94), SeekOrigin.Begin); stream.Write(PowerPcAsm.mulli(0, 3, 0x04)); // r4 <- 804363c8 -> r4 <- tableAddr stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211c98), SeekOrigin.Begin); stream.Write(PowerPcAsm.lis(4, v.upper16Bit)); stream.Write(PowerPcAsm.addi(4, 4, v.lower16Bit)); // mulli r3,r31,0x24 -> mulli r3,r31,0x04 stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211ca0), SeekOrigin.Begin); stream.Write(PowerPcAsm.mulli(3, 31, 0x04)); }
protected override void writeAsm(EndianBinaryWriter stream, AddressMapper addressMapper, List <MapDescriptor> mapDescriptors) { short tableRowCount = (short)mapDescriptors.Count; var mapDescriptionTableAddr = writeTable(mapDescriptors); PowerPcAsm.Pair16Bit v = PowerPcAsm.make16bitValuePair((UInt32)mapDescriptionTableAddr); // HACK: Expand the description message ID table // subi r3,r3,0x15 -> nop stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8021214c), SeekOrigin.Begin); stream.Write(PowerPcAsm.nop()); // cmpwi r3,0x12 -> cmpwi r3,tableRowCount stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80212158), SeekOrigin.Begin); stream.Write(PowerPcAsm.cmpwi(3, tableRowCount)); // r4 <- 0x80436bc0 -> r4 <- mapDescriptionTableAddr stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80212164), SeekOrigin.Begin); stream.Write(PowerPcAsm.lis(4, v.upper16Bit)); stream.Seek(4, SeekOrigin.Current); stream.Write(PowerPcAsm.addi(4, 4, v.lower16Bit)); }
protected override void writeAsm(EndianBinaryWriter stream, AddressMapper addressMapper, List <MapDescriptor> mapDescriptors) { var mapIcons = writeIconStrings(mapDescriptors); Dictionary <string, VAVAddr> iconTableMap; VAVAddr iconTableAddr = writeIconTable(mapIcons, out iconTableMap); VAVAddr mapIconPointerTable = writeMapIconPointerTable(mapDescriptors, iconTableMap); ushort iconCount = (ushort)iconTableMap.Count; short tableRowCount = (short)mapDescriptors.Count; PowerPcAsm.Pair16Bit v = PowerPcAsm.make16bitValuePair((UInt32)iconTableAddr); PowerPcAsm.Pair16Bit w = PowerPcAsm.make16bitValuePair((UInt32)mapIconPointerTable); // note: To add custom icons, the following files need to be editted as well: // - ui_menu_19_00a.brlyt within game_sequence.arc and within game_sequence_wifi.arc // - ui_menu_19_00a_Tag_*.brlan within game_sequence.arc and within game_sequence_wifi.arc // custom map icon hack (change it that way that it will call the GetMapDifficulty routine instead of the GetMapOrigin routine // the GetMapDifficulty routine is mostly unused by the game and we repurpose it to return the pointer to the pointer of the string of the map icon instead // then we go through all map icon pointer pointers and check if it is the same as the one retrieved. If it is then we make it visible, otherwise we set the visibility to false. var GetMapDifficulty = addressMapper.toVersionAgnosticAddress((BSVAddr)0x80211da4); // bl GetMapOrigin -> bl GetMapDifficulty var offset = addressMapper.toVersionAgnosticAddress((BSVAddr)0x8021e77c); stream.Seek(addressMapper.toFileAddress(offset), SeekOrigin.Begin); stream.Write(PowerPcAsm.bl(offset, GetMapDifficulty)); // cmpw r28,r30 -> cmpw r29,r30 stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8021e790), SeekOrigin.Begin); stream.Write(PowerPcAsm.cmpw(29, 30)); // cmplwi r28,0x12 -> cmplwi r28,iconCount stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8021e7c0), SeekOrigin.Begin); stream.Write(PowerPcAsm.cmplwi(28, iconCount)); // bl GetMapOrigin -> bl GetMapDifficulty offset = addressMapper.toVersionAgnosticAddress((BSVAddr)0x8021e8a4); stream.Seek(addressMapper.toFileAddress(offset), SeekOrigin.Begin); stream.Write(PowerPcAsm.bl(offset, GetMapDifficulty)); // cmpw r29,r28 -> cmpw r30,r28 stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8021e8b8), SeekOrigin.Begin); stream.Write(PowerPcAsm.cmpw(30, 28)); // cmplwi r29,0x12 -> cmplwi r29,iconCount stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8021e8e8), SeekOrigin.Begin); stream.Write(PowerPcAsm.cmplwi(29, iconCount)); // bl GetMapOrigin -> bl GetMapDifficulty offset = addressMapper.toVersionAgnosticAddress((BSVAddr)0x8021e824); stream.Seek(addressMapper.toFileAddress(offset), SeekOrigin.Begin); stream.Write(PowerPcAsm.bl(offset, GetMapDifficulty)); // cmplwi r28,0x12 -> cmplwi r28,iconCount stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8021e84c), SeekOrigin.Begin); stream.Write(PowerPcAsm.cmplwi(28, iconCount)); // r29 <- 0x8047f5c0 -> r29 <- iconTableAddr stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8021e780), SeekOrigin.Begin); stream.Write(PowerPcAsm.lis(29, v.upper16Bit)); stream.Seek(4, SeekOrigin.Current); stream.Write(PowerPcAsm.addi(29, 29, v.lower16Bit)); // r30 <- 0x8047f5c0 -> r30 <- iconTableAddr stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8021e8a8), SeekOrigin.Begin); stream.Write(PowerPcAsm.lis(30, v.upper16Bit)); stream.Seek(4, SeekOrigin.Current); stream.Write(PowerPcAsm.addi(30, 30, v.lower16Bit)); // r30 <- 0x8047f5c0 -> r30 <- iconTableAddr stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8021e828), SeekOrigin.Begin); stream.Write(PowerPcAsm.lis(30, v.upper16Bit)); stream.Seek(4, SeekOrigin.Current); stream.Write(PowerPcAsm.addi(30, 30, v.lower16Bit)); // mr r3,r28 -> mr r3,r26 stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8021e94c), SeekOrigin.Begin); stream.Write(PowerPcAsm.mr(3, 26)); // mr r3,r28 -> mr r3,r26 stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8021e968), SeekOrigin.Begin); stream.Write(PowerPcAsm.mr(3, 26)); // Modify the GetMapDifficulty routine to retrieve the current map icon addr addr // subi r31,r3,0x15 -> nop stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211dc8), SeekOrigin.Begin); stream.Write(PowerPcAsm.nop()); // cmpwi r31,0x12 -> cmpwi r31,tableRowCount stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211dd4), SeekOrigin.Begin); stream.Write(PowerPcAsm.cmpwi(31, tableRowCount)); // li r3,0x15 -> nop stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211e4c), SeekOrigin.Begin); stream.Write(PowerPcAsm.nop()); // mulli r4,r3,0x24 -> mulli r4,r3,0x04 stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211e58), SeekOrigin.Begin); stream.Write(PowerPcAsm.mulli(4, 3, 0x04)); // r3 <- 804363c8 -> r3 <- mapIconPointerTable stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211e5c), SeekOrigin.Begin); stream.Write(PowerPcAsm.lis(3, w.upper16Bit)); stream.Write(PowerPcAsm.addi(3, 3, w.lower16Bit)); // mulli r0,r31,0x24 -> mulli r0,r31,0x04 stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211e64), SeekOrigin.Begin); stream.Write(PowerPcAsm.mulli(0, 31, 0x04)); // lwz r3,0x1c(r3) -> lwz r3,0x0(r3) stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211e78), SeekOrigin.Begin); stream.Write(PowerPcAsm.lwz(3, 0x0, 3)); // --- Hack to make icons invisible which do not have a map --- // -- Init Maps in the map array with -1 -- var subroutineInitMapIdsForMapIcons = allocate(writeSubroutineInitMapIdsForMapIcons(addressMapper, VAVAddr.NullAddress), "SubroutineInitMapIdsForMapIcons"); stream.Seek(addressMapper.toFileAddress(subroutineInitMapIdsForMapIcons), SeekOrigin.Begin); stream.Write(writeSubroutineInitMapIdsForMapIcons(addressMapper, subroutineInitMapIdsForMapIcons)); // re-write the routine again since now we know where it is located in the main dol // increase the array size // rlwinm r3,r16,0x2,0x0,0x1d -> r3,r16,0x3,0x0,0x1d stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80187794), SeekOrigin.Begin); stream.Write(PowerPcAsm.rlwinm(3, 16, 0x3, 0x0, 0x1d)); var hijackAddr = addressMapper.toVersionAgnosticAddress((BSVAddr)0x8018779c); // cmpwi r3,0x0 -> bl subroutineInitMapIdsForMapIcons stream.Seek(addressMapper.toFileAddress(hijackAddr), SeekOrigin.Begin); stream.Write(PowerPcAsm.bl(hijackAddr, subroutineInitMapIdsForMapIcons)); // mr r24,r3 -> cmpwi r3,0x0 stream.Write(PowerPcAsm.cmpwi(3, 0)); // increase the array size // rlwinm r3,r16,0x2,0x0,0x1d -> r3,r16,0x3,0x0,0x1d stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80187aa4), SeekOrigin.Begin); stream.Write(PowerPcAsm.rlwinm(3, 16, 0x3, 0x0, 0x1d)); hijackAddr = addressMapper.toVersionAgnosticAddress((BSVAddr)0x80187aac); // cmpwi r3,0x0 -> bl subroutineInitMapIdsForMapIcons stream.Seek(addressMapper.toFileAddress(hijackAddr), SeekOrigin.Begin); stream.Write(PowerPcAsm.bl(hijackAddr, subroutineInitMapIdsForMapIcons)); // mr r24,r3 -> cmpwi r3,0x0 stream.Write(PowerPcAsm.cmpwi(3, 0)); // -- if a map id is -1, make the map icon invisible -- hijackAddr = addressMapper.toVersionAgnosticAddress((BSVAddr)0x8021e73c); var returnContinueAddr = addressMapper.toVersionAgnosticAddress((BSVAddr)0x8021e740); var returnMakeInvisibleAddr = addressMapper.toVersionAgnosticAddress((BSVAddr)0x8021e808); var subroutineMakeNoneMapIconsInvisible = allocate(writeSubroutineMakeNoneMapIconsInvisible(addressMapper, VAVAddr.NullAddress, returnContinueAddr, returnMakeInvisibleAddr), "SubroutineMakeNoneMapIconsInvisibleMultiplayer"); stream.Seek(addressMapper.toFileAddress(subroutineMakeNoneMapIconsInvisible), SeekOrigin.Begin); stream.Write(writeSubroutineMakeNoneMapIconsInvisible(addressMapper, subroutineMakeNoneMapIconsInvisible, returnContinueAddr, returnMakeInvisibleAddr)); // re-write the routine again since now we know where it is located in the main dol // lwz r0,0x184(r3) -> b subroutineMakeNoneMapIconsInvisible stream.Seek(addressMapper.toFileAddress(hijackAddr), SeekOrigin.Begin); stream.Write(PowerPcAsm.b(hijackAddr, subroutineMakeNoneMapIconsInvisible)); // -- if the map id is -1, do not check if it has been unlocked or not --- hijackAddr = addressMapper.toVersionAgnosticAddress((BSVAddr)0x8021e570); returnContinueAddr = addressMapper.toVersionAgnosticAddress((BSVAddr)0x8021e574); var returnSkipMapUnlockedCheck = addressMapper.toVersionAgnosticAddress((BSVAddr)0x8021e5a8); var subroutineWriteSubroutineSkipMapUnlockCheck = allocate(writeSubroutineSkipMapUnlockCheck(addressMapper, VAVAddr.NullAddress, returnContinueAddr, returnSkipMapUnlockedCheck), "SubroutineWriteSubroutineSkipMapUnlockCheck"); stream.Seek(addressMapper.toFileAddress(subroutineWriteSubroutineSkipMapUnlockCheck), SeekOrigin.Begin); stream.Write(writeSubroutineSkipMapUnlockCheck(addressMapper, subroutineWriteSubroutineSkipMapUnlockCheck, returnContinueAddr, returnSkipMapUnlockedCheck)); // re-write the routine again since now we know where it is located in the main dol // or r3,r26,r26 -> b subroutineWriteSubroutineSkipMapUnlockCheck stream.Seek(addressMapper.toFileAddress(hijackAddr), SeekOrigin.Begin); stream.Write(PowerPcAsm.b(hijackAddr, subroutineWriteSubroutineSkipMapUnlockCheck)); // --- Various Map UI Fixes --- // -- if the map index is over the map array size, do not loop around to the first map index again -- // ble 0x80187e1c -> b 0x80187e1c stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80187dfc), SeekOrigin.Begin); stream.Write(PowerPcAsm.b(8)); // -- fix map selection going out of bounds in tour mode -- // bne 0x80188258 -> nop stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80188230), SeekOrigin.Begin); stream.Write(PowerPcAsm.nop()); }
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) { var tableRowCount = mapDescriptors.Count; var tableAddr = writeTable(mapDescriptors); PowerPcAsm.Pair16Bit v = PowerPcAsm.make16bitValuePair((UInt32)tableAddr); // subi r30,r30,0x15 -> nop stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020d2b0), SeekOrigin.Begin); stream.Write(PowerPcAsm.nop()); // cmpwi r30,0x12 -> cmpwi r30,tableElementsCount stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020d2bc), SeekOrigin.Begin); stream.Write(PowerPcAsm.cmpwi(30, (short)(tableRowCount))); // li r0,0x15 -> nop stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020d334), SeekOrigin.Begin); stream.Write(PowerPcAsm.nop()); // mulli r5,r0,0x24 -> mulli r5,r0,0x04 stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020d340), SeekOrigin.Begin); stream.Write(PowerPcAsm.mulli(5, 0, 0x04)); // r4 <- 804363c8 -> r4 <- tableAddr stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020d344), SeekOrigin.Begin); stream.Write(PowerPcAsm.lis(4, v.upper16Bit)); stream.Seek(0x4, SeekOrigin.Current); stream.Write(PowerPcAsm.addi(4, 4, v.lower16Bit)); // mulli r0,r30,0x24 -> mulli r0,r30,0x04 stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020d350), SeekOrigin.Begin); stream.Write(PowerPcAsm.mulli(0, 30, 0x04)); // lwz r0,0x4(r4) -> lwz r0,0x4(r4) stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020d35c), SeekOrigin.Begin); stream.Write(PowerPcAsm.lwz(0, 0x0, 4)); // subi r31,r3,0x15 -> nop stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211f90), SeekOrigin.Begin); stream.Write(PowerPcAsm.nop()); // cmpwi r31,0x12 -> cmpwi r31,tableElementsCount stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211f9c), SeekOrigin.Begin); stream.Write(PowerPcAsm.cmpwi(31, (short)(tableRowCount))); // li r3,0x15 -> nop stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80212014), SeekOrigin.Begin); stream.Write(PowerPcAsm.nop()); // mulli r4,r3,0x24 -> mulli r4,r3,0x04 stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80212020), SeekOrigin.Begin); stream.Write(PowerPcAsm.mulli(4, 3, 0x04)); // r3 <- 804363c8 -> r3 <- tableAddr stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80212024), SeekOrigin.Begin); stream.Write(PowerPcAsm.lis(3, v.upper16Bit)); stream.Write(PowerPcAsm.addi(3, 3, v.lower16Bit)); // mulli r0,r31,0x24 -> mulli r0,r31,0x04 stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8021202c), SeekOrigin.Begin); stream.Write(PowerPcAsm.mulli(0, 31, 0x04)); // lwz r3,0x4(r3) -> lwz r3,0x0(r3) stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80212040), SeekOrigin.Begin); stream.Write(PowerPcAsm.lwz(3, 0x0, 3)); }
protected override void writeAsm(EndianBinaryWriter stream, AddressMapper addressMapper, List <MapDescriptor> mapDescriptors) { // this is the virtual address for the ForceVentureCardVariable: 0x804363c4 // instead of allocating space for the venture card variable with the free space manager, we use a fixed virtual address // so that this variable can be reused by other hacks outside of CSMM. var forceVentureCardVariable = addressMapper.toVersionAgnosticAddress((BSVAddr)0x804363c4); stream.Seek(addressMapper.toFileAddress(forceVentureCardVariable), SeekOrigin.Begin); // write zeroes to it stream.Write(new byte[4]); // --- Model --- // some examples: // 80087f88 = Property // 80088040 = Bank // 80088100 = Default // 80088050 = Take a break // 80088068 = Stockbroker // 80088048 = Arcade // 80088060 = Switch // 80088058 = Cannon stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80453330), SeekOrigin.Begin); stream.Write((UInt32)addressMapper.toVersionAgnosticAddress((BSVAddr)0x80088060)); // --- Texture --- var customTextureHandler = allocate(writeGetTextureForCustomSquareRoutine(15, 14), "GetTextureForCustomSquareRoutine"); var virtualPos = addressMapper.toVersionAgnosticAddress((BSVAddr)0x80086d98); stream.Seek(addressMapper.toFileAddress(virtualPos), SeekOrigin.Begin); // li r15,0x1 -> bl customTextureHandler stream.Write(PowerPcAsm.bl(virtualPos, customTextureHandler)); customTextureHandler = allocate(writeGetTextureForCustomSquareRoutine(31, 30), "GetTextureForCustomSquareRoutine2"); virtualPos = addressMapper.toVersionAgnosticAddress((BSVAddr)0x80087a24); stream.Seek(addressMapper.toFileAddress(virtualPos), SeekOrigin.Begin); // li r31,0x1 -> bl customTextureHandler stream.Write(PowerPcAsm.bl(virtualPos, customTextureHandler)); // --- Icon --- stream.Seek(addressMapper.toFileAddress((BSVAddr)0x804160c8), SeekOrigin.Begin); stream.Write((uint)addressMapper.toVersionAgnosticAddress((BSVAddr)0x80415ee0)); // pointer to the texture name (0x80415ee0 points to the string "p_mark_21" which is the switch icon texture stream.Write((uint)addressMapper.toVersionAgnosticAddress((BSVAddr)0x80415ee0)); // we need to write it twice: once for each design type (Mario and DragonQuest) // --- Name --- stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80475580), SeekOrigin.Begin); stream.Write(3336); // id of the message in ui_message.csv (3336 = "Switch square") // --- Description --- var customDescriptionRoutine = allocate(writeGetDescriptionForCustomSquareRoutine(addressMapper, (VAVAddr)0), "GetDescriptionForCustomSquareRoutine"); stream.Seek(addressMapper.toFileAddress(customDescriptionRoutine), SeekOrigin.Begin); stream.Write(writeGetDescriptionForCustomSquareRoutine(addressMapper, customDescriptionRoutine)); // re-write the routine again since now we know where it is located in the main dol virtualPos = addressMapper.toVersionAgnosticAddress((BSVAddr)0x800f8ce4); stream.Seek(addressMapper.toFileAddress(virtualPos), SeekOrigin.Begin); // bl Game::uitext::get_string -> bl customDescriptionRoutine stream.Write(PowerPcAsm.bl(virtualPos, customDescriptionRoutine)); virtualPos = addressMapper.toVersionAgnosticAddress((BSVAddr)0x800f8d6c); stream.Seek(addressMapper.toFileAddress(virtualPos), SeekOrigin.Begin); // bl Game::uitext::get_string -> bl customDescriptionRoutine stream.Write(PowerPcAsm.bl(virtualPos, customDescriptionRoutine)); virtualPos = addressMapper.toVersionAgnosticAddress((BSVAddr)0x800f8dd8); stream.Seek(addressMapper.toFileAddress(virtualPos), SeekOrigin.Begin); // bl Game::uitext::get_string -> bl customDescriptionRoutine stream.Write(PowerPcAsm.bl(virtualPos, customDescriptionRoutine)); virtualPos = addressMapper.toVersionAgnosticAddress((BSVAddr)0x800f8e5c); stream.Seek(addressMapper.toFileAddress(virtualPos), SeekOrigin.Begin); // bl Game::uitext::get_string -> bl customDescriptionRoutine stream.Write(PowerPcAsm.bl(virtualPos, customDescriptionRoutine)); virtualPos = addressMapper.toVersionAgnosticAddress((BSVAddr)0x800f8ee4); stream.Seek(addressMapper.toFileAddress(virtualPos), SeekOrigin.Begin); // bl Game::uitext::get_string -> bl customDescriptionRoutine stream.Write(PowerPcAsm.bl(virtualPos, customDescriptionRoutine)); virtualPos = addressMapper.toVersionAgnosticAddress((BSVAddr)0x800f8f4c); stream.Seek(addressMapper.toFileAddress(virtualPos), SeekOrigin.Begin); // bl Game::uitext::get_string -> bl customDescriptionRoutine stream.Write(PowerPcAsm.bl(virtualPos, customDescriptionRoutine)); // --- Behavior --- // the idea is that whenever someone stops at the event square, it sets our custom variable "ForceVentureCardVariable" to the id of the venture card and runs the Venture Card Mode (0x1c). // The custom variable is used to remember which venture card should be played the next time a venture card is executed. var procStopEventSquareRoutine = allocate(writeProcStopEventSquareRoutine(addressMapper, forceVentureCardVariable, (VAVAddr)0), "procStopEventSquareRoutine"); stream.Seek(addressMapper.toFileAddress(procStopEventSquareRoutine), SeekOrigin.Begin); stream.Write(writeProcStopEventSquareRoutine(addressMapper, forceVentureCardVariable, procStopEventSquareRoutine)); // re-write the routine again since now we know where it is located in the main dol stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80475838), SeekOrigin.Begin); stream.Write((UInt32)procStopEventSquareRoutine); // --- Hijack Venture Card Mode --- // We are hijacking the execute venture card mode (0x1f) to check if our custom variable "ForceVentureCardVariable" has been set to anything other than 0. // If it was, then setup that specific venture card to be executed. Also reset our custom variable "ForceVentureCardVariable" so that normal venture cards still work. var forceFetchFakeVentureCard = allocate(writeSubroutineForceFetchFakeVentureCard(forceVentureCardVariable), "forceFetchFakeVentureCard"); virtualPos = addressMapper.toVersionAgnosticAddress((BSVAddr)0x801b7f44); stream.Seek(addressMapper.toFileAddress(virtualPos), SeekOrigin.Begin); // li r4,-0x1 -> bl forceFetchFakeVentureCard stream.Write(PowerPcAsm.bl(virtualPos, forceFetchFakeVentureCard)); // li r8,0x3 -> nop stream.Seek(addressMapper.toFileAddress((BSVAddr)0x801b7f74), SeekOrigin.Begin); stream.Write(PowerPcAsm.nop()); }
protected override void writeAsm(EndianBinaryWriter stream, AddressMapper addressMapper, List <MapDescriptor> mapDescriptors) { var tableRowCount = mapDescriptors.Count; var tableAddr = writeTable(mapDescriptors); PowerPcAsm.Pair16Bit v = PowerPcAsm.make16bitValuePair((UInt32)tableAddr); // subi r30,r30,0x15 -> nop stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020d1c4), SeekOrigin.Begin); stream.Write(PowerPcAsm.nop()); // cmpwi r30,0x12 -> cmpwi r30,tableElementsCount stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020d1d0), SeekOrigin.Begin); stream.Write(PowerPcAsm.cmpwi(30, (short)(tableRowCount))); // li r0,0x15 -> nop stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020d248), SeekOrigin.Begin); stream.Write(PowerPcAsm.nop()); // mulli r4,r0,0x24 -> mulli r4,r0,0x04 stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020d254), SeekOrigin.Begin); stream.Write(PowerPcAsm.mulli(4, 0, 0x04)); // r3 <- 804363c8 -> r3 <- tableAddr stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020d258), SeekOrigin.Begin); stream.Write(PowerPcAsm.lis(3, v.upper16Bit)); stream.Write(PowerPcAsm.addi(3, 3, v.lower16Bit)); // mulli r0,r30,0x24 -> mulli r0,r30,0x04 stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020d260), SeekOrigin.Begin); stream.Write(PowerPcAsm.mulli(0, 30, 0x04)); // lwz r0,0x8(r3) -> lwz r0,0x0(r3) stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020d26c), SeekOrigin.Begin); stream.Write(PowerPcAsm.lwz(0, 0x0, 3)); // subi r31,r3,0x15 -> nop stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211ce4), SeekOrigin.Begin); stream.Write(PowerPcAsm.nop()); // cmpwi r31,0x12 -> cmpwi r31,tableElementsCount stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211cf0), SeekOrigin.Begin); stream.Write(PowerPcAsm.cmpwi(31, (short)(tableRowCount))); // li r3,0x15 -> nop stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211d68), SeekOrigin.Begin); stream.Write(PowerPcAsm.nop()); // mulli r4,r3,0x24 -> mulli r4,r3,0x04 stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211d74), SeekOrigin.Begin); stream.Write(PowerPcAsm.mulli(4, 3, 0x04)); // r3 <- 804363c8 -> r3 <- tableAddr stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211d78), SeekOrigin.Begin); stream.Write(PowerPcAsm.lis(3, v.upper16Bit)); stream.Write(PowerPcAsm.addi(3, 3, v.lower16Bit)); // mulli r0,r31,0x24 -> mulli r0,r31,0x04 stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211d80), SeekOrigin.Begin); stream.Write(PowerPcAsm.mulli(0, 31, 0x04)); // lwz r3,0x8(r3) -> lwz r3,0x0(r3) stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211d94), SeekOrigin.Begin); stream.Write(PowerPcAsm.lwz(3, 0x0, 3)); }
protected override void writeAsm(EndianBinaryWriter stream, AddressMapper addressMapper, List <MapDescriptor> mapDescriptors) { var tableRowCount = mapDescriptors.Count; var tableAddr = writeTable(mapDescriptors); PowerPcAsm.Pair16Bit v = PowerPcAsm.make16bitValuePair((UInt32)tableAddr); // subi r3,r3,0x15 -> nop stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020cec8), SeekOrigin.Begin); stream.Write(PowerPcAsm.nop()); // cmpwi r3,0x12 -> cmpwi r3,tableElementsCount stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020ced4), SeekOrigin.Begin); stream.Write(PowerPcAsm.cmpwi(3, (short)(tableRowCount))); // li r0,0x15 -> nop stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020cf68), SeekOrigin.Begin); stream.Write(PowerPcAsm.nop()); // mulli r0,r3,0x24 -> mulli r0,r3,0x0c stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020cee0), SeekOrigin.Begin); stream.Write(PowerPcAsm.mulli(0, 3, 0x0c)); // r3 <- 804363c8 -> r3 <- tableAddr stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020cee4), SeekOrigin.Begin); stream.Write(PowerPcAsm.lis(3, v.upper16Bit)); stream.Seek(0x4, SeekOrigin.Current); stream.Write(PowerPcAsm.addi(3, 3, v.lower16Bit)); // mulli r3,r0,0x24 -> mulli r3,r0,0x0c stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020cf74), SeekOrigin.Begin); stream.Write(PowerPcAsm.mulli(3, 0, 0x0c)); // lwz r0,0xc(r3) -> lwz r0,0x0(r3) stream.Seek(addressMapper.toFileAddress((BSVAddr)0x8020cf8c), SeekOrigin.Begin); stream.Write(PowerPcAsm.lwz(0, 0x0, 3)); // subi r4,r4,0x15 -> nop stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211af4), SeekOrigin.Begin); stream.Write(PowerPcAsm.nop()); // cmpwi r4,0x12 -> cmpwi r4,tableElementsCount stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211b00), SeekOrigin.Begin); stream.Write(PowerPcAsm.cmpwi(4, (short)(tableRowCount))); // li r3,0x15 -> nop stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211b94), SeekOrigin.Begin); stream.Write(PowerPcAsm.nop()); // mulli r0,r4,0x24 -> mulli r0,r4,0x0c stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211b0c), SeekOrigin.Begin); stream.Write(PowerPcAsm.mulli(0, 4, 0x0c)); // r4 <- 804363c8 -> r4 <- tableAddr stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211b10), SeekOrigin.Begin); stream.Write(PowerPcAsm.lis(4, v.upper16Bit)); stream.Seek(0x4, SeekOrigin.Current); stream.Write(PowerPcAsm.addi(4, 4, v.lower16Bit)); // mulli r3,r3,0x24 -> mulli r3,r3,0x0c stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211ba0), SeekOrigin.Begin); stream.Write(PowerPcAsm.mulli(3, 3, 0x0c)); // lwz r0,0xc(r3) -> lwz r0,0x0(r3) stream.Seek(addressMapper.toFileAddress((BSVAddr)0x80211bb8), SeekOrigin.Begin); stream.Write(PowerPcAsm.lwz(0, 0x0, 3)); }