1429 words
7 minutes
记一次脑洞大开但是一无所获的探索

最近bot上新了“查看谱面”和“查询音乐”,然后就看到群里有人用可怜BOT的“预览”播放歌曲的预览段

ss

感觉这个功能会更有用一些,所以就打算尝试一下做这个功能。

然后就去翻游戏软体里面有没有相关文件存储了歌曲预览段的开始时间和结束时间,找了谱面文件那一侧的文件,然而并没有找到。

秉承着数据一定有地方存储这个想法,我又找到了SoundData文件夹里面的acb文件和awb文件

ss

我看到熟悉的「非常用格式」「双文件」的存储方式,我聪明的小脑瓜只花费了0.114秒就意识到这一定是一个加密的「元信息加数据」文件组合。于是就去搜如何解析这两个文件。

在使用各种工具解析之后,得到的却是两个相同大小的加密文件

ss

这显然就是两个相同的音频文件啊!尽管如此,这两个文件的文件名还是让我坚信,预览段的元信息一定就存在这个acb文件中!

然后我再去问了神奇的LLM,给出的代码是一个都跑不通,全都依赖了一个叫acbpy的库,然而这个库是完全的不存在,,,,,,

虽然没有成功得到答案,但至少找到了方向

既然用工具和代码解析不成功,那我就去解析raw data!

于是我(让cursor)写了个简单的脚本去解析这个acb文件,提取出它里面的所有数值,然后我再去人工查询那首歌的预览段位置,根据这个数字去搜索存储该预览段位置的文件位置。然而,虽然解析出了很多神秘的数值,但却并没有发现任何与实际预览段匹配的时间数值。到这里似乎只能放弃解析acb文件这条路了

既然这条路堵死了,我又转向了游戏代码上。随后便去搜索使用到acb文件的代码,看到有一个叫PreviewPlay的函数,调用了一个LoadCueSheet的函数使用了acb文件,于是便打算探索它怎么用读到的文件,然后看到了调用了SoundManager.Play这个函数,从名字上也是很容易想到这是个播放器,播放的就是这个预览段。然而,在调用这个功能的时候,它传的入参startTime是0,WTF?那播放器为啥播的是预览段而不是一整首歌?

抱着一种怀疑的心态,我对这段代码插了桩,试图在运行的时候打印出相关的调试信息

ss

奇怪了,竟然还真是调用了这个函数的,那为啥呢?

然后想起前面那两个解析出来的加密文件,我又想起他这里cueSheet是1,就是播放的是00001_Preview吧!但是为什么解析出来的预览段是一个跟Play一样的文件呢?

我仍然不理解,所以我继续插桩,想继续打印一些数据出来再下定论。得到了更多的信息

ss

再次发现Play和Preview并无任何差别,只有length上有区别,但是又很奇怪,为什么preview的length是-1呢?

我继续探索,也并没有找到什么新的证据,也没找到更多的可以插桩获得新信息的点,抱着碰壁就应该换路子的想法,我还是从“社会工程学”的角度入手了

在去联系了可怜BOT的开发者之后,他给我发了一个专栏(这个专栏甚至是之前我们在做谱面猜歌提取mv资源的时候,鸭鸭发给我看过的,当时直接跳过了这部分去看了mv资源相关的部分)。最终查到了sbga在两个神秘的位置用十六进制存下了preview段的起始和结束时间

ss

看到这里的那一刻,无语充斥了我的情绪,不知道sbga(或者是这个音频中间件)费尽心思加密这些不那么重要的元信息的意义是什么,那个Play函数中始终为0的startTime参数的意义又是什么

Anyway,不管用的是什么方式,总之是找到了答案,所以也挺感谢可怜BOT的开发者的,虽然人家粉丝很多,可怜BOT用的人也比我们多,但还是很耐心地给我解释了这个过程,或许这就是开源精神的伟大之处吧

虽然这次探索可以说是一无所获,但我却不觉得这是一件浪费时间的事,就算不说潜在的逆向工程的积累,单是探索过程中自然迸发的想法与解决方案就可以视作大脑的活跃剂了

但是,能花时间去做探索这件事情却很艰难,因为这学期的事情非常多,时间确实也变得更加宝贵,在用了七八个小时探索之后,我就已经决定如果当天晚上睡前找不到&可怜BOT不理我的话,我就放弃了。这也确实是无奈之举,希望以后能有更多可以自由探索的quota吧

热衷于探索的天性不应被无用的琐事扼杀

记一次脑洞大开但是一无所获的探索
https://cainithm.tech/posts/explore-preview-time/
Author
ssgXD
Published at
2025-11-16
License
CC BY-NC-SA 4.0