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