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); }
private List <UInt32> writeSubroutineSkipMapUnlockCheck(AddressMapper addressMapper, VAVAddr entryAddr, VAVAddr returnContinueAddr, VAVAddr returnSkipMapUnlockedCheck) { // precondition: r26 is mapid // r3 is unused // postcondition: r3 is mapid var asm = new List <UInt32>(); asm.Add(PowerPcAsm.or(3, 26, 26)); // r3 <- mapid asm.Add(PowerPcAsm.cmpwi(3, -1)); // mapid == -1 ? asm.Add(PowerPcAsm.beq(entryAddr, asm.Count, returnSkipMapUnlockedCheck)); // goto skipMapUnlockedCheck asm.Add(PowerPcAsm.b(entryAddr, asm.Count, returnContinueAddr)); // else goto returnContinueAddr 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 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 hijackAddr = addressMapper.toVersionAgnosticAddress((BSVAddr)0x802bb120); var returnAddr = addressMapper.toVersionAgnosticAddress((BSVAddr)0x802bb124); var uploadSimulatedButtonPress = allocate(writeUploadSimulatedButtonPress(addressMapper, (VAVAddr)0, returnAddr), "UploadSimulatedButtonPress"); stream.Seek(addressMapper.toFileAddress(uploadSimulatedButtonPress), SeekOrigin.Begin); stream.Write(writeUploadSimulatedButtonPress(addressMapper, uploadSimulatedButtonPress, returnAddr)); // re-write the routine again since now we know where it is located in the main dol // lwz r0,0x4(r3) -> b uploadSimulatedButtonPress stream.Seek(addressMapper.toFileAddress(hijackAddr), SeekOrigin.Begin); stream.Write(PowerPcAsm.b(hijackAddr, uploadSimulatedButtonPress)); }