From ac17f6a8e2d8f110ce8e7261e44835b927ad709c Mon Sep 17 00:00:00 2001 From: tkgstrator Date: Wed, 1 Jul 2026 11:14:25 +0000 Subject: [PATCH 1/2] feat(recipes): port KiouEditor sites to 1.0.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the 17 non-AFK KiouEditor hook sites to recipes/v1_0_2.py so a KiouEditor build targeting KIOU 1.0.2 can chinlan-patch them alongside the existing KiouForge base + kifu observer + HeaderProvider entries. RVAs verified against assets/1.0.2/dump.cs on 2026-07-01; prologue bytes captured from assets/1.0.2/Kiou-1.0.2.ipa UnityFramework. Ran python -m tools.verify_sites --recipe recipes --index assets/1.0.2/dump.cs.index.json --ipa assets/1.0.2/Kiou-1.0.2.ipa: 32 / 33 sites pass (the pre-existing HeaderProvider verification skip is unrelated — index-lookup misses the fully-qualified type name). 0x5C3C29C SyncItemListReply.InternalMergeFrom 0x5C458C4 UpdateCollectionPresetReply.InternalMergeFrom 0x5CACEF8 GameServiceClient.SelectCharacterAsync 0x5C2C034 SelectCharacterReply.InternalMergeFrom 0x5B51C3C ShogiMatchingPlayerStatus.InternalMergeFrom 0x5C06590 GetShogiHistoryDetailListReply.InternalMergeFrom 0x5C05FF0 GetShogiHistoryDetailListReply.get_IsPremiumUser 0x585E000 KifuDetailModel.IsPremiumUser 0x582E614 CharacterVoicePlayer.SatisfiesRule 0x584DB64 CharacterVoiceScrollerCellModel.get_IsLocked 0x597E608 BeginnerSupportEvaluator.ctor 0x5980890 BeginnerSupportEvaluator.EnsureInitializedLocked 0x5942AA0 ResolvedBeginnerSupport.get_Enabled 0x5942AC0 ResolvedBeginnerSupport.get_Depth 0x5AA4054 HomeUtilityPresenter.ctor 0x5DD7F54 UIButtonBase.OnPointerClick 0x5DD2874 TitleScene+d__10.MoveNext GAME_ORCHESTRATOR_IS_AFK is intentionally NOT ported to a CAVE_ENTRY on 1.0.2. KiouForge's build already patches that site inline via AFK_SITE / AFK_ORIG_8 to force IsAfkEnabled -> false, and a KiouEditor 1.0.2 build that wants feature-flag-gated AFK toggling would crash on first call unless KiouForge simultaneously publishes the cave slot (BLR to null slot pointer). Migrating 1.0.2 AFK to a cave will land in a follow-up PR that coordinates with a KiouForge update. Also drops tools/__init__.py — PEP-420 namespace packages let python -m tools.check_recipes resolve without it, and its presence was shadowing the sibling shared/tools/ package on consumer builds. Verified: python -m tools.check_recipes still passes locally (HOOK_IDS=34 ENTRY_SLOT_INDEX=29 sites=60). Not addressed in this PR: - C-side catalog RVAs for the KiouEditor sites in KIOUHook.h / KIOUHook.m are still pinned to 1.0.1 (via the KIOU_HOOK_RVA_* macros). On chinlan builds this is inert (the dispatcher's g_inject_entry table drives hook dispatch, not the catalog RVAs), so the recipe port here is sufficient for the primary consumer (KiouEditor chinlan). Direct-ABI callouts from Hook/AssistTune.m and Hook/FriendUnhide.m (SetHashSize / GameObject.GetComponent / RectTransformUtility.WorldToScreenPoint) WILL land at wrong addresses on a 1.0.2 build until the catalog gains version-aware dispatch — track that as a follow-up. Co-Authored-By: Claude Opus 4.7 --- recipes/v1_0_2.py | 30 ++++++++++++++++++++++++++++++ tools/__init__.py | 0 2 files changed, 30 insertions(+) delete mode 100644 tools/__init__.py diff --git a/recipes/v1_0_2.py b/recipes/v1_0_2.py index 9552d24..eb4d2d9 100644 --- a/recipes/v1_0_2.py +++ b/recipes/v1_0_2.py @@ -50,5 +50,35 @@ # HttpMessageInvoker.SendAsync / Yaha borrow path that crashes when # the request or HttpHeaders internal dictionary is touched. (0x5BD9EE8, "f657bda9", "KIOU_HOOK_ID_HEADER_PROVIDER_SET_OR_UPDATE_HEADER", CAVE_ENTRY, "Project.Network.HeaderProvider.SetOrUpdateHeader"), + + # --- KiouEditor entry caves (CAVE_ENTRY, 1.0.2 port of the 1.0.1 sites) --- + # RVAs verified against assets/1.0.2/dump.cs on 2026-07-01. Prologues + # captured from assets/1.0.2/Kiou-1.0.2.ipa UnityFramework. None are + # PC-relative; each first-4-bytes can be relocated verbatim into the + # cave tail. + # + # GAME_ORCHESTRATOR_IS_AFK is intentionally NOT ported to a CAVE_ENTRY + # on 1.0.2: KiouForge's build already patches that address inline via + # AFK_SITE / AFK_ORIG_8, and a KiouEditor build for 1.0.2 that wants + # feature-flag-gated AFK toggling would need KiouForge to publish the + # cave slot to avoid a BLR-to-null cave-tail crash. Keep the inline + # patch until a consumer requires the cave path on 1.0.2. + (0x5C3C29C, "fc6fbaa9", "KIOU_HOOK_ID_SYNC_ITEM_LIST_MERGE", CAVE_ENTRY, "SyncItemListReply.InternalMergeFrom"), + (0x5C458C4, "fa67bba9", "KIOU_HOOK_ID_COLLECTION_PRESET_MERGE", CAVE_ENTRY, "UpdateCollectionPresetReply.InternalMergeFrom"), + (0x5CACEF8, "ffc302d1", "KIOU_HOOK_ID_SELECT_CHAR_ASYNC", CAVE_ENTRY, "SelectCharacterAsync"), + (0x5C2C034, "fc6fbaa9", "KIOU_HOOK_ID_SELECT_CHAR_REPLY_MERGE", CAVE_ENTRY, "SelectCharacterReply.InternalMergeFrom"), + (0x5B51C3C, "fc6fbaa9", "KIOU_HOOK_ID_MATCHING_PLAYER_MERGE", CAVE_ENTRY, "ShogiMatchingPlayerStatus.InternalMergeFrom"), + (0x5C06590, "fc6fbaa9", "KIOU_HOOK_ID_HISTORY_DETAIL_MERGE", CAVE_ENTRY, "GetShogiHistoryDetailListReply.InternalMergeFrom"), + (0x5C05FF0, "00804039", "KIOU_HOOK_ID_HISTORY_GET_PREMIUM", CAVE_ENTRY, "GetShogiHistoryDetailListReply.get_IsPremiumUser"), + (0x585E000, "00004139", "KIOU_HOOK_ID_KIFU_DETAIL_IS_PREMIUM", CAVE_ENTRY, "KifuDetailModel.IsPremiumUser"), + (0x582E614, "e80300aa", "KIOU_HOOK_ID_VOICE_PLAYER_SATISFIES", CAVE_ENTRY, "CharacterVoicePlayer.SatisfiesRule"), + (0x584DB64, "00704039", "KIOU_HOOK_ID_VOICE_CELL_GET_IS_LOCKED", CAVE_ENTRY, "CharacterVoiceScrollerCellModel.get_IsLocked"), + (0x597E608, "f85fbca9", "KIOU_HOOK_ID_BSE_CTOR", CAVE_ENTRY, "BeginnerSupportEvaluator.ctor"), + (0x5980890, "f657bda9", "KIOU_HOOK_ID_BSE_ENSURE_INITIALIZED", CAVE_ENTRY, "BeginnerSupportEvaluator.EnsureInitializedLocked"), + (0x5942AA0, "00404039", "KIOU_HOOK_ID_RBSUPPORT_GET_ENABLED", CAVE_ENTRY, "ResolvedBeginnerSupport.get_Enabled"), + (0x5942AC0, "002040b9", "KIOU_HOOK_ID_RBSUPPORT_GET_DEPTH", CAVE_ENTRY, "ResolvedBeginnerSupport.get_Depth"), + (0x5AA4054, "fc6fbaa9", "KIOU_HOOK_ID_HOME_UTILITY_PRESENTER_CTOR", CAVE_ENTRY, "HomeUtilityPresenter.ctor"), + (0x5DD7F54, "f44fbea9", "KIOU_HOOK_ID_UIBUTTONBASE_ONPOINTERCLICK", CAVE_ENTRY, "UIButtonBase.OnPointerClick"), + (0x5DD2874, "ff0303d1", "KIOU_HOOK_ID_TITLE_SCENE_MOVENEXT", CAVE_ENTRY, "TitleScene+d__10.MoveNext"), ] # fmt: on diff --git a/tools/__init__.py b/tools/__init__.py deleted file mode 100644 index e69de29..0000000 From 3e9f2f77faa4f2cc5dcafc65ffbb06b558773dda Mon Sep 17 00:00:00 2001 From: tkgstrator Date: Wed, 1 Jul 2026 11:19:46 +0000 Subject: [PATCH 2/2] feat(hooks): migrate 1.0.2 AFK to CAVE_ENTRY + always-false installer Retires the AFK_SITE inline patch on 1.0.2 in favour of a CAVE_ENTRY so both 1.0.1 and 1.0.2 route GameOrchestrator.IsAfkEnabled through the same hook-body path. Recipe changes (recipes/v1_0_2.py): - AFK_SITE / AFK_ORIG_8 -> None / "" (drops the inline `MOVZ W0, #0; RET` patch) - GAME_ORCHESTRATOR_IS_AFK row added to SITES (CAVE_ENTRY at 0x594A034, prologue f44fbea9) To keep the "AFK is always off" behaviour KiouForge relied on without pulling in the KIOUEditor feature-flag surface, KIOU-Hook now exposes a thin convenience installer: void KIOUAfkDisableAlwaysFalseInstall(uintptr_t unityBase); It publishes a static `return false` hook body into the cave's entry slot. One call from a consumer's Tweak.m is enough to restore the historic behaviour. The feature-flag-gated variant (KIOUEditorInstallAfkDisableHook in Hook/AfkDisable.m) is unchanged. **Coordinated rollout required.** Once this lands on main, any consumer that chinlan-patches with the updated recipe will get the AFK cave inserted. Consumers MUST publish the AFK cave slot before their patched IPA runs, or the first IsAfkEnabled call BLR-jumps to a null slot pointer and crashes. KiouForge's companion change (call `KIOUAfkDisableAlwaysFalseInstall` from its Tweak.m + submodule bump) is expected to land in the same window as this PR. Verified with: python -m tools.check_recipes -> 61 sites (was 60) TARGET_VERSION=1.0.2 python -m tools.verify_sites ... -> 33/34 rows pass (the pre-existing HeaderProvider miss is the lone FAIL, unrelated). Co-Authored-By: Claude Opus 4.7 --- KIOUHook.h | 11 +++++++++++ KIOUHook.m | 23 +++++++++++++++++++++++ recipes/v1_0_2.py | 18 ++++++++---------- 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/KIOUHook.h b/KIOUHook.h index 292e1d1..c54547d 100644 --- a/KIOUHook.h +++ b/KIOUHook.h @@ -307,6 +307,17 @@ uintptr_t KIOUHookSiteAddr(const char *hook_name, uintptr_t unityBase); void KIOUInstallAccountObserveHook(uintptr_t unityBase); void KIOUInstallGrpcLoggingHook(uintptr_t unityBase); +// Publish a hook body that unconditionally returns false for +// GameOrchestrator.IsAfkEnabled. This is the "AFK is always off" +// convenience installer for consumers that want the historic +// inline-patch behaviour without wiring the KIOUEditor feature-flag +// surface. Cave slot is populated after this call, so any consumer +// building against a recipe that carries the AFK site as +// CAVE_ENTRY (v1_0_1 and later v1_0_2) can call this once at +// startup and be done. See Hook/AfkDisable.m for the +// feature-flag-gated variant (KIOUEditorInstallAfkDisableHook). +void KIOUAfkDisableAlwaysFalseInstall(uintptr_t unityBase); + // Drive BackToTitleSequence.RunAsync — called by consumer settings UIs after // the user confirms an account switch so KIOU re-runs AccountExists → Login // with the pending_device_id substitution in effect (no app relaunch needed). diff --git a/KIOUHook.m b/KIOUHook.m index 4c0181c..a295ec5 100644 --- a/KIOUHook.m +++ b/KIOUHook.m @@ -159,3 +159,26 @@ uintptr_t KIOUHookSiteAddr(const char *name, uintptr_t unityBase) { return orig; #endif } + +// --------------------------------------------------------------------------- +// KIOUAfkDisableAlwaysFalseInstall — publishes an unconditional +// GameOrchestrator.IsAfkEnabled -> false stub into the AFK cave slot. +// +// This is the drop-in replacement for the retired +// (AFK_SITE / AFK_ORIG_8) inline patch on 1.0.2: consumers that want +// the historic "AFK is always off" behaviour without any feature-flag +// wiring call this once from their Tweak.m and are done. The +// KIOUEditor feature-flag-gated variant +// (KIOUEditorInstallAfkDisableHook in Hook/AfkDisable.m) is a separate +// module that swaps in on top of this. +// --------------------------------------------------------------------------- + +static bool kiou_afk_always_false(void *self) { + (void)self; + return false; +} + +void KIOUAfkDisableAlwaysFalseInstall(uintptr_t unityBase) { + KIOUHookInstall(KIOU_HOOK_NAME_GAME_ORCHESTRATOR_IS_AFK, + (void *)kiou_afk_always_false, unityBase); +} diff --git a/recipes/v1_0_2.py b/recipes/v1_0_2.py index eb4d2d9..3563bc4 100644 --- a/recipes/v1_0_2.py +++ b/recipes/v1_0_2.py @@ -20,9 +20,13 @@ ENTRY_SLOT_BASE_RVA = 0x091E91B8 ZERO_REGION_END_RVA = 0x091F5978 -# GameOrchestrator.IsAfkEnabled — inline-patched to return false. -AFK_SITE = 0x594A034 -AFK_ORIG_8 = "f44fbea9fd7b01a9" +# GameOrchestrator.IsAfkEnabled is now handled via CAVE_ENTRY (see SITES +# below). The inline patch is retired so both 1.0.1 and 1.0.2 route AFK +# through the same hook-body path. Consumers that want the historic +# "always disabled" behaviour without wiring KIOUEditorFeatureEnabled +# can call KIOUAfkDisableAlwaysFalseInstall(unityBase) — see KIOUHook.h. +AFK_SITE = None +AFK_ORIG_8 = "" # fmt: off SITES = [ @@ -56,13 +60,6 @@ # captured from assets/1.0.2/Kiou-1.0.2.ipa UnityFramework. None are # PC-relative; each first-4-bytes can be relocated verbatim into the # cave tail. - # - # GAME_ORCHESTRATOR_IS_AFK is intentionally NOT ported to a CAVE_ENTRY - # on 1.0.2: KiouForge's build already patches that address inline via - # AFK_SITE / AFK_ORIG_8, and a KiouEditor build for 1.0.2 that wants - # feature-flag-gated AFK toggling would need KiouForge to publish the - # cave slot to avoid a BLR-to-null cave-tail crash. Keep the inline - # patch until a consumer requires the cave path on 1.0.2. (0x5C3C29C, "fc6fbaa9", "KIOU_HOOK_ID_SYNC_ITEM_LIST_MERGE", CAVE_ENTRY, "SyncItemListReply.InternalMergeFrom"), (0x5C458C4, "fa67bba9", "KIOU_HOOK_ID_COLLECTION_PRESET_MERGE", CAVE_ENTRY, "UpdateCollectionPresetReply.InternalMergeFrom"), (0x5CACEF8, "ffc302d1", "KIOU_HOOK_ID_SELECT_CHAR_ASYNC", CAVE_ENTRY, "SelectCharacterAsync"), @@ -80,5 +77,6 @@ (0x5AA4054, "fc6fbaa9", "KIOU_HOOK_ID_HOME_UTILITY_PRESENTER_CTOR", CAVE_ENTRY, "HomeUtilityPresenter.ctor"), (0x5DD7F54, "f44fbea9", "KIOU_HOOK_ID_UIBUTTONBASE_ONPOINTERCLICK", CAVE_ENTRY, "UIButtonBase.OnPointerClick"), (0x5DD2874, "ff0303d1", "KIOU_HOOK_ID_TITLE_SCENE_MOVENEXT", CAVE_ENTRY, "TitleScene+d__10.MoveNext"), + (0x594A034, "f44fbea9", "KIOU_HOOK_ID_GAME_ORCHESTRATOR_IS_AFK", CAVE_ENTRY, "GameOrchestrator.IsAfkEnabled"), ] # fmt: on