From d45c5c6cf588f99bd6a344a4c033c62349cb5c5f Mon Sep 17 00:00:00 2001 From: shen <> Date: Wed, 30 Sep 2026 17:54:11 -0700 Subject: [PATCH] Fix Android directory CRC login crash and record Mi 8 Lite device tests --- audit/history.jsonl | 1 + audit/port-map/EterBase/CRC32.cpp.json | 4 +-- docs/XIAOMI8LITE-DEVICE-TEST-20260930.md | 37 ++++++++++++++++++++++++ src/platform/EterBase/CRC32.cpp | 15 ++++++++-- tests/port/port_utils_test.cpp | 7 +++++ 5 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 docs/XIAOMI8LITE-DEVICE-TEST-20260930.md diff --git a/audit/history.jsonl b/audit/history.jsonl index 5636b292..6865e6bc 100644 --- a/audit/history.jsonl +++ b/audit/history.jsonl @@ -647,3 +647,4 @@ {"date": "2026-09-30", "unit": "EterLib/GrpFontTexture.cpp", "action": "improve-platform-rendering", "evidence": "Retina UI clarity: dynamic font atlas rebuild preserving cached pointers and logical metrics, 8-bit HiDPI glyph alpha, physical pixel alignment and 1:1 sampling; 2406 bundled HD textures. Full native suite 29 passed, 2 skipped, macOS packaged inventory screenshots and strict signature verification. See audit/evidence/2026-09-30-mac-ui-clarity.md."} {"date": "2026-09-30", "unit": "EterLib/GrpImageInstance.cpp", "action": "improve-platform-rendering", "evidence": "Retina UI clarity: dynamic font atlas rebuild preserving cached pointers and logical metrics, 8-bit HiDPI glyph alpha, physical pixel alignment and 1:1 sampling; 2406 bundled HD textures. Full native suite 29 passed, 2 skipped, macOS packaged inventory screenshots and strict signature verification. See audit/evidence/2026-09-30-mac-ui-clarity.md."} {"date": "2026-09-30", "unit": "UserInterface/PythonPlayerAutoHunt.cpp", "action": "user-authorized-extension", "evidence": "Replace tag-and-chase gather with free server-authoritative cloak-style BeginFight pull, nearest 3/10/20 actual monster groups. Server rate limit and safety checks, request generations, stationary approach wait. host.autohunt_pull and desktop/mobile port.login_flow pass; 203 FreeBSD compile/deploy and live 3-group/9-monster reply verified. See tools/server/autohunt-pull/README.md."} +{"date": "2026-09-30", "unit": "EterBase/CRC32.cpp", "action": "fix-platform-adapter", "evidence": "MI 8 Lite Android 15 fake-login SIGABRT/std::bad_alloc from directory CRC. Reject non-regular/null FILE handles and failed seeks; preserve normal CRC/position. port_utils_test pass, port_map check 0 errors; rebuilt APK installed and on-device fake-login/map entry succeeds. docs/XIAOMI8LITE-DEVICE-TEST-20260930.md"} diff --git a/audit/port-map/EterBase/CRC32.cpp.json b/audit/port-map/EterBase/CRC32.cpp.json index 4f0c6f92..45452049 100644 --- a/audit/port-map/EterBase/CRC32.cpp.json +++ b/audit/port-map/EterBase/CRC32.cpp.json @@ -30,7 +30,7 @@ "test": [ "tests/port/port_utils_test.cpp" ], - "note": "40250 maps the file (CreateFile + CreateFileMapping/MapViewOfFile from offset 0) and CRCs the whole view; the port reads the whole FILE* into a buffer. Invariant: same CRC of the full file, the handle's position is unchanged, 0 when the file cannot be opened." + "note": "40250 maps the file (CreateFile + CreateFileMapping/MapViewOfFile from offset 0) and CRCs the whole view; the port reads the whole FILE* into a buffer. Invariant: same CRC of the full file, the handle's position is unchanged, 0 when the file cannot be opened. Android 15 MI 8 Lite regression: reject non-regular FILE handles before seeking/allocating, preserving Win32 directory-open failure; check seek errors. Directory/null regression in port_utils_test and successful on-device fake-login/map entry after prior std::bad_alloc abort." }, "GetFileCRC32": { "status": "ADAPTED", @@ -40,7 +40,7 @@ "test": [ "tests/port/port_utils_test.cpp" ], - "note": "40250 maps the file (CreateFile + CreateFileMapping/MapViewOfFile from offset 0) and CRCs the whole view; the port reads the whole FILE* into a buffer. Invariant: same CRC of the full file, the handle's position is unchanged, 0 when the file cannot be opened. Callers: GuildMarkUploader/GuildMarkDownloader symbol CRC, root pack CRC report." + "note": "40250 maps the file (CreateFile + CreateFileMapping/MapViewOfFile from offset 0) and CRCs the whole view; the port reads the whole FILE* into a buffer. Invariant: same CRC of the full file, the handle's position is unchanged, 0 when the file cannot be opened. Callers: GuildMarkUploader/GuildMarkDownloader symbol CRC, root pack CRC report. Android 15 MI 8 Lite regression: reject non-regular FILE handles before seeking/allocating, preserving Win32 directory-open failure; check seek errors. Directory/null regression in port_utils_test and successful on-device fake-login/map entry after prior std::bad_alloc abort." }, "GetFileSize": { "status": "ADAPTED", diff --git a/docs/XIAOMI8LITE-DEVICE-TEST-20260930.md b/docs/XIAOMI8LITE-DEVICE-TEST-20260930.md new file mode 100644 index 00000000..84d4c720 --- /dev/null +++ b/docs/XIAOMI8LITE-DEVICE-TEST-20260930.md @@ -0,0 +1,37 @@ +# 小米 8 Lite 真机测试(2026-09-30) + +已安装 Release 原生库的 debug 签名 APK,并同步 1273 个中文客户端资源文件(约 1474 MB)。 +设备为 MI 8 Lite / platina、SDM660、Adreno 512、约 4 GB RAM,Android 15 API 35, +LineageOS 22.2-20260103-UNOFFICIAL,GPU Vulkan 1.1.128,OpenGL ES 3.2,4 KB 页。 +屏幕 2280×1080 横屏 / 60 Hz;驱动构建日期 2020 年。手机时钟比工作区日期早进一天,CSV 名称保留手机原值。 + +## 结果 + +- 正常启动显示中文服务器选择及空账号登录页;软键盘可弹出;应用切后台返回正常。 +- 本地模拟登录经过真实客户端登录/选人/加载流程,进入 `metin2_map_a1`,24 只怪物及 4 个其他角色。 +- 触控摇杆后角色移动,背包可通过 I 键打开。背包窗口与右侧触控技能按钮重叠,需另行优化遮挡。 +- 场景、角色、怪物和背包均显示,渲染可运行,但目前性能不能满足流畅游玩。 +- 默认设置(75% 场景分辨率、2x MSAA)8 个两秒采样窗口:约 3.25 FPS,GPU 平均约 312 ms,RSS 约 580 MB。 +- 60% 场景分辨率、关闭 MSAA,同场景 8 个两秒采样窗口:约 5 FPS,GPU 平均约 204 ms,RSS 约 594 MB。 +- 50% 场景分辨率、关闭 MSAA:静止场景约 6.3–6.4 FPS,GPU 约 157 ms;移动和开背包后场景不同,不用于同场景均值比较。 +- 日志显示 GPU 等待远高于脚本/CPU 时间,优先排查地形、多层混合、透明覆盖及片元着色开销,单纯降分辨率不足以达到 30 FPS。 +- ROM 的 ADPF PerformanceHint/PowerHAL 不可用,客户端按已有回退路径运行;日志有 AHardwareBuffer 小尺寸探测失败,未阻断此次 Vulkan 启动。 + +## 修复的闪退 + +初版 APK 在进入游戏的 `BuildProcessCRC -> GetFileCRC32 -> GetHFILECRC32` 阶段发生 `std::bad_alloc` / SIGABRT。 +`GetExcutedFileName` 当前报告工作目录;POSIX `fopen` 可以打开目录,Android 的目录 seek 偏移可能极大, +CRC 文件适配器因此尝试分配异常大的缓冲区。40250 的普通 Win32 文件打开不接受目录。 + +文件适配层现在先检查 FILE 指向普通文件,拒绝目录/空句柄并检查 seek 错误,普通文件 CRC 和位置保持不变。 +`port_utils_test` 通过;审计 `port_map.py check` 为 0 错误。重新构建、安装后两次模拟登录均成功进入地图。 +未改变游戏登录协议或绕过 CRC 校验逻辑。 + +## 范围与后续 + +这是短时真机功能及性能测试,不是长时间温升或功耗测试(USB 充电中)。 +尚未提供游戏测试账号,因此未登录真实服务器,未验证服务器战斗、技能命中、自动狩猎、移动戒指或切地图。 +建议下一轮用真实账号复核;性能方面先增加 GPU 分项测试,再处理渲染瓶颈,最后决定低配默认参数。 +本次未修改默认画质,手机最终恢复正常登录入口,模拟登录仅由测试启动参数启用。 + +截图、CSV 和 logcat 位于 `build/qa/xiaomi8lite-20260930/`;APK 位于 `android/app/build/outputs/apk/debug/app-debug.apk`。 diff --git a/src/platform/EterBase/CRC32.cpp b/src/platform/EterBase/CRC32.cpp index 6ef12427..1200d443 100644 --- a/src/platform/EterBase/CRC32.cpp +++ b/src/platform/EterBase/CRC32.cpp @@ -6,6 +6,7 @@ #include #include +#include namespace { @@ -107,10 +108,20 @@ DWORD GetHFILECRC32(HANDLE hFile) { // 40250 maps the whole file from offset 0 and CRCs it; the stream position is left where it was. FILE* fp = static_cast(hFile); + // PORT: Win32 CreateFile(FILE_ATTRIBUTE_NORMAL) rejects directories. POSIX fopen does not; + // Android directory seek offsets can be enormous, so reject them before allocating a buffer. + struct stat info{}; + if (!fp || fstat(fileno(fp), &info) != 0 || !S_ISREG(info.st_mode)) + return 0; const long pos = std::ftell(fp); - std::fseek(fp, 0, SEEK_END); + if (pos < 0 || std::fseek(fp, 0, SEEK_END) != 0) + return 0; const long size = std::ftell(fp); - std::fseek(fp, 0, SEEK_SET); + if (size < 0 || std::fseek(fp, 0, SEEK_SET) != 0) + { + std::fseek(fp, pos, SEEK_SET); + return 0; + } std::vector data(size > 0 ? size : 0); const size_t got = data.empty() ? 0 : std::fread(data.data(), 1, data.size(), fp); std::fseek(fp, pos, SEEK_SET); diff --git a/tests/port/port_utils_test.cpp b/tests/port/port_utils_test.cpp index 2b7c9031..9348e681 100644 --- a/tests/port/port_utils_test.cpp +++ b/tests/port/port_utils_test.cpp @@ -111,6 +111,13 @@ int main() check(GetHFILECRC32(fp) == 0xCBF43926u && ftell(fp) == 4, "GetHFILECRC32 whole file, position kept"); fclose(fp); check(GetFileCRC32("/nonexistent/mt_crc32") == 0 && GetFileSize("/nonexistent/mt_crc32") == 0, "CRC32 missing file"); + check(GetFileCRC32("/tmp") == 0, "CRC32 directory rejected without allocation"); + fp = fopen("/tmp", "rb"); + if (fp) { + check(GetHFILECRC32(fp) == 0, "CRC32 directory handle rejected"); + fclose(fp); + } + check(GetHFILECRC32(nullptr) == 0, "CRC32 null handle rejected"); remove(path); }