梦幻西游新资源的提取一

时间:2022-12-04 04:08:15

之前韩の娃娃问我新出的锦衣是否是在shape.wd8资源包内,但是分析之后发现资源是以3D模型的形式存放在r3d.npk内的。虽然去年夏天简单分析过.gim模型格式,但要我把它渲染的跟梦幻里一样效果还是有些难度的,毕竟我也只是刚刚导出了T-Pose模型而已。
前几天又看到了空灵姐的留言,说大家还在为提取新资源而头疼。既然如此,那么何不尝试提取一下,也小小的满足一下自己的好奇心。

 

问题切入点
负责渲染的程序xyqsvc.exe是如何将渲染好的图片传给客户端程序my.exe使用的?

最开始时,我以为xyqsvc.exe将渲染好的图片存放在v3d_cache目录下的文件内,然后my.exe读取这个文件。但仔细一想,多个程序对同一个文件同时读写,还要同步,这几乎是不可能的。而且使用FileMon监视文件读写,发现my.exe是根本不读取这些文件的。之后的分析显示,v3d_cache下的v3d.hdr和v3d.dat只是xyqsvc.exe的缓存文件,是为了方便下次使用而保存的。传送图片的真正办法是内存共享。(查找内存共享时看到了云风哥对这个技术的介绍,证明我的分析是基本正确的。出乎我的意料的是,xyqsvc.exe是多个客户端共享的)

 

言归正传,既然渲染好的图片缓存下来了,那么必定是可以提取的。(其实不缓存也可以在内存里截取,但涉及的问题就要复杂许多)

下面把我分析的 v3d.hdr 和 v3d.dat 的格式写出来,全当是做个备忘吧。

v3d.hdr 的格式分析

文件开头是一个
typedef struct {
 unsigned long flag; // 'v3dh'
 unsigned long version; // 0x10005 等,版本很多
 unsigned long offset; // 索引表所在的地址
} v3dh_file_header;

跳转到v3dh_file_header.offset后是
long v3dd_size;  // v3d.dat 的大小(以字节为单位)
long v3dh_index_size; // v3dh 索引表的大小(以字节为单位)
接着是一片(就是数组啦)
typedef struct {
 long offset1; // 见后面说明
 long offset2; // 同上
 long linker; // 与此索引相关联的索引编号(从零开始计数)
} v3dh_index;

v3dh_index.offset1 所指内容的格式
unsigned char size; // 这部分数据的大小,不包含这一字节
unsigned long var1; // 未知
unsigned long var2; // 装备调色板信息
unsigned long var3; // 武器调色板编号
unsigned long var4; // 未知(实际是一个链表,目前见到的都是NULL)
unsigned long var5; // 指向名称信息,开头1字节总是0,想不明白
unsigned char Ext[235]; // 最长为 235 ,具体数值根据 size 判断

以上变量中 var2 var4 var5 均为实际相对 var1 的偏移量。
如果 var2 为零,那么对应的调色板为 ("mypal/weapon//%d.pal", var3)
如果 var2 不为零,那么它指向如下结构:
typedef struct {
 short num1;
 short count;
 short num2[count];
};
这个结构的含义不是非常清楚,目前见到的例子中,count总是1,这里只对这种情况说明。
如果 num2[0] 中的高3位不为零,即 (num2[0] & 0xE000) != 0 时,对应的调色板为 ("mypal/equip//%d.pal", num2[0])
否则,对应的调色板为 ("mypal/equip//%d_%d.pal", num2[0], num1)

v3dh_index.offset2 所指内容的格式
与 offset1 相比,这个就简单的太多了。
long frameCount;// 帧数
long dirCount; // 方向数
long dat_offset_list[frameCount*dirCount]; // 每一帧数据在 v3d.dat 里的地址

 

mypal.wdf 中 .pal 文件的格式
unsigned long flag; // '.pal'
unsigned short palCount; // 调色板颜色数(目前见到的都是256)
unsigned short sectionCount; // 分段数目(从逻辑上来说 sectionCount 要么等于零,要么大于等于二)
unsigned char sectionSize[sectionCount]; // 这个数组之和等于 palCount
_ARGB palItems[palCount]; // 实际上,这里的 alpha 都是错的,游戏也不使用

v3d.hdr 的分析至此结束。

 

内容稍后继续,没有广告照样精彩。。。