「更新」:用dsv4.1f向安卓端移植了一个Galgame(为Mali GPU优化适配立绘解码)

(2026-10-06更新)

初步移植好游戏后,我粗略看了开头场景感觉没问题就以为万事大吉。结果今天认真上手玩了之后发现:人物立绘哪儿去了?怎么只有背景图! :angry:火急火燎地打开PC版一看,确认是移植版出现了问题:

(PC版场景人物都是动态立绘,身体会摇动、会眨眼睛、说话会动嘴巴)

图片示例

(移植版背景图正常,但什么立绘都看不到;这里出现的色块是debug引入的,原版没有)

图片示例

因为Codebuddy免费额度用光,这回换上QoderCN的免费Qwen-3.8-Flash。Qoder官方本来说9月30号就截止Qwen-3.8-Flash免费调用活动,结果到了10月又延期,且没明说截止日期。本着不用白不用的心态,迅速下好然后破限使用 :laughing:

Qwen-3.8-Flash排查了好几轮,确认不是引擎脚本的问题,而是当前使用手机的Mali GPU不支持硬件解码立绘。具体来说:

立绘 .psb 里内嵌的是 BC7 / DXT5 压缩贴图,而这台机器的 GPU(天玑 9400+ / Mali-G925-Immortalis)的 GLES 驱动只支持 ASTC 和 ETC1,没有 s3tc/bptc/rgtc。
官方安卓 libartemis.so 又不做任何软解,背景/CG 是普通 PNG,引擎自己解码后上传 RGBA 照常显示,这一条误导AI,导致浪费了大量排查时间。

排除掉的错误方向

game.os/trueos 组合、ex.progressos、
vsync 首帧强制推进、禁用 emote.cacheinit() 的 windows 预加载分支、
createEmoteLayer progress=true、以及怀疑 so 不是 emote 版(md5 比对确认是)。

为什么不换引擎

有个开源的 artemis-compat(GPL-3)实测对象正是本作,
且把 BC7/DXT5 做了 CPU 软解,能显示立绘图。但它的头发/胸部惯性摇动不模拟
(setHairScale/setBustScale 只存值 + 打日志),而本作重度依赖这套物理。
换过去就是个不会摇的半成品,对不起移植折腾这么久的工夫,不可接受 :angry:

方案评估

用工具把真实图集解出来统计:

立绘图集里alpha==0 的像素占 62%,所以 PSB 的 RL 游程压缩收益极大——
RGBA8 + RL 实测 1.05 B/px(体积几乎不变还保 8 位精度),
RGBA4444 + RL 只有 0.24 B/px。但两个前提都不成立:本作是 spec="win" 图集布局,
compress 字段只存在于 krkr 布局的 icon 上,官方资产里图集从来没用过它;
而且 pf8 包级零压缩(4141 个条目的存储间隙全为 0),打包时也压不回去。
裸转 RGBA4444 则是 2448 MB → 4.78 GB,alpha 还只剩 16 级。

最终方案:不碰资源,在 GL 边界拦截。

APK 带一个 64 KB 源码的libarkhook.so,在 Application.onCreate 抢跑,手写 dl_iterate_phdr 解析并覆写 libartemis.so 的 GOT 槽(这些 GL 入口都是 UND 动态符号,走自己的 PLT),把 BC7/DXT5 在 CPU 上解成 RGBA8 再上传。包体积一点不涨、psb/pfs 一字未改。同时伪造 glGetString(GL_EXTENSIONS) 的返回值,把s3tc/dxt1/bptc 三个名字追加进去,引擎开始调用

性能优化

直接看方案对比:

4096×4096 那张 渲染线程实际阻塞
直接软解 294 ms 294 ms(丢 18 帧)
多核并行 61.5 ms 95 ms(解码 61.5 + 上传 33.6)
后台解码 + glClear 前 pump 61.5 ms 0.72 ms
  • 并行:BC7 的 4×4 block 彼此独立,按 block 行用原子计数器抢任务就行,
    7 个 worker + 渲染线程自己 = 4.8 倍。输出缓冲从 malloc 换成
    mmap + MADV_HUGEPAGE,因为 67 MB 首次触碰要上万次 4 KB page fault。
  • 异步:动手前先量了「两次 glClear 之间最多创建几张压缩纹理」,
    实测最多 2 张、15 秒内 2400 次 clear,所以拿 glClear 当上传 pump 点
    最多只延迟一帧。这一步让渲染线程从 95 ms 掉到 0.72 ms。

异步有三个不踩不知道的坑:

    1. 源数据必须自己拷一份(调用返回后引擎那块内存随时可能被复用,后台再读就花屏);
    1. 提交时要先传一个 1×1 全透明像素占位,
      否则纹理不完整、采样会得到不透明黑,角色位置变黑块;
    1. pump 时必须临时切回提交时那个纹理对象,因为那时引擎早就绑了别的纹理。

这么一番折腾之后,总算是能愉快地玩耍了:


原文分割线


用 dsv4.1f 向安卓端移植了PC 版《アマカノ3》(约 8 GB),原生安卓手机运行,非 Winlator 兼容层,拿引擎官方的 Android 运行库自己改包数据。

(注:这个游戏官方存在安卓版,我仔细搜了一下也看到了分享。但考虑到我是第一次尝试安卓移植,积累经验比单纯拥有安卓版游戏更重要,所以最后还是进行了自主移植尝试)

工具:DeepSeek-V4.1-Flash+Codebuddy(免费额度)。脚本、逆向、调试由AI来做,我负责决策、真机验证。
文中没有带任何游戏文件,因为我的小水管上传只有2MB/s,完整文件有8GB多,上传太慢了


成果

  • 手机:vivo V2507A(天玑 9400+ / Android 16 / arm64-v8a / 12GB 内存)
  • 状态:标题、剧情、立绘、对话框全部正常,底部按钮栏 + 顶部工具条完整。(顶部工具条是PC版就有的原生大按钮,在触摸屏上直接就能用,功能完全,非常舒适,不用费劲去按下面的小按钮。)
  • 顺便把官方 1.0.2 升级补丁打了上去(存档页从 15 槽变 25 槽、多了「返回上一选项」按钮)
图片示例

APP图标使用官方海报截取女主头像,deepseek进行适配,效果很好,最终使用B方案:

图片示例


主要流程

先侦察手机规格

用 Termux(免 root)把硬件信息全采了一遍:CPU、GPU、ABI、内存、存储、屏幕、可用工具链。

关键结论:

  1. ro.product.cpu.abilist 只有 arm64-v8a —— 纯 64 位设备,跑不了任何 32 位原生库。
  2. 有 OpenGL ES 3.x / Vulkan,12GB 内存,存储够。

参数看不懂没关系 :melting_face: 先摸清机器规格,让AI定方案就行了


判定引擎

打开游戏目录,看到 Amakano3.pfs 的扩展名后缀就知道引擎了:Artemis Engine(iMel 系的引擎)。

目前主要的 Galgame 移植现状:

引擎 安卓方案 体验
KiriKiri / KAG Kirikiroid2(原生) 好
NScripter ONScripter Plus(原生) 好
Artemis 官方就有 Android 运行库 最好
Siglus(Key 系)、YU-RIS 等 只能 Winlator(兼容层) 折腾、耗电、翻车多

Artemis 有官方 Android 运行库,所以路线定为:拿官方运行库工程 → 换上自己的包名和图标 → 把 PC 版数据改造成安卓系统认识的格式。

折磨到死的调试

换上工程、塞进数据、装到手机上——标题画面出来了,挺激动。然后进入主菜单就崩了:

  • 字体被截断、各选项挤成一团
  • 剧情页底部那一整排按钮栏(Skip/Auto/Save/Load/Log/Config…)直接消失
  • 没有虚拟按键,只能单击
图片示例

Deepseek第一反应是"图片没按屏幕缩放",于是花了大力气去改图片尺寸、改分辨率——全是白费。因为方向从一开始就错了。把包里的脚本全解出来读,终于找到真正的根因:

-- system/adv/adv.lua:216
function reload_ui()
    if game.os ~= "windows" then          -- ← 安卓时这一段是空的!
    elseif flg.ui then ...
    else init_adv_btn() flip()            -- 只有 windows 才建立 ADV 底部按钮栏
    end
end

安卓下这段直接跳过,按钮栏从来没被创建过。再查资源:PC 包里只有 pc/ja 一套 UI,而 Android 版的表里指向 pa/、_hd/、_and/ 这些目录——PC 包里根本不存在。也就是说,官方 Android 版的数据包和 PC 版是两套,手上只有 PC 版,只能手动改——不修 android 表,而是让游戏以为自己跑在 Windows 上。

另外还顺手关掉了两个 Windows 专属弹窗(存档位数校验、旧系统版本检查),它们在安卓上会误报。

game.os 一变,加载的表也变了,Windows 表里写着 blur_path="pc/"、blur_exp=".hlsl"——HLSL 是 DirectX 的着色器,安卓的 GLES 根本编译不了。改回 mb/ + .glsl 。顺带发现原版 shader 里有一行非法写法:

vec4 fore = texture2D(...);
fore = 0.0;        // ← vec4 赋 float,非法

官方 1.0.1 才修(改成逐分量 fore.r/g/b = 0.0),而且只修了 blur_k,漏了 blur_kx/blur_ky,这两个得自己补。

PC 上"模拟真机"

安卓实机调试一次要传 1.7GB,太慢。于是在 PC 上构造了和手机等价的环境:

game.os   = "windows"    ← 强制
game.trueos = "android"  ← 模拟真机

这样 PC 上能跑、能截图、能快速迭代,而且 lua 层的逻辑组合和真机完全一致。

升级官方 1.0.2补丁

折腾好本体后,没想到升级补丁还折腾了大半天。官方给了升级补丁 Amakano3.pfs.010 / .030。查官方文档才知道:

同目录下的 root.pfs.NNN(000~999)是补丁,优先于 root.pfs。

所以改名丢进数据目录就能生效——机制本身很干净。然后Deepseek想当然地自己重建了一个补丁包(把两个官方补丁合并成一个,顺便改掉里面几处路径)。

结果:黑屏 → 崩溃。折腾了很久。

最后逐字节对比才发现——.pfs 的索引在"条目表"之后还有一段尾部结构:

[u32 count+1] + (count+1)×[u32 值][u32 0] + [u32 条目区末尾]

重建索引时Deepseek整个把它漏掉了,引擎一读就 SIGSEGV。排查了半天才搞出来。


图标

工程模板自带的是模板作者的示例图,不是本作素材。我找了官方海报,让DeepSeek裁了女主角头部,做成标准 adaptive icon:

  • foreground:透明底,头像居中占画布 90%
  • background:取裁切区四角平均色(暖米色)
  • legacy 的方形 / 圆形图标也都生成,5 档 dpi 全覆盖

总结:

AI干得好的:

  • 逆向文件格式、写解析/加密脚本
  • 在几千个文件里定位"哪一行决定了行为"
  • 快速生成大量候选方案、反复试错
  • 跨语言/跨工具链的琐碎工作(Lua / Python / PowerShell / adb / Gradle)

AI干得不好的:

方向必须要人把关,不然会在错路上一路狂奔;有过好多次,活没干完,Deepseek直接嚷嚷说"看起来好了",然后停工,这种时候必须拿个鞭子抽他他才干活 :enraged_face:

遗留

  • OP 影片是 .wmv,安卓不解码,不播。暂时没改

因为codebuddy今天deepseek的免费额度用完了:joy:

写在最后

AI 把这类"脏活累活密集"的移植工作门槛拉低了很多,但仍然需要人来判断拿主意。希望各位大胆尝试,能把自己喜欢的 Galgame 搬上手机。

17 个赞

太强了佬友,终于可以不用可恶的模拟器玩了 :laughing:

1 个赞

不需要专门去破限吗佬,最近用grok感觉都不怎么听我话了。考虑换国模了

我嘞甜蜜女友,入坑作,好玩爱玩 :tieba_003:

4 个赞

想问一下佬codebuddy好用吗,我到时也试一下

我的codebuddy是破限过的,用论坛里佬给的脚本和系统提示词

和workbuddy共享免费的额度,反正免费,炸了我都说好。codebuddy用cli要改造一下。一天大概有70、80M dsv4.1f的额度,轻度用够了

先学习学习感谢佬分享信息以后看看

严肃学习中,必须先破限才能做么?

佬能告知一下站内哪位大佬的脚本和提示词嘛,我最近也在研究破限思路

看成了deepseek向安卓端植入了一个Glagame​:heart_eyes:

我跟你的方向正好相反,手机太小,我要移植到PC里XD

1 个赞

大佬,这个是真的强,大赞 :+1:

2 个赞

没试过不破会怎么样,估计大概率以付费资源为由拒绝

我之前在这个帖子里问过: 求教codebuddy如何破甲破限?

移植到手机里不就是为了躺着起飞嘛 :laughing:

有没有想过投屏在墙上,我觉得你可以更上一层楼XD