Skip to content

记一次旮旯启动失败的心路历程

Updated: at 05:22

缘起

又一个凌晨,我准备美美打开自己的mac小薄本,推上一会我最爱的旮旯给,结果打开mac上的wine,然后打开了 弹出一个类似于这样的弹窗(找不到源弹窗截图了):

Member "MenuItem" does not exist
[ OK ]

我当时寻思着,这玩意,我几个月前刚打开过这个游戏,怎么会是呢,于是当即就打开ai让他给我调查一番,当然他又是公式化不停的看游戏资源问题也好,看引擎代码也好,看各种傻逼边角料细节也好,我受不了了只得重新带了log参数打开游戏给他参考:

CrossOver的log做的很棒
CrossOver的log做的很棒

运行成功之后,ai对着一堆傻逼没用的 log 翻来覆去,确实也找出了一些蛛丝马迹:

savedata/krkr.console.log(UTF-16LE)里,对话框之前先有这一行:

02:03:50 ! Specify an ID,
02:03:53 ==== An exception occured at k2compat.tjs(235)[(function) createDebugShortcutMenuItem] ====
02:03:53 #(235) var item = new MenuItem(win, sysarg);
02:03:53 00000038 gpd %2, %-2.*4 // *4 = (string)"MenuItem"
02:03:59 %-4=(string)"Member \"MenuItem\" does not exist"

Specify an ID 是 TJS2 引擎的 TJSIDExpected(識別子を指定してください)的英文版。ai 翻遍 krkrz 源码,它只有一个抛出点:

// tjs2/tjsObject.h — tTJSSymbolData
void _SetName(const tjs_char * name)
{
if(Name) Name->Release(), Name = NULL;
if(!name) TJS_eTJSError(TJSIDExpected);
if(!name[0]) TJS_eTJSError(TJSIDExpected);
Name = TJSAllocVariantString(name);
}

然后这啥福ai就开始疯狂怀疑是我wine的区域语言设置问题了,中间有段时间还怀疑是我这电脑的语言区域有问题,让我改成日区,我看着我的全汉化游戏:😅

然后又一通育儿式体贴般的引导,然后他三下五除二找了一堆要素然后言之凿凿的说:「winemac.drv 有 bug」。
我真想说梁文锋你训的ai也有bug。
然后他紧接着就怀疑 插件文件损坏了,特别是 menu.dll 这玩意损坏了,可能是坏档了。刚在10分钟前在win上刚打过这个游戏的我:😅

加 +relay 跑了之后ai扫了几十万行的log然后又找到一些蛛丝马迹:

Call KERNEL32.RaiseException(e06d7363,00000001,00000003,0022c5d0) ret=0063d6a7

0x0063d6a7 落在 exe 的 .text。反汇编 0x63d650:

63d650 push ebp
63d65b push 8
63d65e mov esi, 0x6d9bc0 ; ThrowInfo
63d666 rep movsd
63d6a1 call dword ptr [0x6c1238] ; 导入的 RaiseException
63d6a7 leave

这是 MSVC 的 _CxxThrowException 包装。顺着 ThrowInfo 解引用,异常类型是:

[0] pType → .?AVeTJSError@TJS@@
[1] pType → .?AVeTJS@TJS@@

抛的 C++ 异常,类型是 TJS 引擎自己的异常类。所以这很明显 不是什么真的崩溃,而是引擎遇到什么啥福信息崩溃了(非常关键)

反汇编 V2Link(RVA 0x3570)可以看到它通过
QueryFunctionsByNarrowString 逐个取宿主函数,名字全是纯 ASCII:

tTJSVariant::tTJSVariant()
iTJSDispatch2 * ::TVPGetScriptDispatch()
tTJSNativeClassForPlugin * ::TJSCreateNativeClassForPlugin(const ttstr &, ...)
void ::TVPRegisterAcceleratorKey(HWND,char,short,short)
... 共 62 个

ASCII 在简中语言环境下转换怎么可能失败呢,menu.dll 向 Window / MenuItem 注册成员时用的"Window" / "menu" / "MenuItem",更是不可能他妈的转换失败。

Call user32.MapVirtualKeyW(000000b7,0) ret=7a3b3149 → retval=00000000
Call user32.GetKeyNameTextW(02000000,0022c748,00000020) ret=7a3b315d
Ret user32.GetKeyNameTextW() retval=00000001

这是异常前的那段,当然我根本看不懂但是很明显这里在不停的gtkeyname,而且这段的log打在 keyboard 频道里,我们就顺理成章的再测了一遍 keyboard 的log:

lparam 0x02380000 -> L"Alt" // 正常
lparam 0x021d0000 -> L"Ctrl" // 正常
lparam 0x022a0000 -> L"Shift" // 正常
lparam 0x02000000 -> L"\0003lt" // ?
lparam 0x02000000 -> L"\0003aps Lock" // ?
lparam 0x02000000 -> L"\0003" // ?

scan = (lParam >> 16) & 0x1FF:

lParamscan次数结果
0x022a00000x2a3"Shift"
0x021d00000x1d3"Ctrl"
0x023800000x383"Alt"
0x022700000x272";"
0x020000000x0080"\x03lt" "\x03" …

很明显这一坨和正常的键位完全不像的 "\x03lt" 的狗屎玩意肯定就是某种错误:

应为实际次数
Alt\x03lt21
F2x\x032014
Caps Lock\x03aps Lock6
Esc\x03sc4
Down\x03own4
Tab\x03ab3
Return\x03eturn2
(单字符)\x0325

每个读到的键盘键名全都被污染了,导致几把的引擎没法初始化,然后嘎巴一下似这儿了
其实到现在我还没有任何头绪,当然这傻逼ai也没给我起到任何的引导作用,然后我让他搜难道其他用wine的人就从来没遇到过这种傻逼问题么? 它去查了一圈还去看了wine的源码:

dlls/winemac.drv/keyboard.c,macdrv_GetKeyNameText:

status = UCKeyTranslate(uchr, keyc, kUCKeyActionDisplay, ...,
size - 1, &len, (UniChar*)buffer);
if (status != noErr) len = 0;
if (len && buffer[0] > 32) buffer[len] = 0; // ← 只有可打印字符才补 '\0'
...
return len;

buffer[0] > 32 的意思是:只有可打印字符才写终止符。
当键名是控制字符(≤ 32,比如 0x03)时,buffer 永远不会有 '\0',
但函数照样 return len(非零)。

上层 win32u 于是无条件采信:

if ((len = user_driver->pGetKeyNameText(lparam, buffer, size)) >= 0) return len;

看完这些信息之后我的ai说:原来如此我明白了,问题非常清晰了 「一句话:函数往内存里写键名时忘了写「字符串结束符」,但返回值仍告诉调用者“写好了”,于是调用者一路读到了上一次留下的旧数据。」并言之凿凿的说,这一定是wine的bug,让我去上报吧,放弃吧,修不了的。
三个月前刚成功运行过的我:😅

峰回路转

到现在其实已经山穷水复疑无路了,我真的是急眼了红温了,然后问题是键盘我就思来想去我突然想到我1个月前其实改过某个键盘layout:

还好改动在git上有记录…
还好改动在git上有记录…

我突然就像被电了一下一样意识到这个一定是问题的关键,然后果不其然,ai告诉我当你在mac上用wine启动旮旯给的时候你当前的键盘layout甚至都会作为环境传入进去我操你妈…
虽然现在我还不知道为什么会这么运作但是,相当于wine把当前的键盘layout当作简中去识别一些键码,结果因为我当前的键位基于hexlayout,很多东西都没有,全部fallback了…
然后紧接着我把当前输入法切到abc,然后尝试打开我心爱的旮旯给:

日向:没有没有问题
日向:没有没有问题

事情重新像我想的那样运作be like,当然我在写这篇文章的时候因为忙着草日向呢所以没空细究到底是为啥以及这到底是不是啥福wine的bug亦或是又是傻逼deepseek的幻觉,但是ai确实很不错,作为一个优秀的代码搜索工具,至于想让他有点脑子那还是算了吧。