《星界之梯》查询网页开发记录
之前玩一款港游 《星界之梯》 就一直想把里面的立绘找出来做桌面,所以一边玩一遍寻思解包的方法。
游戏分析
要拿到资源,首先要确定游戏的资源在哪。我玩游戏的时候发现一个现象,就是在游戏副本里面,断开网络,并不影响副本里面的操作,只有通关把网络打开,就能 connect 到服务器领取奖励。
也就是说,游戏大部分的运行逻辑都在手机端完成的,服务端只是对手机端的充值,副本胜利判定,和账户中关卡开放程度进行管理。
那手机端的游戏数据会放在哪里呢?不应该在 apk 里面,因为图片,音效,运行逻辑数据太大,只能是需要的时候读取。而为了 apk 不卡顿,这些数据不应该存放在外置 SD 卡。而系统的 app 的 root 路径是需要管理员权限的,游戏如果把数据放那里,那没有 Root 用户怎么安装游戏呢?所以,游戏数据包应该在手机内存的Android/data/
文件夹下。
果然,我在Android/data/
文件夹下的com.firedog
里面找到了数据。以前要是更新,还会看到*.obb
文件在Android/obb/
文件夹下。
游戏包解析
上面说到的*.obb
文件,其实是 Android 游戏数据包。以前玩过手机 QQ 美化,所以知道 Android 上的特殊格式一般可以解压打开。而*.obb
解压得到的就是Android/data/
下的数据。
接下来看解压后的数据,里面有 json 文件,unity3d 的文件。json 文件是一种对象数据文件,所以立绘是不会在这里的。而且我用 sublime 搜索没发现有任何 png 或者 jpg 的文件引用。先放一边,我们要的立绘不在这。接下来动手拆 unity3d 数据了。
拆包
没做过游戏,不太清楚 unity3d 的包怎么打开,上百度查了一圈,找到了 disunity 和 UnityStudio。disunity 是一个命令行反编译 unity3d 工具,而 UnityStudio 是 win 上使用的反编译 unity3d 工具。我用的是 Linux,所以毫不犹豫用了 disunity。说起来这款工具的作者在 2016 年 1 月 18 号停更了 Java 版本,2017 年 08 月 13 日我翻了一下发现有个 Python 版本的昨天刚刚更新。把里面的版本试了一圈,发现 3.x 系列才能正确的反编译出东西,4.x 貌似还是有点小问题,5.0 根本不能用好吗。
disunity 3.x 使用方法:
1 |
|
图片格式批量转换
得到了很多文件夹,里面有音频、纯文本数据和后缀为tga
的文件,我要的 png 呢?难道是这些tga
后缀文件?不知道就百度呗,果然这个 tga 也是图片资源,开始我电脑不识别呀,还是转换成大众点的格式比较好,上 github 找轮子,果然还是有人写过的,tga2png 有例子,可是我需要批量呀,既然是 js 模块,我用 node 写个批量处理好了。
1 |
|
这样就拿到立绘了。
不过有个问题,obb 里面有些资源不全呀,游戏没出错。应该是有部分资源在 apk 里面。动手反编译 apk。
反编译 apk
这里用到了一个工具 apktool,官网有说明的。
1 |
|
还有一种方法比较暴力,直接解压,用 dex2jar 转换成 jar 用 jd-gui 看源码。
用纯 React 写一个 AS 查询网页
这个游戏的行使有 1000 多个,有时候搭配起来还是很麻烦的,所以就有了写个查询的网页。
星界之梯的数据其实不符合我操作的预期的,例如:
- 有些有名字的行使,并没有发布出来,也就是和立绘没有一一对应。
- 行使的数据分散到不同的 json 文件,而文件对应关系也不理想。
还有些是前端的问题:
- React 没有双向绑定,要点击一个头像,在侧边栏显示详情,组件关系怎么解决?
- 图片太多,全部加载完的话会需要很长时间,有什么好的方案?
目前这四个问题需要我一步一步解决。
json 数据处理
数据如果没法对应起来,前端做的事就很麻烦了。为了解决这个问题,我决定把所有需要的数据写到一个 json 文件中,这样子处理起来,就容易许多了。
整合完 json,接下来就是让立绘和数据一一对应。头像的文件和立绘文件是一一对应,所以只有检测头像是否存在,也就能知道立绘是否存在了。做法是读取头像文件夹存在的文件,把文件名转换成数值,保存到数组中,需要的时候拼接字符串即可。但是这个数组不能和整合好的 json 文件对应,所以 json 文件不能引用这个数组。怎么处理?
其实可以这么做,把数组作为中间数据,用来规范命名资源文件,到时候 json 文件只需要数值拼接成字符串就能访问到准确的资源文件。
React 通信问题
React 是单向数据的,常用的有props
和state
,props
是父组件给子组件传值用的信使,state
是组件内部的状态。没有子组件向父组件传值的方法呀。官方给出的答案是状态提升。
状态提升,就是把state
统一放在父组价中管理,父组件通过props
把子组件需要的参数传给子组件。JavaScript 有个神技能,就是一等公民,所以我们可以把一个函数传递给子组件,子组件拿到参数,里面有需要的值和一个父组件中定义的函数,子组件把参数拿来给自己用,当有事触发,可以把触发的信息具体是什么塞到父组件给的函数里返回给父组件。这样就能实现子组件向父组件通信了。
而我想在左边栏显示详情,右边是全部行使的头像,点击头像,左边栏显示对应的行使详情。状态提升就能做到。
首先定义一个函数传递给右边栏组件,右边栏里面的的头像都要自己唯一的 ID,同时有个点击触发 handler 传递 id 给父组件的函数返回给父组件,父组件拿到 id 就传递给左边栏显示。左右边栏是兄弟关系。
图片太多的问题
行使头像还好,可是行使立绘就呵呵哒了呀。怎么样才能让行使立绘需要的时候才加载呢?问了慕课网的鞭挞师,答案是 CSS 引入图片,就这么简单。
界面美化
虽然东西是写出来了,可是我感觉左边栏的滚动条好丑呀,可是隐藏滚动调,要是左边的内容太多,那就看不到了。….. 关键时刻找度娘,找到了个可行方案,就是写两个 div,上面的 div 比下面的 div 宽度小,就把下面的 div 的滚动条给盖住了,看不到的。而下面的 div 还是能滚动。