From e27650cf4232c89303fdea9aae205b63c8dbec17 Mon Sep 17 00:00:00 2001 From: xbzk Date: Tue, 8 Sep 2026 20:10:41 +0200 Subject: [PATCH] [nce] avoid cyrilic utf8 strings being interpreted as exclusive store instructions (#4369) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions). - [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md) - [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability. ------------------- Problem: In cyrilic idioms, Absolum won't past beyond 1st area (intro miniboss). From the user panic i`ve extracted these strings: > GlobalState.request_next() > save_state_to_saveGame(...) > platform.ProcessSave() > bin_serialize_to_file(...) > StandaloneTypeModel.Write(SaveGame, ...) > Write(SaveGame.AdventureSaveSlot, ...) > Write(PlayerState, ...) > ProtoBuf.ProtoWriter.WriteString(...) + 0xcc > ProtoBuf.Helpers.DebugAssert(...) > Debug.Fail(...) By debugging ProtoBuf.ProtoWriter.WriteString(...) argument it was found that the string Отлично! (Great! expression for combos between 6 and 30 iirc) was reaching a size comparison of different values and causing an assert in guest side.. It seems the game tries to save highest combo level. I've managed to skip this bug by doing a perfect combo for entire level, so that the problematic word never was generated or attempted to be saved. After further research, the issue chain was identified: Game maths literal string size via two ways and compare, and one of them was getting replaced by a wrong value due to Eden letting the literal string be interpreted as a real instruction. Logic added: const bool pair = decltype(l)::ExtractValue(raw) & 1; const bool fixed_rt2 = decltype(rt2)::ExtractValue(raw) == 0b11111; return pair || fixed_rt2; - pair checks bit 21 (L & 1): register-pair instructions actually use Rt2, so they remain accepted. - For single-register forms, require Rt2 == 31. The table word fails this check, so NCE leaves it intact. Sources https://www.scs.stanford.edu/~zyedidia/arm64/stxrb.html https://www.scs.stanford.edu/~zyedidia/arm64/stxp.html https://github.com/qemu/qemu/blob/master/target/arm/tcg/a64.decode#L344 Altough the solution is a case specific guard, it is data agnostic: This is a true fix to avoid a specific case in which a literal may be interpreted as a valid instruction, without interfering with other true instruction cases. Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4369 Reviewed-by: lizzie Reviewed-by: CamilleLaVey --- src/core/arm/nce/instructions.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/arm/nce/instructions.h b/src/core/arm/nce/instructions.h index 451778df65..6c71b6acf2 100644 --- a/src/core/arm/nce/instructions.h +++ b/src/core/arm/nce/instructions.h @@ -121,7 +121,10 @@ union Exclusive { constexpr explicit Exclusive(u32 raw_) : raw{raw_} {} constexpr bool Verify() { - return this->GetSig() == 0x10; + if (this->GetSig() != 0x10) return false; + const bool pair = decltype(l)::ExtractValue(raw) & 1; + const bool fixed_rt2 = decltype(rt2)::ExtractValue(raw) == 0b11111; + return pair || fixed_rt2; } constexpr u32 GetSig() {