对《想当个破解者?》的实操环节——以Wiz相关文件为例(下)
galgame吧
全部回复
仅看楼主
level 13
huohua_离民 楼主
前文再续,书接上一回。话说上回,我们以FAVORITE社(下称F社)的第二部作品《ウィズ アニバーサリィー》(下称Wiz)的游戏资源为切入点,初步解析了bin文件的具体格式,并使用先进生产力AI工具写好了一个封解包bin文件的工具。接下来我们开始处理比较神秘的hzc文件。
注意,根据起始资源的来源,可能会得到多种hzc文件。例如,如果我们解包steam版《Colorful World(五彩斑斓的世界)》的文件,虽然也会得到hzc,但那一种与我们接下来讨论的这一种有所不同。我们暂时先讨论原版的这一些hzc文件。以后如有机会再回过头去分析另外一种。
2025年03月01日 11点03分 1
level 13
huohua_离民 楼主
第二部分:hzc文件的处理:
我们开始吧,执行我们之前的初级解包文件,得到储存图像的hzc文件:
文件名带h的是HCG,摆出来不太好看,更何况还有个更小的文件“BG002_91b.hzc”,所以我们就以这个背景图为第一个测试的例子。首先还是拖到十六进制编辑器:
在一大堆密集的数字之前,有不少的0x00数据,所以我们先猜一手2C是正式文件内容的偏移。结合我们之前的印象:每4个字节为一个小端序整数,记录一些信息。那么看一下文件头内容:
68 7A 63 31 00 F9 15 00 20 00 00 00 4E 56 53 47
00 01 00 00 20 03 58 02 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 (78 DA ......)
等等,看看2C位置的字符:0x78 0xDA。回忆一下第四部分介绍的压缩格式zlib:“另一种常见情况是 0x78 0xDA,区别仅在于使用了最高压缩级别。”也就是说,这一个hzc文件很可能就是用zlib压缩的。
那么我们直接解压出来看看是什么情况吧:
很好,完全看不明白。虽然我才疏学浅看不明白,但说不定还有别的高手呢?直接问问DeepSeek是怎么回事吧,不过在此之前我们收集一些必要的数据。
图片分辨率:打开游戏直接截图看窗口大小,QQ截图就能看到截取区域大小。我们可以得出背景图分辨率是800×600(注意,F社的CG是可以放大两倍的,所以如果我们判断CG文件分辨率,则应该是窗口大小的2×2倍。)
图片(不含文件头)大小:wxMEdit写着呢,1440000,一点不差。
(未完待续)
2025年03月01日 11点03分 3
level 13
huohua_离民 楼主
好,把这些信息拿去给D指导看,顺便附上前256字节:
那么输出内容是文件头缺失的BMP文件,补上文件头看看情况如何:
得到的bmp图片如下所示:
确实是这个文件,但是不是上下翻转了?不要紧,稍微动动手——这点小事就不劳D指导大驾了:
def f(inputfile,outputfile,xsize,byte_per_pixel):
group_size=xsize*byte_per_pixel
with open (inputfile,’rb’) as file:
content = file.read()
groups = [content[i:i+group_size] for i in range(0, len(content), group_size)]
with open (outputfile,’wb’) as file:
for group in groups[::-1]:
file.write(group)
把开始的那个解压缩后的数据文件丢进去,然后再安上文件头,看看是什么情况:
很好,我们现在已经完成了一整个解包的程序了。
总结一下要点:将原BMP去除文件头后,倒序写入每一行字节并使用zlib最高压缩级别,然后写入hzc文件的文件头,即得到归档文件中的图片文件。
(未完待续)
2025年03月01日 11点03分 4
level 13
huohua_离民 楼主
但是在整个过程中,hzc文件头中包含的原图片相关参数至关重要:毕竟立绘文件不可能和窗口一样大吧?所以我们回过头去分析hzc文件的文件头都包含了哪些有用的信息。
回顾一下,上面的背景图的hzc文件的文件头是这样的:
68 7A 63 31 00 F9 15 00 20 00 00 00 4E 56 53 47
00 01 00 00 20 03 58 02 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00
我们知道,原本的图片分辨率为800×600 即0x0320×0x0258 ;图片大小为1440000 ,也就是0x0015f900。仔细看看刚才的hzc文件的第一行,没错,文件头之后紧跟着的四位数是小端序整数,记录了压缩前文件大小。就在这四位小端序整数的正下方,记录了图片分辨率,这里就用2字节的小端序整数了。
再学习一下BMP文件的文件头组成。这种技术问题直接找百度百科词条即可[1]。如果是54字节的文件头,通常包含以下内容:
1.2字节的“BM”(仅考虑Windows系统)
2.4字节的文件总大小(包括像素数据和文件头)
3.4字节的0x00
4.4字节的小端序像素数据绝对偏移
5.4字节的小端序位图信息头长度(我们就认为是28 00 00 00就好)
6.两个4字节的小端序整数,分别记录图片的长和宽(也就是计算分辨率)
7.2字节的“0x01 0x00”
8.2字节,记录每个像素的位数。此处我们仅需考虑0x18与0x20两种即可,前者对应RGB,后者对应RGBA。这一位的信息影响每个像素对应的字节数。换言之我们可以通过分辨率和文件大小反推出这一位的值。
9.余下的字节是后续的相关内容,不过我们全都输入0x00即可,影响不大。
背景图用的是RGB,考虑到立绘图必然会出现透明部分,立绘图应该用RGBA,是否如此?我们打开一个立绘的hzc看看:
文件头是这样的:
68 7A 63 31 58 3C 2B 00 20 00 00 00 4E 56 53 47
00 01 01 00 A2 02 1B 04 C1 00 E2 01 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00
稍微对比一下,我们可以发现,除了已知的图片大小、分辨率的变量信息,还有一些不同。0x12位置处,立绘文件是0x01而背景图是0x00;0x18到0x1B位置,立绘文件有内容而背景图没有。先计算图片数据吧。大小为0x002B3C58=2833496字节;分辨率为674×1051;像素数为708374,即每个像素使用4个字节的数据,符合RGBA。这两处不同到底哪一个负责区分图片使用RGB还是RGBA?我倾向于0x12单字节控制。不过还是不确定,换一张立绘:
0x12位置为01,0x18到0x1B位置处内容不同。作出判断:0x12位置的值确定图片是24位或32位。那么把以上信息丢给D指导吧。
那么这会儿我们写好了一个,顺带一提在input指令后加上“.strip(‘”’)”可以支持拖动文件进cmd框从而减少处理工作(毕竟某些文件名真不好打出来)。试试背景图?可以。立绘呢?有点瑕疵,但GARbro弄出来的也一个样所以可以放着不管。还有表情呢?这回报错了。检查一下原因:
0x12位置出现了0x02,是新的内容。此外还有0x20也出现了不一样的内容。还是一样,先看图片数据。图片大小0x1f 0x9a 0x40=2071104字节。长0x86宽0xA8即分辨率为134×168。那么总的像素数为22512,算它是32位图,理论大小为90048字节,这可差远了。怎么回事呢?或许我们主动修改一下图片的长或者宽?总之我们把y_size改成刚好能装下的3864 = 0x0F 0x18,试试看:
(图片过长,考虑到观感就不放了,总之这里是很多个表情差分上下搭在一起)
呃,好多个爱丽丝(Wiz的主角アリス)的头。原来是hzc里面储存了多个表情差分,所以只考虑一张图的话大小完全对不上。这里总共有23张表情差分,刚好对得上0x20位置的0x17(16+7=23)。所以我们得加一条:如果在第19个字符发现了0x02,将图片数设置为第33个字符对应的数z,然后解zlib压缩并反转好了的那些数据要平均分为z组,每一组单独加一个一样的文件头。调整完毕,试试看:
顺利完成。
2025年03月01日 11点03分 6
level 13
huohua_离民 楼主
接下来考虑一下怎么把bmp放回原来的hzc吧。不过hzc的文件头还有一小部分没解决,也就是0x18到0x1B位置的那四个字节。实际上这四个字节完全不影响我们的解包工作,那它们又是什么参数呢?我们得回到游戏实际测试了。首先我们先展示一下fvp不强制要求读bin文件的实际案例。首先是正常情况下的主界面:
然后是外置graph文件夹、文件夹内放入删去后缀名的其它图片文件(方便起见,此处替换成了上面展示过的“BG002_91b.hzc”),再次打开游戏可以发现:
很神奇对吧?我也这么觉得。总而言之就是,fvp会更优先读取文件夹内的hzc文件(当然,不能有后缀)。我们再改一改那四个未知参数看看会发生什么:
左边位置标黑的地方就是那几个参数,我随便输入了一些内容(本来是00 00 00 00),之后得到了右方的结果。可以给结论了:这个位置的数是用于标明图片在游戏中显示时的偏移位置。好消息是至此我们掌握了整个文件头的信息,坏消息是,我们不可能仅靠bmp原图直接写出合适的hzc文件。所以我们必须把原hzc文件和待替换bmp文件放在一起才能做成新的hzc文件。这里只需要用同样的思路回去封包即可,也就是①读取原hzc的文件头信息(尤其是图片偏移信息)保留固定内容②将去掉文件头的bmp先分组后各组以行为单位倒序③以zlib加密(要和原来的内容一样的话,就用最高压缩得到0x78 0xDA开头的字段)④加上hzc文件头。这几步可以直接找D指导,毕竟我们对这两个文件头的分析已经都在文章里了。当然,按理来说修改前后的bmp分辨率和图片位都不变,大小自然也相同,所以把整个文件头保留只修改下边的内容就行。
以上,虽然过程磕磕绊绊,毕竟我们掌握了如何解析普通的hzc文件、将其转为bmp格式。于是到此为止我们过完了整个游戏破解的基本过程,可喜可贺,可喜可贺。
2025年03月01日 11点03分 7
level 13
huohua_离民 楼主
附加内容:非常规hzc文件的处理:
非常规hzc文件,是指不使用zlib压缩的那些图片资源文件。当然这就超出Wiz这个示例了,目前我只在steam版本的《五彩斑斓的世界》见过这种特殊情况(所以接下来的内容
属于是
对这种情况的特异性分析),不过我还是补充这一点。由于GARbro认定了hzc文件必然使用zlib压缩,一旦遇到这种特殊情况就无法解包了。不过初级解包还是行的,上一部分的工具还能用。那么我们把一个非常规hzc文件拖进十六进制编辑器,看看具体是什么原因吧:
还是从文件头开始看起。首先是hzc1,然后是图片大小,然后是固定的20 00 00 00 和NVSG,然后是固定的00 01和表示RGBA的01 00,然后是图片分辨率,然后是一串零。然后,哦哦,就在文件头结束的位置出现了不一样的地方:本来是0x78 0xDA,现在变成了“TLG6.0”。TLG6.0又是什么高手了?原来是krkr的图片格式。这下明白了,这种非常规的hzc文件用了别的压缩方法,GARbro不认识,自然解不了了。我们可以写一个程序把“TLG6.0”之前的内容全都删掉,然后把文件后缀改成tlg。之后就能用GARbro预览其内容了:
额外一提Crass可以直接实现tlg转png,所以你把批量处理好的tlg丢给它就行。由于英译版色鸟鸟的图片差分使用了仅覆盖差分部分的方式(而不是原本的用一张完整的新图),在节省了空间的同时,也对我们的解包工作造成了一点点影响:我们还得开个ps之类的把差分和原图合一块。至于封包就不必了,毕竟fvp可不关心这些玩意到底是怎么压缩的——看看KanadeKuro大佬做的补全补丁内容,用0x78 0x9C这种不太一样的zlib压缩方式的hzc文件,fvp都能正常读呢。
2025年03月01日 12点03分 8
level 13
huohua_离民 楼主
结语:总而言之经过了若干天的与十六进制编辑器及python脚本对线的过程,我们已经学会了自己写程序实现F社游戏资源的封解包,可喜可贺。虽然F社游戏资源的加密压缩什么的都并不比原文章的示例《CROSS†CHANNEL》强多少就是了……但以示例来学习自然是以思路为重,思路对了,就算不懂编程也能让AI代劳;思路不对,就算是GARbro这么强大的解包工具也有失手的时候。总之希望各位看完这些,多少能在“思路”上有所收获吧。
对了,是不是还有个hcb文件来着?脚本文件的处理已经早就讨论过了,还请各位自行到我之前的白永FHD补丁贴子下方的参考文献处学习吧,我也该多花点时间实际地享受游戏,而不是在这里大搞解构主义了。
离民
2025年3月1日
参考文献:
[1]BMP格式[EB/OL].百度百科.[2025-2-27].https://baike.baidu.com/item/BMP%E6%A0%BC%E5%BC%8F/3427000
2025年03月01日 12点03分 9
level 11
看不懂思密达[呵呵]
2025年03月01日 12点03分 10
先看教程再看实操,理解就会更深入啦^_^
2025年03月01日 12点03分
@huohua_离民 感觉有必要出一个目录(
2025年03月01日 12点03分
@wang_ctrl 彳亍(
2025年03月01日 13点03分
level 13
huohua_离民 楼主
2025年03月01日 13点03分 11
level 13

2025年03月01日 13点03分 12
level 14
大佬[滑稽]
2025年03月01日 13点03分 13
level 15
好好 少见的教程帖[吐舌]
2025年03月01日 14点03分 14
实际实操环节,并非教程……
2025年03月01日 15点03分
level 15
帮顶喵[小乖]
2025年03月01日 15点03分 15
level 11
学习了✍
2025年03月01日 15点03分 16
level 11
不明觉厉,膜拜
2025年03月01日 17点03分 17
1 2 尾页