public static void PatchDisableMouseCapture(Stream bin, Sen1ExecutablePatchState state) { bool jp = state.IsJp; // change function that captures the mouse cursor to not do that bin.Position = state.Mapper.MapRamToRom(jp ? 0x5de6c6 : 0x5df536); bin.WriteUInt8(0xeb); // jz -> jmp // change function that handles camera movement to not react to mouse movement // and not to fall back to WASD camera movement either (legacy code...?) using (var branch = new BranchHelper4Byte(bin, state.Mapper)) { bin.Position = state.Mapper.MapRamToRom(jp ? 0x4464e5 : 0x446635); branch.WriteJump5Byte(0xe9); bin.WriteUInt8(0x90); // nop branch.SetTarget(jp ? 0x44667au : 0x4467cau); } // there's a third function at 0x53c766 that seems involved here, but leaving it alone seems to work just fine... // remove call to ShowCursor(0) bin.Position = state.Mapper.MapRamToRom(jp ? 0x7be0ea : 0x7bf9ba); for (int i = 0; i < 8; ++i) { bin.WriteUInt8(0x90); // nop } // skip mouse movement processing function or something like that? bin.Position = state.Mapper.MapRamToRom(jp ? 0x446e3a : 0x446f8a); bin.WriteUInt8(0x90); // nop bin.WriteUInt8(0x90); // nop bin.WriteUInt8(0x90); // nop bin.WriteUInt8(0x90); // nop bin.WriteUInt8(0x90); // nop }
public static void PatchThorMasterQuartzString(Stream binary, Sen1ExecutablePatchState patchInfo) { binary.Position = patchInfo.RomAddressThorMasterQuartzTextureIdTypo; long p = binary.Position; byte[] tmp = binary.ReadUInt8Array(4); binary.Position = p; binary.Write(tmp, 1, 3); }
public static void PatchJumpR2NotebookSettings(Stream binary, Sen1ExecutablePatchState patchInfo) { // note: 0x6de04c might be where the game swaps the two L2/R2 options if they match after setting binary.Position = (long)patchInfo.Mapper.MapRamToRom((ulong)patchInfo.AddressJumpR2NotebookSettings); binary.WriteUInt8(0x90); // nop binary.WriteUInt8(0x90); // nop binary.WriteUInt8(0x90); // nop binary.WriteUInt8(0x90); // nop binary.WriteUInt8(0x90); // nop binary.WriteUInt8(0x90); // nop }
public static void PatchDisablePauseOnFocusLoss(Stream bin, Sen1ExecutablePatchState state) { bool jp = state.IsJp; // 0x444AA0 -> game active setter // 0x444AF0 -> game active getter // don't silence audio output when unfocused using (var branch = new BranchHelper4Byte(bin, state.Mapper)) { bin.Position = state.Mapper.MapRamToRom(jp ? 0x447d6a : 0x447eba); branch.WriteJump5Byte(0xe9); branch.SetTarget(jp ? 0x447da6u : 0x447ef6u); } // still run main game loop when unfocused bin.Position = state.Mapper.MapRamToRom(jp ? 0x441c00 : 0x441d50); bin.WriteUInt8(0x90); // nop bin.WriteUInt8(0x90); // nop bin.WriteUInt8(0x90); // nop bin.WriteUInt8(0x90); // nop bin.WriteUInt8(0x90); // nop bin.WriteUInt8(0x90); // nop // avoid processing mouse clicks when unfocused // (this previously happened only implicitly because the game didn't run...) // carve out some now-unused code space uint codespaceStart = jp ? 0x447d6fu : 0x447ebfu; uint codespaceEnd = jp ? 0x447da4u : 0x447ef4u; var codespace = new RegionHelper(codespaceStart, codespaceEnd - codespaceStart, "Pause on Focus Loss: Codespace Region"); bin.Position = state.Mapper.MapRamToRom(codespace.Address); for (uint i = 0; i < codespace.Remaining; ++i) { bin.WriteUInt8(0xcc); // int 3 } // and assemble some logic to skip mouse button processing when unfocused using (var jump_to_codespace = new BranchHelper4Byte(bin, state.Mapper)) using (var back_to_function = new BranchHelper4Byte(bin, state.Mapper)) using (var skip_processing = new BranchHelper1Byte(bin, state.Mapper)) { var be = EndianUtils.Endianness.BigEndian; back_to_function.SetTarget(jp ? 0x479ad8u : 0x47b348u); bin.Position = state.Mapper.MapRamToRom(jp ? 0x479ad4u : 0x47b344); ulong GetKeyStateAddress = bin.ReadUInt32(be); ulong GameStateAddress = (jp ? 0x1361c28u : 0x01363fc8u).ToEndian(be); bin.Position = state.Mapper.MapRamToRom(jp ? 0x479ad1 : 0x47b341); jump_to_codespace.WriteJump5Byte(0xe9); // jmp jump_to_codespace bin.WriteUInt8(0x90); // nop bin.WriteUInt8(0x90); // nop bin.Position = state.Mapper.MapRamToRom(codespace.Address); jump_to_codespace.SetTarget(codespace.Address); bin.WriteUInt48(0x8b0d00000000u | GameStateAddress, be); // mov ecx,[static_address_that_holds_game_state] bin.WriteUInt16(0x85c9, be); // test ecx,ecx skip_processing.WriteJump(0x74); // jz skip_processing bin.WriteUInt48(0x8a89b8070000, be); // mov cl,byte ptr[ecx+7b8h] ; cl now holds 0 if unfocused, 1 if focused bin.WriteUInt16(0x84c9, be); // test cl,cl skip_processing.WriteJump(0x74); // jz skip_processing bin.WriteUInt48(0x8b0d00000000u | GetKeyStateAddress, be); // mov ecx,[USER32.DLL:GetKeyState] bin.WriteUInt16(0x85c9, be); // test ecx,ecx skip_processing.WriteJump(0x74); // jz skip_processing bin.WriteUInt8(0x50); // push eax bin.WriteUInt16(0xffd1, be); // call ecx back_to_function.WriteJump5Byte(0xe9); // jmp back_to_function skip_processing.SetTarget(state.Mapper.MapRomToRam((ulong)bin.Position)); bin.WriteUInt16(0x33c0, be); // xor eax,eax back_to_function.WriteJump5Byte(0xe9); // jmp back_to_function codespace.TakeToAddress(state.Mapper.MapRomToRam(bin.Position), "Pause on Focus Loss: don't process mouse clicks"); } }
public static void PatchJumpR2NotebookOpen(Stream binary, Sen1ExecutablePatchState patchInfo) { binary.Position = (long)patchInfo.Mapper.MapRamToRom((ulong)patchInfo.AddressJumpR2NotebookOpen); binary.WriteUInt8(0x90); // nop binary.WriteUInt8(0x90); // nop }
public static void PatchJumpBattleResultsAutoSkip(Stream binary, Sen1ExecutablePatchState patchInfo) { binary.Position = (long)patchInfo.Mapper.MapRamToRom((ulong)patchInfo.AddressJumpBattleResultsAutoSkip); binary.WriteUInt40(0xe981010000, EndianUtils.Endianness.BigEndian); // jmp binary.WriteUInt8(0x90); // nop }
public static void PatchJumpBattleAnimationAutoSkip(Stream binary, Sen1ExecutablePatchState patchInfo) { binary.Position = (long)patchInfo.Mapper.MapRamToRom((ulong)patchInfo.AddressJumpBattleAnimationAutoSkip); binary.WriteUInt16(0xeb07, EndianUtils.Endianness.BigEndian); // jmp }
public static void PatchButtonBattleResultsAutoSkip(Stream binary, Sen1ExecutablePatchState patchInfo, byte button) { binary.Position = (long)patchInfo.Mapper.MapRamToRom((ulong)patchInfo.AddressButtonBattleResultsAutoSkip); binary.WriteUInt8(button); }
// button IDs: // 0x0 = Square // 0x1 = Cross // 0x2 = Circle // 0x3 = Triangle // 0x4 = L1 // 0x5 = R1 // 0x6 = L2 // 0x7 = R2 // 0x8 = Select // 0x9 = Start // 0xA = L3 // 0xB = R3 // 0xC = D-Pad Up // 0xD = D-Pad Right // 0xE = D-Pad Down // 0xF = D-Pad Left public static void PatchButtonTurboMode(Stream binary, Sen1ExecutablePatchState patchInfo, byte button) { binary.Position = (long)patchInfo.Mapper.MapRamToRom((ulong)patchInfo.AddressButtonTurboMode); binary.WriteUInt8(button); }
public static void PatchLanguageAppropriateVoiceTables(Stream binary, Sen1ExecutablePatchState state) { state.InitCodeSpaceIfNeeded(binary); var mapper = state.Mapper; var regionStrings = state.RegionScriptCompilerFunctionStrings; var regionCode = state.RegionScriptCompilerFunction; bool jp = state.IsJp; using (BranchHelper4Byte jump_from_function = new BranchHelper4Byte(binary, mapper)) using (BranchHelper4Byte back_to_function = new BranchHelper4Byte(binary, mapper)) using (BranchHelper4Byte get_pc_config = new BranchHelper4Byte(binary, mapper)) using (BranchHelper4Byte pc_config__get_voice_language = new BranchHelper4Byte(binary, mapper)) using (BranchHelper1Byte depends_on_voice_lang = new BranchHelper1Byte(binary, mapper)) using (BranchHelper1Byte exit_inject = new BranchHelper1Byte(binary, mapper)) using (BranchHelper1Byte use_english_string = new BranchHelper1Byte(binary, mapper)) using (BranchHelper1Byte use_text_lang_string = new BranchHelper1Byte(binary, mapper)) { EndianUtils.Endianness be = EndianUtils.Endianness.BigEndian; EndianUtils.Endianness le = EndianUtils.Endianness.LittleEndian; Stream _ = binary; get_pc_config.SetTarget(jp ? 0x7bdef0u : 0x7bf7c0u); pc_config__get_voice_language.SetTarget(jp ? 0x7bd3f0u : 0x7becc0u); // inject into the asset-from-text-subfolder loader function long address_of_inject = jp ? 0x56f6d8 : 0x5708d8; _.Position = mapper.MapRamToRom(address_of_inject); ulong push_local_formatting_string = _.PeekUInt40(be); uint address_local_formatting_string = ((uint)(push_local_formatting_string & 0xffffffffu)).SwapEndian(); jump_from_function.SetTarget(regionCode.Address); jump_from_function.WriteJump5Byte(0xe9); back_to_function.SetTarget(mapper.MapRomToRam((ulong)_.Position)); ulong push_en_formatting_string; ulong push_jp_formatting_string; uint t_vctiming_address; uint t_voice_address; if (jp) { push_jp_formatting_string = push_local_formatting_string; t_vctiming_address = 0xb41c68; t_voice_address = 0xb41bdc; // english formatting string doesn't exist in the japanese executable, generate and inject it byte[] formatting_string_jp = _.ReadBytesFromLocationAndReset(mapper.MapRamToRom(address_local_formatting_string), 0x13); byte[] extra = _.ReadBytesFromLocationAndReset(0x73fd32, 0x3); byte[] formatting_string_en = new byte[formatting_string_jp.Length + 3]; ArrayUtils.CopyByteArrayPart(formatting_string_jp, 0, formatting_string_en, 0, 0xb); ArrayUtils.CopyByteArrayPart(extra, 0, formatting_string_en, 0xb, 3); ArrayUtils.CopyByteArrayPart(formatting_string_jp, 0xb, formatting_string_en, 0xe, 0x8); uint nonlocal_string_address = regionStrings.Address; _.Position = mapper.MapRamToRom(regionStrings.Address); _.Write(formatting_string_en); regionStrings.TakeToAddress(mapper.MapRomToRam(_.Position), "Voice Tables: EN Formatting String"); push_en_formatting_string = 0x6800000000 | (ulong)(nonlocal_string_address.SwapEndian()); } else { push_en_formatting_string = push_local_formatting_string; t_vctiming_address = 0xb43f30; t_voice_address = 0xb43ea4; // japanese formatting string doesn't exist in the english executable, generate and inject it byte[] formatting_string_en = _.ReadBytesFromLocationAndReset(mapper.MapRamToRom(address_local_formatting_string), 0x16); byte[] formatting_string_jp = new byte[formatting_string_en.Length - 3]; ArrayUtils.CopyByteArrayPart(formatting_string_en, 0, formatting_string_jp, 0, 0xb); ArrayUtils.CopyByteArrayPart(formatting_string_en, 0xe, formatting_string_jp, 0xb, 0x8); uint nonlocal_string_address = regionStrings.Address; _.Position = mapper.MapRamToRom(regionStrings.Address); _.Write(formatting_string_jp); regionStrings.TakeToAddress(mapper.MapRomToRam(_.Position), "Voice Tables: JP Formatting String"); push_jp_formatting_string = 0x6800000000 | (ulong)(nonlocal_string_address.SwapEndian()); } // assemble logic to select the voice-language-matching voice tables _.Position = mapper.MapRamToRom(regionCode.Address); _.WriteUInt16(0x81ff, be); // cmp edi,(t_vctiming) _.WriteUInt32(t_vctiming_address, le); depends_on_voice_lang.WriteJump(0x74); // je depends_on_voice_lang _.WriteUInt16(0x81ff, be); // cmp edi,(t_voice) _.WriteUInt32(t_voice_address, le); depends_on_voice_lang.WriteJump(0x74); // je depends_on_voice_lang use_text_lang_string.WriteJump(0xeb); // jmp use_text_lang_string depends_on_voice_lang.SetTarget(mapper.MapRomToRam((ulong)_.Position)); get_pc_config.WriteJump5Byte(0xe8); // call get_pc_config _.WriteUInt16(0x8bc8, be); // mov ecx,eax pc_config__get_voice_language.WriteJump5Byte(0xe8); // call pc_config__get_voice_language _.WriteUInt16(0x84c0, be); // test al,al use_english_string.WriteJump(0x74); // jz use_english_string if (jp) { use_text_lang_string.SetTarget(mapper.MapRomToRam((ulong)_.Position)); } _.WriteUInt40(push_jp_formatting_string, be); // push (jp_formatting_string) exit_inject.WriteJump(0xeb); // jmp exit_inject use_english_string.SetTarget(mapper.MapRomToRam((ulong)_.Position)); if (!jp) { use_text_lang_string.SetTarget(mapper.MapRomToRam((ulong)_.Position)); } _.WriteUInt40(push_en_formatting_string, be); // push (en_formatting_string) exit_inject.SetTarget(mapper.MapRomToRam((ulong)_.Position)); back_to_function.WriteJump5Byte(0xe9); // jmp back_to_function regionCode.TakeToAddress(mapper.MapRomToRam(_.Position), "Voice Tables: Formatting String Select Code"); } }