這篇文章介紹手殺的拆包方法。都是批量完成的。確保某一步完成之后再繼續(xù)下一步。消耗的時(shí)間可能會(huì)相當(dāng)長。
下載三國殺移動(dòng)版
點(diǎn)擊這里下載三國殺移動(dòng)版
下載必需軟件
對(duì)于ArchLinux用戶,直接yay -S pvr-tex-tool-bin imagemagick。
對(duì)于Windows用戶,因?yàn)楣俜较螺dPVRTexTool要注冊,所以只能去軟件園下載了,自己百度解決。
ImageMagick可以在官網(wǎng)下載,當(dāng)然就那破網(wǎng)速我還是建議去軟件園下載。
此外Windows還需要額外準(zhǔn)備一個(gè)解壓縮軟件,我推薦7zip。
記得安裝Python。
解包三國殺移動(dòng)版
把下載下來的apk文件后綴名改成.zip(其實(shí)也不用改),然后把a(bǔ)ssets文件夾解壓縮出來。素材都在這里面。
去解壓好的assets文件夾下面,把res文件夾單獨(dú)拿出來。素材都在這里面。(再放送)
res下面有這些:
- audios文件夾:音頻素材,直接可以聽。
- font:圖片素材的字體
- fonts:ttf字體
- ui:里面是ui素材。
.pvr.ccz@alpha轉(zhuǎn).pvr.ccz
我們可以注意到里面有許多.pvr.ccz文件。ccz文件就是pvr文件的壓縮包形式,所以我們先把ccz文件轉(zhuǎn)為pvr文件。注意到有.pvr.ccz@alpha結(jié)尾的,這個(gè)其實(shí)也是.pvr.ccz文件,只是在文件名后面加了個(gè)alpha。所以我們先把所有的<name>.pvr.ccz@alpha改成<name>_alpha.pvr.ccz形式,方便下一步操作。
由于pvr.ccz文件實(shí)在是太多了,手動(dòng)重名極其折磨人,我們需要寫個(gè)自動(dòng)化腳本。我只會(huì)shell腳本,Windows用戶請(qǐng)裝個(gè)git bash用來跑shell腳本。
假設(shè)你在手剎素材目錄,也就是res目錄下面。現(xiàn)在請(qǐng)新建一個(gè)文本文件,然后改名為rename_alpha.sh(txt拓展名也要改掉)。然后用文本編輯器打開它并粘貼以下內(nèi)容:
for l in `find -name '*@alpha'`
do
mv $l ${l%*.pvr.ccz@alpha}"_alpha.pvr.ccz";
done
保存。在文件夾里面右鍵,點(diǎn)“Git Bash Here”,然后輸入命令bash rename_alpha.sh,回車?,F(xiàn)在重命名工作已經(jīng)完成了。
.pvr.ccz轉(zhuǎn).pvr
.pvr.ccz就是一個(gè)pvr文件用zlib壓縮了加個(gè)16字節(jié)文件頭而已。我們寫個(gè)python腳本就能把一個(gè).pvr.ccz轉(zhuǎn)成.pvr文件了。
現(xiàn)在新建一個(gè)文本文件,重命名為ccz2pvr.py。然后打開并粘貼以下內(nèi)容:
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.
>>>
反正不是啥報(bào)錯(cuò)之類的,說明python正常安裝,按Ctrl+D重新回到bash里面。如果是報(bào)錯(cuò)信息,請(qǐng)確認(rèn)python是否正常安裝。
然后我們創(chuàng)建ccz2pvr.sh,輸入以下內(nèi)容:
for l in `find -name '*.ccz'`
do
python ccz2pvr.py $l
rm $l
done
保存,運(yùn)行它?,F(xiàn)在ccz文件都被轉(zhuǎn)為pvr了。
pvr轉(zhuǎn)png
現(xiàn)在該我們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文件的路徑了。注意了,路徑里面的反斜杠“\”記得改成斜杠"/"。
保存,運(yùn)行命令。現(xiàn)在pvr文件都變成png了。
合并RGB通道和alpha通道
我們可以注意到拿出來的png文件有些是純黑底的,有些是黑白的。那些黑白的是alpha通道信息。現(xiàn)在我們把這兩個(gè)合并起來,得到真正可用的透明底的png圖片。
這里要用到ImageMagick。
新建alpha_png.sh,輸入以下內(nèi)容:
for l in `find -name '*_alpha.png'`
do
n=${l%*_alpha.png}.png
convert $n $l -compose copy-opacity -composite $n
rm $l
done
對(duì)于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
注意目錄里面的反斜杠要改成斜杠哦。保存,運(yùn)行。
plist拆圖
可以看到有些圖是很多小圖拼成的大圖。有些大圖有同名.plist文件,另一些有同名.atlas文件。我們先來把plist圖拆開成小圖。
這里也是找了個(gè)plist拆圖的python腳本。我們創(chuàng)建一個(gè)plist_split.py的文件,輸入下面的內(nèi)容:
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)
# 使用原始大小創(chuàng)建圖像,全透明
image = Image.new('RGBA', size, (0, 0, 0, 0))
# 從圖集中裁剪出子圖
sprite = img.crop(box)
# rotated紋理旋轉(zhuǎn)90度
if item['rotated']:
sprite = sprite.transpose(Image.ROTATE_90)
# 粘貼子圖,設(shè)置偏移
image.paste(sprite, (ox, oy))
# 保存到文件
print('保存文件:%s' % pathname)
image.save(pathname, 'png')
# 獲取 frame 參數(shù)
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】不存在!請(qǐng)檢查' % plist)
return
png = file_name + '.png'
if not os.path.exists(png):
print('png文件【%s】不存在!請(qǐng)檢查' % plist)
return
# 檢查導(dǎo)出目錄
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')
# 讀取所有小圖數(shù)據(jù)
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
保存運(yùn)行。