Nikke:胜利女神Spine提取与查看

@Pelom  March 31, 2022

前言

前几天Nikke进行了一测,惊悉没有资格的也能挤进去,可是还是去晚了
既然玩不到就只能拆包了
先是到处找包,有人发了之后却发现资源不全,只好再去找。资源提取完又发现spine官方提供的运行库有bug,琢磨了半天才解决

预先准备

  • UnityLive2DExtractor.v0.16.21(注意版本)

步骤

  1. 打开手机或模拟器,spine文件位于Android/data/com.proximabeta.nikke/
  2. 打开AssetStudio,导入文件,Filter Type选择TextAssetTexture2D类型,spine文件命名为c+编号(如c010
  3. 去掉多余的后缀.asset,部分贴图文件的大小需要缩放至与.atlas中描述的相同

问题

贴图放缩

之前使用PIL库中的resize()进行放缩,语法为

img = img.resize((width, height), Image.ANTIALIAS)

其中Image.ANTIALIAS是最高质量的参数
但是产生的结果却不尽人意,于是更换为opencv库,语法为

img = cv2.resize(img, (width, height), cv2.INTER_CUBIC)

其中cv2.INTER_CUBIC是三次样条插值的参数

运行库bug

接入运行库后,加载部分角色时出现报错

Error: Region not found in atlas: 00/→ᅠネ↓ンᄡ↓ヨᄡ 125 (mesh attachment: 00/→ᅠネ↓ンᄡ↓ヨᄡ 125)

初步推测其中乱码部分为运行库从二进制文件.skel解码韩文字符时出现的错误
经过检查,发现在运行库中有这么一个用于读取字符的函数:

readString() {
    let byteCount = this.readInt(true);
    switch (byteCount) {
        case 0:
            return null;
        case 1:
            return "";
    }
    byteCount--;
    let chars = "";
    let charCount = 0;
    for (let i = 0; i < byteCount; ) {
        let b = this.readByte();
        switch (b >> 4) {
            case 12:
            case 13:
                chars += String.fromCharCode((b & 31) << 6 | this.readByte() & 63);
                i += 2;
                break;
            case 14:
                chars += String.fromCharCode((b & 15) << 12 | (this.readByte() & 63) << 6 | this.readByte() & 63);
                i += 3;
                break;
            default:
                chars += String.fromCharCode(b);
                i++;
        }
    }
    return chars;
}

.skel中的字符采取UTF-8编码,而函数String.fromCharCode()使用的是Unicode字符集,因此运行库在解码之前进行了转换

从UTF-8到Unicode

每个Unicode字符占两个字节,而对于UTF-8编码,每个字符的长度可能不同
具体规则为:

  1. 对于占一个字节的字符,其UTF-8编码与其ASCII码相同(0xxxxxxx
  2. 对于占n个字节的字符,第一个字节的前n位设为1,第n+1位设为0,后面字节的前两位都设为10;这n个字节的其余空位填充该字符Unicode码,高位用0补足。

例如中文或韩文字符,需要使用3字节的UTF-8编码,形式为:1110xxxx 10xxxxxx 10xxxxxx
在上述函数中读取第一个字节时,b >> 4值为1110,也就是14,按理说应该进入了正确的case语句
但在调试时却发现b >> 4的值为-2,也就是11111110,说明在readByte()读成了负数,进而导致后面全部进入default语句

解决方案:将readByte()替换为readUnsignedByte()

锯齿

在浏览器进行查看时出现了锯齿现象,相比使用SkeletonViewer查看时较为明显,在头发等部位尤为严重
此问题与放缩后的插值算法有关

解决方案:将<canvas>widthheight属性值扩大至2倍,再用CSS控制其实际大小,即:

canvas = document.getElementById('canvas');
canvas.width = window.innerWidth * 2;
canvas.height = window.innerHeight * 2;
canvas{
    position: absolute;
    width: 100%;
    height: 100%;
}

添加新评论