三国杀移动版拆包教程

这篇文章介绍手杀的拆包方法。都是批量完成的。确保某一步完成之后再继续下一步。消耗的时间可能会相当长。

下载三国杀移动版

点击这里下载三国杀移动版

下载必需软件

对于ArchLinux用户,直接yay -S pvr-tex-tool-bin imagemagick

对于Windows用户,因为官方下载PVRTexTool要注册,所以只能去软件园下载了,自己百度解决。

ImageMagick可以在官网下载,当然就那破网速我还是建议去软件园下载。

此外Windows还需要额外准备一个解压缩软件,我推荐7zip。

记得安装Python。

解包三国杀移动版

把下载下来的apk文件后缀名改成.zip(其实也不用改),然后把assets文件夹解压缩出来。素材都在这里面。

去解压好的assets文件夹下面,把res文件夹单独拿出来。素材都在这里面。(再放送)

res下面有这些:

  1. audios文件夹:音频素材,直接可以听。
  2. font:图片素材的字体
  3. fonts:ttf字体
  4. ui:里面是ui素材。

.pvr.ccz@alpha转.pvr.ccz

我们可以注意到里面有许多.pvr.ccz文件。ccz文件就是pvr文件的压缩包形式,所以我们先把ccz文件转为pvr文件。注意到有.pvr.ccz@alpha结尾的,这个其实也是.pvr.ccz文件,只是在文件名后面加了个alpha。所以我们先把所有的<name>.pvr.ccz@alpha改成<name>_alpha.pvr.ccz形式,方便下一步操作。

由于pvr.ccz文件实在是太多了,手动重名极其折磨人,我们需要写个自动化脚本。我只会shell脚本,Windows用户请装个git bash用来跑shell脚本。

假设你在手刹素材目录,也就是res目录下面。现在请新建一个文本文件,然后改名为rename_alpha.sh(txt拓展名也要改掉)。然后用文本编辑器打开它并粘贴以下内容:

for l in `find -name '*@alpha'`
do
  mv $l ${l%*.pvr.ccz@alpha}"_alpha.pvr.ccz";
done

保存。在文件夹里面右键,点“Git Bash Here”,然后输入命令bash rename_alpha.sh,回车。现在重命名工作已经完成了。

.pvr.ccz转.pvr

.pvr.ccz就是一个pvr文件用zlib压缩了加个16字节文件头而已。我们写个python脚本就能把一个.pvr.ccz转成.pvr文件了。

现在新建一个文本文件,重命名为ccz2pvr.py。然后打开并粘贴以下内容:

import zlib
import sys

ccz = sys.argv[1]
pvr = ccz[0:len(ccz) - 4]
ccz = open(ccz, "rb").read()
pvr = open(pvr, "wb")
pvr.write(zlib.decompress(ccz[16:len(ccz)]))
pvr.close()

保存。然后在git bash中输入命令python,回车,如果看到的是这样:

Python 3.10.1 (main, Dec 18 2021, 23:53:45) [GCC 11.1.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 

反正不是啥报错之类的,说明python正常安装,按Ctrl+D重新回到bash里面。如果是报错信息,请确认python是否正常安装。

然后我们创建ccz2pvr.sh,输入以下内容:

for l in `find -name '*.ccz'`
do
  python ccz2pvr.py $l
  rm $l
done

保存,运行它。现在ccz文件都被转为pvr了。

pvr转png

现在该我们PVRTexTool上场了。新建文件pvr2png.sh,输入:

for l in `find -name '*.pvr'`
do
  pvr-tex-tool -ics sRGB -i $l -d ${l%.pvr}.png
  rm $l
  rm ${l%.pvr}.Out.pvr
done

如果你是Windows用户,情况又稍微不同。需要输入的命令改为

for l in `find -name '*.pvr'`
do
  C:/PowerVR/GraphicsSDK/PVRTexTool/CLI/Windows_x86_64/PVRTexToolCLI.exe -ics sRGB -i $l -d ${l%.pvr}.png
  rm $l
  rm ${l%.pvr}.Out.pvr
done

那一长串自然是你的PVRTexToolCLI.exe文件的路径了。注意了,路径里面的反斜杠“\”记得改成斜杠"/"。

保存,运行命令。现在pvr文件都变成png了。

合并RGB通道和alpha通道

我们可以注意到拿出来的png文件有些是纯黑底的,有些是黑白的。那些黑白的是alpha通道信息。现在我们把这两个合并起来,得到真正可用的透明底的png图片。

这里要用到ImageMagick。

新建alpha_png.sh,输入以下内容:

for l in `find -name '*_alpha.png'`
do
  n=${l%*_alpha.png}.png
  convert $n $l -compose copy-opacity -composite $n
  rm $l
done

对于Windows用户,输入这些:

for l in `find -name '*_alpha.png'`
do
  n=${l%*_alpha.png}.png
  <你的ImageMagick安装目录>/convert.exe $n $l -compose copy-opacity -composite $n
  rm $l
done

注意目录里面的反斜杠要改成斜杠哦。保存,运行。

plist拆图

可以看到有些图是很多小图拼成的大图。有些大图有同名.plist文件,另一些有同名.atlas文件。我们先来把plist图拆开成小图。

这里也是找了个plist拆图的python脚本。我们创建一个plist_split.py的文件,输入下面的内容:

import plistlib
import os
import sys
from PIL import Image


def export_image(img, pathname, item):
    # 去透明后的子图矩形
    x, y, w, h = tuple(map(int, item['frame']))
    # 子图原始大小
    size = tuple(map(int, item['sourceSize']))
    # 子图在原始图片中的偏移
    ox, oy, _, _ = tuple(map(int, item['sourceColorRect']))

    # 获取子图左上角,右下角
    if item['rotated']:
        box = (x, y, x + h, y + w)
    else:
        box = (x, y, x + w, y + h)

    # 使用原始大小创建图像,全透明
    image = Image.new('RGBA', size, (0, 0, 0, 0))
    # 从图集中裁剪出子图
    sprite = img.crop(box)

    # rotated纹理旋转90度
    if item['rotated']:
        sprite = sprite.transpose(Image.ROTATE_90)

    # 粘贴子图,设置偏移
    image.paste(sprite, (ox, oy))

    # 保存到文件
    print('保存文件:%s' % pathname)
    image.save(pathname, 'png')

# 获取 frame 参数
def get_frame(frame):
    result = {}
    if frame['frame']:
        result['frame'] = frame['frame'].replace('}', '').replace('{', '').split(',')
        result['sourceSize'] = frame['sourceSize'].replace('}', '').replace('{', '').split(',')
        result['sourceColorRect'] = frame['sourceColorRect'].replace('}', '').replace('{', '').split(',')
        result['rotated'] = frame['rotated']
    return result

# 生成图片
def gen_image(file_name, export_path):
    # 检查文件是否存在
    plist = file_name + '.plist'
    if not os.path.exists(plist):
        print('plist文件【%s】不存在!请检查' % plist)
        return

    png = file_name + '.png'
    if not os.path.exists(png):
        print('png文件【%s】不存在!请检查' % plist)
        return

    # 检查导出目录
    if not os.path.exists(export_path):
        try:
            os.mkdir(export_path)
        except Exception as e:
            print(e)
            return

    # 使用plistlib库加载 plist 文件
    lp = plistlib.load(open(plist, 'rb'))
    # 加载 png 图片文件
    img = Image.open(file_name + '.png')

    # 读取所有小图数据
    frames = lp['frames']
    for key in frames:
        item = get_frame(frames[key])
        export_image(img, os.path.join(export_path, key), item)


if __name__ == '__main__':
    if len(sys.argv) == 3:
        filename = sys.argv[1]
        exportPath = sys.argv[2]
        gen_image(filename, exportPath)

然后新建plist_split.sh,输入:

for l in `find -name '*.plist'`; do
    pic=${l%%.plist}
    python plist_split.py $pic $pic'_plist'
    rm $pic'.png'
    rm $pic'.plist'
done

atlas拆图

新建atlas_split.py,输入

import os
import sys
import re
from PIL import Image

xymatcher = re.compile("\s*xy: (\d+),\s*(\d+)")
sizematcher = re.compile("\s*size: (\d+),\s*(\d+)")
indexmatcher = re.compile("\s*index: ([\-\d]+)")
origmatcher = re.compile("\s*orig: (\d+),\s*(\d+)")
offsetmatcher = re.compile("\s*offset: (\d+),\s*(\d+)")

def write_image(name, sprites, atlas_file, out_dir):
    """Get next texture info from the atlas and write it to the
    directory"""
    rotate = atlas_file.readline() #ignore rotate
    if ("true" in rotate):
        rotate = True
    xy = xymatcher.match(atlas_file.readline())
    x = int(xy.group(1))
    y = int(xy.group(2))
    sizes = sizematcher.match(atlas_file.readline())
    width = int(sizes.group(1))
    height = int(sizes.group(2))
    #split = atlas_file.readline()
    #if "split" in split:
    #    atlas_file.readline() #ignore origin
    #atlas_file.readline() #ignore offset
    orig = origmatcher.match(atlas_file.readline())
    origw = int(orig.group(1))
    origh = int(orig.group(2))
    offset = offsetmatcher.match(atlas_file.readline())
    offsetx = int(offset.group(1))
    offsety = int(offset.group(2))
    index_match = indexmatcher.match(atlas_file.readline())
    index = index_match.group(1)
    box = x, y, x + width, y + height
    if rotate == True:
        box = x, y, x + height, y + width
    tile = sprites.crop(box)
    if rotate == True:
        tile = tile.transpose(Image.ROTATE_270)
    target = Image.new("RGBA", (origw, origh))
    target.paste(tile, (offsetx, offsety))
    name = name if index == "-1" else name + "_" + index
    #return
    out_path = os.path.join(out_dir, name + ".png")
    os.system("mkdir -p " + out_path)
    os.system("rm -r " + out_path)
    print ("Writing sprite: ", name, " to: ", out_path, " sizes: ", width, height, x, y)
    target.save(out_path, "PNG")

def split(atlas, out_dir):
    """Open's the sheet, reads each atlas item and writes it into
    the out directory"""
    #sprites = Image.open(sheet).convert("RGBA")
    atlas_file = open(atlas)
    #read first few lines
    atlas_file.readline() #blank line
    sheet = os.path.join(os.path.dirname(atlas), atlas_file.readline().rstrip("\n"))
    sprites = Image.open(sheet).convert("RGBA") #use rgba for now
    while True:
        line = atlas_file.readline()
        if line.startswith("repeat"):
            break

    while True:
        name = atlas_file.readline()
        if name:
            write_image(name.rstrip("\n"), sprites, atlas_file, out_dir)
        else:
            break


def print_usage():
    print ("Usage : python atlas_splitter.py atlas out_dir")

if len(sys.argv) < 3:
    print_usage()
elif sys.argv[1] == "--help":
    print_usage()
else:
    split(sys.argv[1], sys.argv[2])

再新建atlas_split.sh,输入

for l in `find -name "*.atlas"`
do
    mkdir ${l}_out
    python atlas_split.py $l ${l}_out
done

保存运行。

未完待续

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容

  • 用到的组件 1、通过CocoaPods安装 2、第三方类库安装 3、第三方服务 友盟社会化分享组件 友盟用户反馈 ...
    SunnyLeong阅读 14,615评论 1 180
  • 一、架构 二、框架部署 2.1 准备 准备三台虚拟机,操作系统为CentOS 7.x,每台内存至少8G以上。 步骤...
    CJ21阅读 1,104评论 0 3
  • 在你完成应用程序的beta版本后,最后会有些人去帮你测试,使你去完善应用程序……或者会有投资青睐。但是如果测试人员...
    zmp1123阅读 6,710评论 15 46
  • 安装包组成: 谈到 App 瘦身,最直接的想法莫过于分析一个安装包内部结构,了解其每一部分的来源。解压一个 ipa...
    孔雨露阅读 3,298评论 1 7
  • 转载[https://www.cnblogs.com/crstyl/p/14690895.html] 前言 项目闲...
    iLeooooo阅读 8,260评论 1 23