From b28799ef04571552c14d3596fecb89eff1454920 Mon Sep 17 00:00:00 2001 From: tkgstrator Date: Sat, 1 Aug 2026 12:54:42 +0000 Subject: [PATCH 1/3] feat: support KIOU 1.1.0 (build 15) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit __TEXT grew from 0x8274000 to 0x94BC000 in 1.1.0, so not one 1.0.2 address survives — the old dylib branches into arbitrary code on launch. TARGET_VERSION now defaults to 1.1.0 and maps to KIOU-Hook's KIOU_HOOK_TARGET_BUILD, so the dylib and the recipe can't disagree about which addresses to bake in; an unknown version is an error rather than a silent fallback to whatever the catalog defaults to. The five direct-call RVAs the KIF pipeline uses move from hardcoded defines to the KIOU-Hook catalog, so they track TARGET_VERSION like every other site instead of needing a hand edit per release. Every struct offset the KIF pipeline reads is unchanged from 1.0.2, confirmed by diffing the two dump.cs files. The one offset a dump can't confirm — RP_OFF_CURRENT_VALUE, which the dumper reports as 0x0 for every generic instantiation — was verified on-device: the KIF from an online match carries the opponent's real name, not the placeholder it falls back to when that offset is wrong. Kanade moves to develop, which carries the verify_sites overload resolution and the caves.py placeholder rows 1.1.0 needs (1.1.0 has a recipe row whose method no longer exists in the build). Co-Authored-By: Claude Opus 4.7 --- CHANGELOG.md | 9 +++++++ Makefile | 15 ++++++++++- README.ja.md | 34 ++++++++++++------------ README.md | 38 +++++++++++++-------------- Sources/KiouForge/Kif/Helpers.m | 46 ++++++++++++++++++--------------- control | 2 +- docs/plans/on-device-smoke.md | 9 ++++--- shared | 2 +- vendor/KIOU-Hook | 2 +- 9 files changed, 92 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d670297..994c049 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,17 @@ All notable changes to KiouForge are documented here. ## [Unreleased] +### Added + +- **KIOU 1.1.0 support** (CFBundleVersion 15). All 17 hook sites re-verified against the 1.1.0 Mach-O; every struct offset the KIF pipeline reads is unchanged from 1.0.2. + ### Changed +- `TARGET_VERSION` now defaults to `1.1.0`; `1.0.1` and `1.0.2` remain buildable by passing it explicitly. The Makefile maps the version to KIOU-Hook's `KIOU_HOOK_TARGET_BUILD` so the dylib and the recipe always agree on which addresses to bake in, and errors out on an unknown version instead of silently falling back. +- The five direct-call RVAs the KIF pipeline uses (`GetUSIText`, `ToSFEN`, `ParseUSI`, `KIFWriteOptions..ctor`, `KIFWriter.Write`) now resolve through the KIOU-Hook catalog instead of being pinned in `Kif/Helpers.m`, so they follow `TARGET_VERSION` like every other site. +- `make deploy` accepts `THEOS_DEVICE_PORT`, needed when the device is reached through an iproxy forward on the host — there port 22 answers as the host rather than the phone. +- FPS override logging only reports transitions. KIOU re-applies its own target every ~5 s, and logging each one buried every other line (198 of 242 lines in one session). +- Bumped the `vendor/KIOU-Hook` submodule to `8ab5868` (per-version RVA headers under `rva/`, `recipes/v1_1_0.py`) and `shared` (Kanade) to develop, which carries the verify_sites overload resolution and the `caves.py` placeholder rows 1.1.0 needs. - `make deploy` now works on TrollStore Lite devices. `TROLLSTORE_HELPER` defaults to empty and the target SSH-discovers the helper binary at deploy time, preferring `TrollStoreLite.app` over `TrollStore.app` and filtering out stale `TrollStorePersistenceHelper.app` leftovers whose entitlements expire between sessions (which manifest as ssh 255 / SIGKILL 137). The IPA is staged in `/var/mobile/Documents/` instead of `/tmp/` — the trollstorehelper sandbox cannot read `/tmp/` and rejects an IPA there with error 166. The staged file is `chown mobile:mobile`-ed after `scp` so the helper can open it. Non-Lite setups keep working unchanged; operators who pinned `TROLLSTORE_HELPER` in their `.env` are unaffected. `REMOTE_STAGING_DIR` is exposed as an override for setups that stage elsewhere. ## [0.2.1] — 2026-06-26 diff --git a/Makefile b/Makefile index e0df669..94b7430 100644 --- a/Makefile +++ b/Makefile @@ -20,8 +20,20 @@ TARGET_PROCESS := KIOU TARGET_BUNDLE_ID := com.neconome.shogi # Override on the command line: make ipa TARGET_VERSION=1.0.2 -TARGET_VERSION ?= 1.0.2 +TARGET_VERSION ?= 1.1.0 DECRYPTED_IPA ?= $(CURDIR)/assets/$(TARGET_VERSION)/Kiou-$(TARGET_VERSION).ipa + +# KIOU-Hook selects its per-version RVA header (vendor/KIOU-Hook/rva/) by +# CFBundleVersion, not by marketing version, so map one to the other here. +# Without the -D below the catalog silently falls back to its own default +# and the dylib gets built against another build's addresses. +KIOU_BUILD_1.0.1 := 11 +KIOU_BUILD_1.0.2 := 12 +KIOU_BUILD_1.1.0 := 15 +KIOU_HOOK_TARGET_BUILD := $(KIOU_BUILD_$(TARGET_VERSION)) +ifeq ($(KIOU_HOOK_TARGET_BUILD),) +$(error unknown TARGET_VERSION '$(TARGET_VERSION)'; known: 1.0.1 1.0.2 1.1.0) +endif IPA_RECIPE := recipes.__init__ KIOU_HOOK_DIR := $(CURDIR)/vendor/KIOU-Hook IPA_FRAMEWORK := UnityFramework @@ -70,6 +82,7 @@ endif $(TWEAK_NAME)_CFLAGS := -fobjc-arc -Wno-unused-function \ -D$(BUILD_COMMIT_DEFINE)=\"$(BUILD_COMMIT)\" \ -DKIOU_FORGE_VERSION=\"$(PACKAGE_VERSION)\" \ + -DKIOU_HOOK_TARGET_BUILD=$(KIOU_HOOK_TARGET_BUILD) \ -ISources/Chinlan -I$(TWEAK_SOURCES_DIR) \ -Ivendor/KIOU-Hook ifdef FINAL_RELEASE diff --git a/README.ja.md b/README.ja.md index 8e719cb..453b891 100644 --- a/README.ja.md +++ b/README.ja.md @@ -12,7 +12,7 @@

version - targets KIOU + targets KIOU platform arch runs @@ -136,21 +136,21 @@ KIOU はオンラインゲームのため頻繁にアップデートされ、古 ### 機能 × バージョン対応表 -| 機能 | 1.0.1 (build 11) | 1.0.2 (build 12) | -|---|:---:|:---:| -| FPS Override | ✓ | ✓ | -| AFK Guard | ✓ | ✓ | -| Analysis Tune | ✓ | ✓ | -| Kifu Autosave | ✓ | ✓ | -| **アカウント切り替え** | — | ✓ | +| 機能 | 1.0.1 (build 11) | 1.0.2 (build 12) | 1.1.0 (build 15) | +|---|:---:|:---:|:---:| +| FPS Override | ✓ | ✓ | ✓ | +| AFK Guard | ✓ | ✓ | ✓ | +| Analysis Tune | ✓ | ✓ | ✓ | +| Kifu Autosave | ✓ | ✓ | ✓ | +| **アカウント切り替え** | — | ✓ | ✓ | -アカウント切り替えは 1.0.2 で追加されたフックサイトが必要なため、1.0.1 では非対応です。Jailed / JB ビルドは RVA がコンパイル時に固定されるため、常に**最新バージョンのみ**を対象とします。Patched IPA は `TARGET_VERSION` でビルド時にレシピを選択します。 +アカウント切り替えは 1.0.2 で追加されたフックサイトが必要なため、1.0.1 では非対応です。アドレスはどの配布形式でもビルド時に `TARGET_VERSION` で決まります。Patched IPA はレシピを、dylib は対応する RVA ヘッダを選択します。 ### プラットフォーム | | | |---|---| -| **最新対応 KIOU** | `1.0.2`(CFBundleVersion 12) | +| **最新対応 KIOU** | `1.1.0`(CFBundleVersion 15) | | **KIOU の最低 iOS バージョン** | 10.0(アプリの `MinimumOSVersion`) | | **KiouForge の最低 iOS バージョン** | 13.0(`UIWindowScene` が必要) | | **動作確認済み** | iOS 15.0 〜 26、arm64 | @@ -187,22 +187,22 @@ TrollStore が使えないデバイス向けです。[Sideloadly](https://sidelo **復号済み** の KIOU IPA が必要です([palera1n](https://palera.in/) + Filza または [TrollDecrypt](https://github.com/donato-fiore/TrollDecrypt) で取得できます)。App Store からダウンロードした IPA は FairPlay で暗号化されているため、そのままでは使用できません。 ```sh -# デフォルト(1.0.2) -mkdir -p assets/1.0.2 -cp ~/Downloads/Kiou-1.0.2.ipa assets/1.0.2/ +# デフォルト(1.1.0) +mkdir -p assets/1.1.0 +cp ~/Downloads/Kiou-1.1.0.ipa assets/1.1.0/ make ipa # -> packages/ipa/KiouForge-patched.ipa # バージョンを指定する場合 -make ipa TARGET_VERSION=1.0.1 +make ipa TARGET_VERSION=1.0.2 ``` フックサイトを編集した後や KIOU のアップデート後にビルドする場合: ```sh # 特定バージョンの dump + IPA でレシピを検証 -PYTHONPATH=shared:. TARGET_VERSION=1.0.2 python3 -m tools.verify_sites \ +PYTHONPATH=shared:vendor/KIOU-Hook TARGET_VERSION=1.1.0 python3 -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 + --index assets/1.1.0/dump.cs.index.json \ + --ipa assets/1.1.0/Kiou-1.1.0.ipa ``` diff --git a/README.md b/README.md index f5f1148..588ed6c 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@

version - targets KIOU + targets KIOU platform arch runs @@ -179,23 +179,23 @@ server-side. The recommended target is always the **latest supported version**. ### Feature × version matrix -| Feature | 1.0.1 (build 11) | 1.0.2 (build 12) | -|---|:---:|:---:| -| FPS Override | ✓ | ✓ | -| AFK Guard | ✓ | ✓ | -| Analysis Tune | ✓ | ✓ | -| Kifu Autosave | ✓ | ✓ | -| **Account Switching** | — | ✓ | +| Feature | 1.0.1 (build 11) | 1.0.2 (build 12) | 1.1.0 (build 15) | +|---|:---:|:---:|:---:| +| FPS Override | ✓ | ✓ | ✓ | +| AFK Guard | ✓ | ✓ | ✓ | +| Analysis Tune | ✓ | ✓ | ✓ | +| Kifu Autosave | ✓ | ✓ | ✓ | +| **Account Switching** | — | ✓ | ✓ | -Account Switching requires hook sites introduced in 1.0.2; the Jailed/JB build -always targets the **latest** version only (RVAs are pinned at compile time). -The Patched IPA build selects the recipe at build time via `TARGET_VERSION`. +Account Switching requires hook sites introduced in 1.0.2. Every build shape +pins its addresses at build time via `TARGET_VERSION`: the Patched IPA picks +the recipe, and the dylib picks the matching RVA header. ### Platform | | | |---|---| -| **Latest supported KIOU** | `1.0.2` (CFBundleVersion 12) | +| **Latest supported KIOU** | `1.1.0` (CFBundleVersion 15) | | **KIOU minimum iOS** | 10.0 (`MinimumOSVersion` in app bundle) | | **KiouForge minimum iOS** | 13.0 (requires `UIWindowScene`) | | **Tested on** | iOS 15.0 – 26, arm64 | @@ -238,23 +238,23 @@ Filza, or [TrollDecrypt](https://github.com/donato-fiore/TrollDecrypt)). The App Store download is FairPlay-encrypted and cannot be patched directly. ```sh -# default (1.0.2) -mkdir -p assets/1.0.2 -cp ~/Downloads/Kiou-1.0.2.ipa assets/1.0.2/ +# default (1.1.0) +mkdir -p assets/1.1.0 +cp ~/Downloads/Kiou-1.1.0.ipa assets/1.1.0/ make ipa # -> packages/ipa/KiouForge-patched.ipa # target a specific version -make ipa TARGET_VERSION=1.0.1 +make ipa TARGET_VERSION=1.0.2 ``` Before building after editing hook sites or after a KIOU update: ```sh # verify current recipe against a specific version's dump + IPA -PYTHONPATH=shared:. TARGET_VERSION=1.0.2 python3 -m tools.verify_sites \ +PYTHONPATH=shared:vendor/KIOU-Hook TARGET_VERSION=1.1.0 python3 -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 + --index assets/1.1.0/dump.cs.index.json \ + --ipa assets/1.1.0/Kiou-1.1.0.ipa ``` diff --git a/Sources/KiouForge/Kif/Helpers.m b/Sources/KiouForge/Kif/Helpers.m index af7f607..640d302 100644 --- a/Sources/KiouForge/Kif/Helpers.m +++ b/Sources/KiouForge/Kif/Helpers.m @@ -35,10 +35,10 @@ // KIOUKifTextFromGameController is the bridge into il2cpp — everything else is // pure Foundation. // -// Background: GameController.GetKifuText (RVA 0x5D43D10) returns an in-app -// SUMMARY ("001 ☗ ☗3八飛 … まで、☖後手の勝ち(詰み)"), NOT the standard -// KIF 2.0 that desktop kifu viewers expect. We instead run the canonical -// pipeline KIOU uses internally: +// Background: GameController.GetKifuText returns an in-app SUMMARY +// ("001 ☗ ☗3八飛 … まで、☖後手の勝ち(詰み)"), NOT the standard KIF 2.0 +// that desktop kifu viewers expect. We instead run the canonical pipeline +// KIOU uses internally: // // GetUSIText(self) → "position startpos moves ..." // ↓ @@ -71,18 +71,10 @@ // touched by the fill is ThinkingTimesMicros (per-move clock, queued for v0.4). // =========================================================================== -// --------------------------------------------------------------------------- -// RVAs (KIOU 1.0.1 build 11). Same source of truth as KiouUsiProxy. -// --------------------------------------------------------------------------- -// RVAs pinned to KIOU 1.0.2 (dump.cs verified 2026-07-02). The previous -// values were 1.0.1 leftovers pointing at unrelated methods — calling -// through them at match-end crashed with a SIGSEGV inside GetUSIText -// (LDR from a bogus X10 loaded from *(gameCtrl+0xB0)). -#define RVA_GAMECTRL_GET_USI_TEXT 0x5D49970 // string GameController.GetUSIText(this) -#define RVA_POSITION_TO_SFEN 0x5D49C70 // string Position.ToSFEN(this) -#define RVA_USIPARSER_PARSE_USI 0x5D5CBB0 // static RecordManager USIParser.ParseUSI(string) -#define RVA_KIFWRITEOPTIONS_CTOR 0x5D5925C // void KIFWriteOptions..ctor(this) -#define RVA_KIFWRITER_WRITE 0x5D59264 // static string KIFWriter.Write(RecordManager, KIFWriteOptions) +// The five methods this pipeline calls are direct-ABI catalog rows in +// KIOU-Hook (hook_id = -1 — resolved and called, never hooked), so their +// addresses come from KIOUHookSiteAddr and follow KIOU_HOOK_TARGET_BUILD +// instead of being pinned to one app version here. // KIFWriteOptions instance size needed for the raw-buffer trick. See the // KIFOPTS_OFF_* constants in Internal.h for the field map. Last field @@ -159,29 +151,41 @@ static KIFWriter_Write_t g_KIFWriter_Write = NULL; static Position_ToSFEN_t g_PositionToSFEN = NULL; +// KIOUHookSiteAddr returns 0 for a name the catalog doesn't carry; return +// NULL rather than a pointer to unityBase itself so callers' existing null +// checks catch it. +static void *resolveSite(const char *name) { + uintptr_t addr = KIOUHookSiteAddr(name, g_unityBase); + if (addr == 0) { + IPALog([NSString stringWithFormat:@"[KIF] site unresolved: %s", name]); + return NULL; + } + return (void *)addr; +} + // Resolve the il2cpp NativeFunction pointers we use. Idempotent and cheap; // safe to call once per export call. static void resolveIl2cppFunctions(void) { if (g_unityBase == 0) return; if (!g_GetUSIText) { g_GetUSIText = (GameCtrl_GetUSIText_t) - (void *)(g_unityBase + RVA_GAMECTRL_GET_USI_TEXT); + resolveSite(KIOU_HOOK_NAME_GAMECTRL_GET_USI_TEXT); } if (!g_ParseUSI) { g_ParseUSI = (USIParser_ParseUSI_t) - (void *)(g_unityBase + RVA_USIPARSER_PARSE_USI); + resolveSite(KIOU_HOOK_NAME_USIPARSER_PARSE_USI); } if (!g_KIFOpts_Ctor) { g_KIFOpts_Ctor = (KIFWriteOptions_Ctor_t) - (void *)(g_unityBase + RVA_KIFWRITEOPTIONS_CTOR); + resolveSite(KIOU_HOOK_NAME_KIFWRITEOPTIONS_CTOR); } if (!g_KIFWriter_Write) { g_KIFWriter_Write = (KIFWriter_Write_t) - (void *)(g_unityBase + RVA_KIFWRITER_WRITE); + resolveSite(KIOU_HOOK_NAME_KIFWRITER_WRITE); } if (!g_PositionToSFEN) { g_PositionToSFEN = (Position_ToSFEN_t) - (void *)(g_unityBase + RVA_POSITION_TO_SFEN); + resolveSite(KIOU_HOOK_NAME_POSITION_TO_SFEN); } } diff --git a/control b/control index e1ca59c..091f645 100644 --- a/control +++ b/control @@ -1,6 +1,6 @@ Package: work.tkgstrator.kiouforge Name: KiouForge -Version: 0.2.1 +Version: 0.2.2 Architecture: iphoneos-arm64 Description: KIOU (Shogi) local quality-of-life tool. Extends the frame-rate preset beyond 30/60, suppresses false AFK warnings during long-think sessions, and lets you tune the depth and hash used by the on-device post-game kifu analysis engine. Runs entirely client-side and never modifies any server-stored data. For authorized testing only. Maintainer: NEVER KNOWS BEST diff --git a/docs/plans/on-device-smoke.md b/docs/plans/on-device-smoke.md index 8355a4d..36bfdc1 100644 --- a/docs/plans/on-device-smoke.md +++ b/docs/plans/on-device-smoke.md @@ -78,9 +78,10 @@ practice. Broader flows can layer on the same harness later. frida-il2cpp-bridge`, or `pip install frida-tools` if we go Python). The design below is bun-based to match KiouForge's existing tool chain in `shared/tools/`. -- **`assets/1.0.2/dump.cs.index.json`** (already checked in) — Frida - script reads class + method names from it so RVAs never appear - literally in the script (they change per app version). +- **`assets/$TARGET_VERSION/dump.cs.index.json`** (already checked in + for 1.0.1 / 1.0.2 / 1.1.0) — Frida script reads class + method names + from it so RVAs never appear literally in the script (they change per + app version). ## Architecture @@ -267,7 +268,7 @@ scripts/ case explicitly; user-visible failures still map to "SSH says frida-server crashed" rather than false test failures. - **Presenter signatures change per app version.** The script pins - to 1.0.2 today; when 1.0.3 lands, presenter method names or + to 1.1.0 today; when the next build lands, presenter method names or signatures may drift. Mitigation: keep the mapping (presenter, method, args) in a small YAML config file, loaded per `TARGET_VERSION`. diff --git a/shared b/shared index c29a372..b670411 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit c29a372c7cd1d447c1f12a8b580986aba327e9ef +Subproject commit b6704113065f4ccef308926d87895c49f5691187 diff --git a/vendor/KIOU-Hook b/vendor/KIOU-Hook index 36a3ad1..8ab5868 160000 --- a/vendor/KIOU-Hook +++ b/vendor/KIOU-Hook @@ -1 +1 @@ -Subproject commit 36a3ad15d5f67cdcc3b68e7374049771c8a960a7 +Subproject commit 8ab586811f017520fe185631bc238c551d711adc From faa8956a2fc69440f57d2407fdc17e9c657c5b41 Mon Sep 17 00:00:00 2001 From: tkgstrator Date: Sat, 1 Aug 2026 12:54:51 +0000 Subject: [PATCH 2/3] build(deploy): accept THEOS_DEVICE_PORT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The deploy target assumed the device answers on port 22. That holds for a device on the LAN, but not when it is reached through an iproxy forward on the host (THEOS_DEVICE_IP=host.docker.internal) — there port 22 is the host's own sshd, so scp lands the IPA on the wrong machine and the install silently targets nothing. Defaults to 22, so LAN setups are unaffected. Co-Authored-By: Claude Opus 4.7 --- Makefile | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 94b7430..bbf17a4 100644 --- a/Makefile +++ b/Makefile @@ -211,18 +211,26 @@ ipa:: chinlan # INSTALLED_IPA_BUNDLE_ID — bundle id used to relaunch the app # DEVICE_USER — SSH user (defaults to root) # REMOTE_STAGING_DIR — sandbox-visible dir to scp the IPA into +# THEOS_DEVICE_PORT — SSH port. Needed when the device is reached +# through an iproxy forward on the host +# (THEOS_DEVICE_IP=host.docker.internal), where +# port 22 answers as the host, not the phone. # --------------------------------------------------------------------------- TROLLSTORE_HELPER ?= INSTALLED_IPA_BUNDLE_ID ?= $(TARGET_BUNDLE_ID)$(if $(BUNDLE_ID_SUFFIX),.$(BUNDLE_ID_SUFFIX),) DEVICE_USER ?= root REMOTE_STAGING_DIR ?= /var/mobile/Documents +THEOS_DEVICE_PORT ?= 22 +# scp spells the port -P, ssh spells it -p. +DEVICE_SCP := scp -P $(THEOS_DEVICE_PORT) +DEVICE_SSH := ssh -p $(THEOS_DEVICE_PORT) .PHONY: deploy deploy: ipa @helper='$(TROLLSTORE_HELPER)'; \ if [ -z "$$helper" ]; then \ echo "==> discovering trollstorehelper on $(THEOS_DEVICE_IP)"; \ - helper=$$(ssh $(DEVICE_USER)@$(THEOS_DEVICE_IP) \ + helper=$$($(DEVICE_SSH) $(DEVICE_USER)@$(THEOS_DEVICE_IP) \ "find /var/jb/Applications /var/containers/Bundle/Application \ -maxdepth 4 -type f -name trollstorehelper 2>/dev/null \ | grep -v 'PersistenceHelper' \ @@ -235,15 +243,15 @@ deploy: ipa exit 1; \ fi; \ echo "==> helper: $$helper"; \ - echo "==> scp $(notdir $(IPA_OUT)) -> $(DEVICE_USER)@$(THEOS_DEVICE_IP):$(REMOTE_STAGING_DIR)/"; \ - scp -q $(IPA_OUT) $(DEVICE_USER)@$(THEOS_DEVICE_IP):$(REMOTE_STAGING_DIR)/$(notdir $(IPA_OUT)); \ - ssh $(DEVICE_USER)@$(THEOS_DEVICE_IP) \ + echo "==> scp $(notdir $(IPA_OUT)) -> $(DEVICE_USER)@$(THEOS_DEVICE_IP):$(THEOS_DEVICE_PORT):$(REMOTE_STAGING_DIR)/"; \ + $(DEVICE_SCP) -q $(IPA_OUT) $(DEVICE_USER)@$(THEOS_DEVICE_IP):$(REMOTE_STAGING_DIR)/$(notdir $(IPA_OUT)); \ + $(DEVICE_SSH) $(DEVICE_USER)@$(THEOS_DEVICE_IP) \ "chown mobile:mobile '$(REMOTE_STAGING_DIR)/$(notdir $(IPA_OUT))' 2>/dev/null || true"; \ echo "==> trollstorehelper install force $(REMOTE_STAGING_DIR)/$(notdir $(IPA_OUT))"; \ - ssh $(DEVICE_USER)@$(THEOS_DEVICE_IP) \ + $(DEVICE_SSH) $(DEVICE_USER)@$(THEOS_DEVICE_IP) \ "$$helper install force $(REMOTE_STAGING_DIR)/$(notdir $(IPA_OUT))" @echo "==> launching $(TARGET_PROCESS) ($(INSTALLED_IPA_BUNDLE_ID))" - @ssh $(DEVICE_USER)@$(THEOS_DEVICE_IP) 'sleep 1; (open $(INSTALLED_IPA_BUNDLE_ID) 2>/dev/null \ + @$(DEVICE_SSH) $(DEVICE_USER)@$(THEOS_DEVICE_IP) 'sleep 1; (open $(INSTALLED_IPA_BUNDLE_ID) 2>/dev/null \ || uiopen $(INSTALLED_IPA_BUNDLE_ID):// 2>/dev/null \ || echo "no launcher tool; start $(TARGET_PROCESS) manually")' From 6f2a14e9b8ed4d03dce851b93be4f79c43ea4bdd Mon Sep 17 00:00:00 2001 From: tkgstrator Date: Sat, 1 Aug 2026 12:54:52 +0000 Subject: [PATCH 3/3] fix(hooks): log FPS overrides only on transition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KIOU re-applies its own frame-rate target every ~5 s, so the override logged an identical line each time — 198 of 242 lines in one session, burying everything else in the log. Co-Authored-By: Claude Opus 4.7 --- Sources/KiouForge/Hook/FrameRate.m | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Sources/KiouForge/Hook/FrameRate.m b/Sources/KiouForge/Hook/FrameRate.m index 42353de..f4b58eb 100644 --- a/Sources/KiouForge/Hook/FrameRate.m +++ b/Sources/KiouForge/Hook/FrameRate.m @@ -16,7 +16,12 @@ static int32_t pickFPS(int32_t value) { int32_t v = KIOUFeatureEnabled(KIOU_FEATURE_FPS_OVERRIDE) ? KIOUTargetFPS() : value; - if (v != value) { + // KIOU re-applies its own target every ~5 s, so logging every + // override would bury everything else. Only report transitions. + static int32_t lastFrom = -1, lastTo = -1; + if (v != value && (value != lastFrom || v != lastTo)) { + lastFrom = value; + lastTo = v; IPALog([NSString stringWithFormat: @"[FPS] set_targetFrameRate %d -> %d (override)", value, v]); }