游戏资源解包

发布于:2025-08-09 ⋅ 阅读:(19) ⋅ 点赞:(0)

文件和工具介绍

pck文件

在一些游戏中,PCK 文件用于存储游戏资源的压缩包,其中可能包含游戏的图像、音效、模型、动画等各种资源。

目标资源可能文件夹名称

skin
character

audio //音频文件

aa包知识介绍

010打开文件
265644772784977
UnityFS 是 Unity 引擎用于存储模型、贴图、动画等数据。
示例里能看到 2022.3.44f1 ,是 Unity 编辑器的版本号(2022 系列、3 子版本、44f1 补丁),说明文件由该版本 Unity 生成。
使用StudioGUI解包发现无法识别,提示bundle加密
常见的加密方式如下:http://Addressables.cn秘钥加密、转存二进制后自定义加密、文件头部添加


这个需要对apk进行逆向找到解密逻辑。这里有个得到key后可以进行解密的项目
unityFS加密
UnityCN-Helper

quicksms

quicksms游戏逆向社区l

导入关键文件夹

Sprite和Texture2D
Texture2D文件夹:贴图文件

音频工具

drgunpack5

**解包 PCK 文件:**许多游戏会把资源整合进 PCK 格式的文件包,Dragon Unpacker 可以对这类文件进行解析,让用户能够获取到其中的图片、声音等素材。
**资源提取与导出:**借助它,用户可以把游戏中的资源提取出来,并以常见的文件格式(如 PNG、MP3 等)导出,方便进一步使用。
**游戏 mod 制作:**在制作游戏 mod 时,往往需要对游戏原有的图像或音频资源进行修改,这时就可以先用 Dragon Unpacker 提取出这些资源,修改完成后再重新打包。
使用流程:File–>Open选择pck文件->Formats->Audio->Search,鼠标右键提取音频,如果无法播放可使用脚本解密
把名为脚本的文件夹里面的东西复制到你之前导出来的加密的音频文件下,然后点击那个convert.bat就可以了

unity解包

特征

  • 电脑端有unity图标

  • 安卓端有unity3d后缀,有libunity.so

  • 文件目录里可以看到一个.exe可执行文件,以及该文件名字加上_Data后缀的文件夹。有时有Unity命名的文件

一般流程

AssetStudioGUI工具

加载单个文件一般是.sharedassets文件
解包文件夹:assests->bin->Data或者assests->bundle

Blender

可以导入fbx文件,为3D模型

dnspy工具

反汇编文件:Assembly-CSharp.dll
修改源代码:编辑->编辑类。编辑完后选择文件->保存模块。也可以选择导出工程在其他编辑器上修改。

Aeon_AssetDecDL

解密工具

AssetRipper

AssetRipper 是一种工具,用于从 Unity 序列化文件(CAB-.assets、.sharedAssets 等)和资源包(.unity3d、*.bundle 等)中提取资源,并将其转换为原生 Unity 引擎格式。
转换出的文件夹中:skeleton.atlas为纹理图集,skeleton为首的png文件为贴图,skeleton.skel.bytes为骨骼文件

xnview

图片大小转换工具

文件头伪加密

方法一

删掉头部第二个UnityFS之前的全部字节就可以了,很多游戏都是这种加密,有的只有一个UnityFS,但不在开头位置,这种也是一样的处理方法。
可以使用工具:FakeHeader V2

方法二


使用raz版本的AssetStudioGUI,并且选择特定的Unity version

查看版本

把.apk改成.zip,解压
进入目录assets\bin\Data,用文本打开某个level文件(其他二进制文件也可以,挑个小的二进制文件打开会快点)

或者使用脚本

import os
import sys


def find_unityfs_offset(file_data):
    unityfs_signature = b'UnityFS'
    pos = file_data.find(unityfs_signature)
    return pos if pos != -1 else None


def process_ab_file(input_path, output_dir):
    try:
        with open(input_path, 'rb') as f:
            file_data = f.read()
        offset = find_unityfs_offset(file_data)
        if offset is None:
            print(f"警告: {os.path.basename(input_path)} 中未找到UnityFS签名,跳过处理")
            return False
        os.makedirs(output_dir, exist_ok=True)

        output_path = os.path.join(output_dir, os.path.basename(input_path))
        with open(output_path, 'wb') as f:
            f.write(file_data[offset:])
        print(f"处理成功: {os.path.basename(input_path)} (移除前 {offset} 字节)")
        return True
    except Exception as e:
        print(f"处理 {os.path.basename(input_path)} 时出错: {str(e)}")
        return False
def process_directory(input_dir, output_dir):
    if not os.path.isdir(input_dir):
        print(f"错误: 输入目录不存在 - {input_dir}")
        return
    ab_files = [f for f in os.listdir(input_dir) if f.lower().endswith('.ab')]
    if not ab_files:
        print(f"警告: 目录中没有找到.ab文件 - {input_dir}")
        return
    print(f"找到 {len(ab_files)} 个.ab文件,开始处理...")

    success_count = 0
    for filename in ab_files:
        input_path = os.path.join(input_dir, filename)
        if process_ab_file(input_path, output_dir):
            success_count += 1
    print(f"\n处理完成: 成功 {success_count}/{len(ab_files)} 个文件")
    print(f"处理后的文件已保存到: {output_dir}")


if __name__ == "__main__":
    input_dir = r""
    output_dir = r""
    process_directory(input_dir, output_dir)

虚幻引擎unreal Engine

鉴别

确认UE版本:先进入游戏,然后打开任务管理器,找到游戏进程,右键打开文件位置。右键文件-属性-详细信息,即可判断是否UE引擎和具体的UE引擎版本。

目标解包文件是这个目录:Game/Content/Paks

Fmodel

spine导出和基本流程



同时注意需要点击一下ADD UNDETECTED GAME把相关路径添加进去
key:0xC14735FB5A872D2AFA76A5C38521AB8B8E21072C08525B913307608BD1182FA7

解密成功后由红色变为绿色。注意输入密钥后需要重启一下。
文件名中带有“Spine”就是包含Spine动画的pak文件

双击其中一个打开它,左键选中“Game”主目录,右键依次点击这两个选项,每个“Spine”的pak文件包都这样操作,注意名称中带“_0_p”是补丁,先解不带“_0_p”的,再解带“_0_p”的。

全部文件导出完成后,将导出的“Game”文件夹拖到“尘白禁区-拆分atlas、skel、json(.NET Framework 4.6.2).exe”程序图标上即可。
尘白禁区-拆分

模型导出


这个设置可以使用blender修改

搜索相关角色
选择保存所有纹理和模型

psk模型blender导入

首先安装psk插件和node插件,然后角色的所有psk文件:文件->导入->psk
接下来是贴图:几何节点(顶部)->着色器编辑器(左边中间)

添加->着色器->原理化->选中原理化Ctrl+shift+T打开导入,导入贴图png

PBR 材质节点搭建
PBR(基于物理的渲染)需要多个纹理通道配合,常见通道:

_d.png:漫反射(Diffuse) 纹理(基础颜色)
_n.png:法线(Normal) 纹理(表面凹凸细节)
_r.png:粗糙度(Roughness) 纹理(控制反光程度)
_a.png:Alpha(透明) 纹理(控制透明区域)

贴图类型 需添加的节点 连接目标
_d.png 图像纹理(Image Texture) Principled BSDF.Base Color
_n.png 图像纹理 + 法线贴图 Principled BSDF.Norma
_r.png 图像纹理 Principled BSDF.Roughness
_a.png 图像纹理 Principled BSDF.Alpha
第一步:添加原理化 BSDF
第二步:添加一个图像纹理节点:在着色编辑器中,按 Shift + A → 纹理 → 图像纹理 ,选择你的 _d.png 。连接图像纹理颜色与原理化颜色原点。
第三步:添加法线贴图:先添加一个图像纹理选择n.png。在着色编辑器中,按 Shift + A → 矢量 → 法线贴图。连接 图像纹理(_n.png).Color → 法线贴图.Color。连接 法线贴图.Normal → Principled BSDF.Normal。法线贴图 节点的 空间(Space) 设为 切线(Tangent)。
第四步:连接 图像纹理(_r.png).Color → Principled BSDF.Roughness(粗糙度)。
第五步:连接 图像纹理(_a.png).Alpha → Principled BSDF.Alpha(透明)。材质属性面板 → 设置(Settings) → 混合模式(Blend Mode) 设为 Alpha 混合(Alpha Blend)。

添加 法线贴图(Normal Map) 节点:(Shift + A → 矢量 → 法线贴图 )。

renpy引擎

鉴别

  • 可以看到游戏目录里有以renpy命名的文件夹,即可确定为renpy引擎

解包流程

  • 使用rpaExtract工具
  • 在游戏的game目录找到rpa后缀的文件(体积最大的那个)
  • 将rpaExtract软件和rpa文件一起放在较短的全英文路径如:
  • 将rpa文件点选后拖动到rpaExtract软件上松手,跳出如下窗口:
  • 解包成功,images文件夹为CG所在文件夹:
  • 如果上述软件无法解压,可使用UnRen工具进行解包,
    将UnRen工具放至游戏目录,打开后按照提示操作即可:

RPG Maker MZ/MV引擎

鉴别

  • 文件目录有www命名的文件夹,执行程序为Game.exe,
  • 并且在www/img文件夹路径可以发现rpgmvp/rpgmvo后缀的文件,这些就是游戏图片素材。

解包流程

  • 解包网站: rpgmvp图片解密

  • 有时打开一些RPG Meker游戏会发现里面只有一个很大的rgss2/rgss3/rgss3a文件,这类数据包需要借助GARbro工具解压。选中需要导出的图片后右键选择”转换多媒体文件“即可:

krkr引擎

鉴别

  • 很明显的krkr命名文件,并且目录内会有很大的.xp3文件,这个就是需要解压的游戏数据包。

解包流程

用GARbro解包。

Tyrano引擎

鉴别

  • 不封包,下图路径可以看到tyrano 、data、node_modules文件夹,index.html 、package.json文件直接去路径中找cg。

  • 封入.exe,文件目录单一,只有一个很大的.exe文件:可以尝试直接将.exe文件改后缀为.zip解压(记得备份),如果文件没加密即可获取游戏资源。如果失败,则需要用到UniExtract工具解压,选择.exe文件,点击确定即可开始解包。

  • 封入app.asar,在下图路径会有一个很大的app.asar文件:需要借助工具WinAsar,打开软件点击 Extract,会把解包内容放到同目录下的 app.asar.unpack 里。

Electron 引擎解包

鉴别

  • 文件特征
    • resources 文件夹:Electron 应用通常会包含一个 resources 文件夹,里面存放着应用的资源文件, 如 HTML、CSS、JavaScript 代码等,这些文件用于构建应用的用户界面和实现业务逻辑。
    • .pak 文件 :像 chrome_100_percent.pak、chrome_200_percent.pak 以及 resources.pak 等,Electron 基于 Chromium,这些 .pak 文件是 Chromium 用于存储字体、界面语言包等资源的文件格式,在 Electron 应用中也会出现。
    • v8_* 文件 :v8_context_snapshot.bin 与 V8 引擎有关,V8 是 Google 开发的一款高性能 JavaScript 引擎,Electron 使用 V8 来运行 JavaScript 代码, 所以出现与 V8 相关的文件是符合 Electron 应用特征的。
    • 把exe文件修改为zip文件,然后用压缩程序打开,然后再看压缩文件中是不是有一个resources目录,下面有一个app.asar文件。

拆包

游戏资源一般没有加密,直接在文件夹中找就行。

apk拆包

存储在Android/data/“包名”(使用自带的文件管理器或MT管理器即可查看)
存储在data/data/“包名”(这个需要root或者模拟器才能查看)
assetstudio是常用的用于解包未加密unity资源文件的工具,下面是对这几个版本的简介。

TexturePacker

处理pvr.ccz文件,直接把文件夹拖进去就可以

aa包获取

方法一:抓包

目前版本需要靠抓包获取他的资源文件下载列表
erolab其他游戏(NS开发商的)全部采用了这个在线获取index/json资源列表的方法了,抓取这个资源列表也很简单只需要在游戏第一次启动时候抓包然后按响应内容大小排序就能发现一个体积较大的txt/json文件大概率就是

方法二:Android/data/

使用mt管理器,将联想模拟器中的文件夹移动到共享文件夹中

顺便一提有的游戏资源在data/data/文件夹下,甚至没有加密

模型导入

AssetStudioGUI

版本:
Raz版:支持一些加密方式的解密(unityCN,fakeheader),不过有点问题(有问题就换个版本assetstudio)。Studio
Raz教程:教程

aelurum版:支持自动检索导出一些正常的live2d文件。:AssetStudio

原版:releases
tuanjie版本:releases

文件类型介绍

mesh:模型
AnimationClip:Unity中的功能,动作
Animator:Unity中的功能,动画状态机,解包动作文件的时候会用到,我们这里解包出来的时候会有.fbx文件,是已经着好色的模型了,推荐用这个。
AudioClip:声效文件
Mesh:网格体文件,纯纯的.obj白模,点击一下可以在右边预览;
MonoBehaviour:Unity的脚本基类
TextAsset:角色的L2D素材
Texture2D:贴图文件夹,用来给网格体着色的。

live2D提取:UnityLive方法


选择Texture2D文件,点击Show original file会打开文件资源管理器。
将该文件直接拖到工具UnityLive2DExtractor的exe执行文件中,然后会导出Live2DOutput文件夹。

这是gitup上的一个项目。
点开导出的Live2DOutput文件夹,会显示类似下图的文件结构。

打开live2D的Ex工作室,选择Live2D编辑器,选择Moc或Json文件选项,选择上图的Character.moc3文件,跳出来的对话框选择勾选贴图,确定,选择配置文件,编辑,选项,修改缩放因子为0.001。以上就可以看到导入的模型了。

Live2D文件提取:Spine导出法


如图勾选,TextAsset是模型动作,Texture2D是模型贴图。
以下我们有两个思路,
一种是把所筛选的两个文件夹全部导出,然后整理同一角色(名字相同)的文件为一个文件夹。
另一种思路是,在AssetStudio页面内把内容按名字排序,导出同一名字的TextAsset和Texture2D。

如上图,两种导出方式。

如下图,为整理的一个角色的文件。

接下来打开Live2DViewweEX,选择EX工作室里面的Spine编辑器,选择模型文件夹,创建配置文件。

live2Dmoc模型导出选项

方法1

Animator 是 “动画总指挥”,靠控制器编排逻辑;Animator Clips 是 “动画素材库”,存具体动作数据。两者配合,让 Unity 里的模型能实现复杂、流畅的动画表现,从简单 UI 动效到复杂角色动作都离不开这套体系 。
lie2Dmoc模型一般分为表情,动作等如下图

导出时我们需要勾选以下几个选项

然后选择同一角色,可以点击如下图排序选择同一个角色的

然后右键选择如下图导出方式


然后使用live2d导入即可,如下图

然后就需要导入表情和动作,可以参考下面文章
live2d动作
live2d
参考表格

动作 描述
Act: 可能是 “主动作(Activity)”,比如角色的常规行为(走路、挥手等);
Dress: 可能关联 “换装(Dress - up)” 相关动作(比如裙摆飘动、衣服部件微动 );
Finish: 可能是 “收尾、结束动作(Finish)”(比如表演谢幕、动作组收尾 );
Idle :是 “待机动作(Idle)”,角色静止时循环播放的动画(比如呼吸、轻微晃动 );
Item: 可能关联 “道具交互(Item)” 动作(比如拿取物品、道具触发的反应 )

方法2

这个不需要选择过滤

模型导出为fade处理

导出设置和导出选项

设置

options–>export

选项

3D模型提取

spine工具

spine文件格式介绍

.atlas(贴图解析文件)
.skel(骨骼和动作文件)
以上两个类型是TextAsset可以一起导出,上面个名字一样的Texture2d是这个cg的图片素材,这一个cg只导出这三个文件就行了。

导出功能

全部导出


导出后关键文件就在sprite和Texture2D
分组导出

skeletonViewer-3.8.99

spine-skeleton-viewer
spine查看器
java -jar 运行

spine文件json格式查看


json格式的*.json文件是骨架,atlas文件是纹理图集
skel格式的.skel文件是骨架,atlas文件是纹理图集`。

导入

首先导入骨骼json文件
然后导入纹理。


注意此时上图所示输出文件夹,必须和一骨骼对象的文件夹一致

导入模型错误处理

导入图片大小报错处理

素材.png的像素分辨率 和 .atlas文件里的不一样。打开.atlas文件(可以直接用记事本打开)看到size是8192x8192,而素材.png的分辨率是2048x2048。可以利用Adobe Photoshop把图片的分辨率拉成8192x8192。

取消约束比例

资源提取工具

AssetStudioGUI

资源提取:能够解析 Unity 引擎生成的资源文件(如 assets、resource、bundle 等),从中提取图像、模型、音频、文本等资源。
File–>Load file,打开到这个目录\assets\bin\Data,Data里面就是资源了
图片立绘文件夹:assests->bin->Data或者assests->bundle

live2d

官方教程:live2d官方教程

支持格式

EX工作室能使用的模型文件:
1、Live2d-2版本模型文件:(编辑器位于:EX工作室-创作工具-Live2D编辑器)

moc:模型文件
mtn:动作文件
png:贴图文件
physics.json:物理效果文件,可无
exp.json:表情文件,可无

2、Live2d-3版本模型:(编辑器位于:EX工作室-创作工具-Live2D编辑器)

moc3:模型文件
motion3.json:动作文件
png:贴图文件
physics3.json:物理效果文件,可无

3、Spine模型:(编辑器位于:EX工作室-创作工具-Spine编辑器)

skel、json:json为可编辑的模型文件,skel为二进制模型文件
atlas:纹理图集文件
png:贴图文件

4、DragonBones模型:(编辑器位于:EX工作室-创作工具-Spine编辑器)

_ske.dbbin、_ske.json:json为可编辑的模型文件,dbbin为二进制模型文件
_tex.json:纹理图集文件
png:贴图文件

live2d导入spine白边问题处理

**方法一:**png在ps修改后发现出现白边
原理:ps修改png导入发现白边
将PNG在直通(STRAIGHT)与预乘(PREMULT)ALPHA之间转换
使用项目:PMA-Converter
首先用脚本 png预乘alpha转直通 把解包的出的预乘图像转换到非预乘,此时也就是ps可以处理的图像

用ps修改转换后的图像,保存png,使用另一个脚本转换为预乘,即可在游戏和软件里正常加载。
方法二:着色器问题
live2d教程

导出为live2d文件

live2d模型json和动作文件在MonoBehavir文件中

注意看文件路径,和后缀。


如上两个图,导出后都是json格式,这是因为moc文件被分成了json文件,我们需要将其复原
下面这段代码用来处理 Live2D 模型的主 JSON 文件生成moc文件(不包含动作等,仅仅提取出模型)

import os
import json

current_dir = '.'  # 当前目录,可以替换成你的目标路径

for item in os.listdir(current_dir):
    item_path = os.path.join(current_dir, item)
    if not os.path.isdir(item_path):
        continue

    # 处理 .json -> .moc3 的转换
    main_json = os.path.join(item_path, f"{item}.json")
    if os.path.exists(main_json):
        with open(main_json, 'r', encoding='utf-8') as f1:
            data = json.load(f1)

        # 写入 moc3 文件
        moc3_path = os.path.join(item_path, f"{item}.moc3")
        with open(moc3_path, 'wb') as f2:
            f2.write(bytes(data['_bytes']))
        print(f"{item}.moc3 文件已提取完成!")

    # 处理 model3.json 的更新
    model_json = os.path.join(item_path, f"{item}.model3.json")
    if os.path.exists(model_json):
        with open(model_json, 'r', encoding='utf-8') as f3:
            model_data = json.load(f3)

        # 初始化数据结构
        file_refs = model_data.setdefault("FileReferences", {})
        expressions = file_refs.setdefault("Expressions", [])
        motions = file_refs.setdefault("Motions", {})  # 确保是字典类型

        # 扫描 motions 目录
        motions_dir = os.path.join(item_path, "motions")
        if os.path.exists(motions_dir) and os.path.isdir(motions_dir):
            for motion_file in os.listdir(motions_dir):
                if motion_file.endswith('.motion3.json'):
                    motion_name = motion_file.replace('.motion3.json', '')
                    motion_path = os.path.join("motions", motion_file).replace('\\', '/')

                    # 如果动作不存在则添加(使用setdefault)
                    motions.setdefault(motion_name, [{"File": motion_path}])

        # 扫描 expressions 目录
        expression_dir = os.path.join(item_path, "expressions")
        if os.path.exists(expression_dir) and os.path.isdir(expression_dir):
            for exp_file in os.listdir(expression_dir):
                if exp_file.endswith('.exp3.json'):
                    exp_name = exp_file.replace('.exp3.json', '')
                    exp_path = os.path.join("expressions", exp_file).replace('\\', '/')

                    expressions.append({"Name": exp_name, "File": exp_path})

        # 写回更新后的数据
        with open(model_json, 'w', encoding='utf-8') as f4:
            json.dump(model_data, f4, ensure_ascii=False, indent=2)

然后使用live2D ExStudio加载这个文件夹,选择主模型moc.json文件就可以加载了
接着是导入表情,选择配置文件编辑,如下图添加即可

如果没有显示可用的表情文件,使用下面这个软件。
Live2D.model3.json文件重建
接着是添加动作
打开【Json编辑器】
新建动作组: 选择【动作】,在【动作组】 列表点击【】,弹出的对话框的预定义一栏选择【Idle】,点击【确认】
新建动作: 在【动作】列表点击【】,在新建的动作内容页找到【文件】,点击右侧【】,选择一个 motion3.json 文件作为待机动作
若想建立多个待机动作,请重复上一个步骤
点击【确认】之后,在Live2D编辑器中查看效果

json和skel混淆

有些SPINE文件不是給二進制的skel , 而是給文本json
所以後綴只能手動再加回去.json
可以使用Hex Editor Neo 查找特定關鍵詞 : {“skeleton”
就可以知道哪幾個文件是json形式
再一一手動添加.json後綴即可

png修复

遊戲圖像將一張PNG圖拆分成兩張
只有RGB的圖 + 只有帶alpha通道的圖
要合併為原來的PNG圖像
這邊需要使用ImageMagick
使用下面bat指令

@echo off
set exevar="C:\Program Files\ImageMagick-7.1.1-Q16-HDRI\magick.exe"

for /f "usebackq tokens=*" %%d in (`dir /s /b *.png`) do (
    %exevar% "%%d" "%%~dpnd_alpha.png" -alpha off -compose copyopacity -composite "%%~dpnd.png"
)

moc3文件解密

fade.json转motion.json

脚本遍历指定目录(character)及其子目录,识别两种文件:
.fade.json → 转换为 motion3.json(Live2D 动画曲线配置文件 )。
CubismPhysicsController.json → 转换为 physics3.json(Live2D 物理模拟配置文件 )。
运行:node live2dConverter.js

const fs = require('fs');
const path = require('path');

function processFadeFiles(dirPath) {
    const files = fs.readdirSync(dirPath);
    for (const file of files) {
        const filePath = path.join(dirPath, file);
        const stat = fs.statSync(filePath);
        if (stat.isDirectory()) {
            processFadeFiles(filePath);
        } else if (file.endsWith('.fade.json')) {
            const fileName = path.basename(file, '.fade.json');
            const data = fs.readFileSync(filePath, 'utf8');
            const obj = JSON.parse(data);
            const motion3Json = {
                'Version': 3,
                "Meta": {
                    "Duration": 0.000,
                    "Fps": 60.0,
                    "Loop": true,
                    "AreBeziersRestricted": true,
                    "CurveCount": 0,
                    "TotalSegmentCount": 0,
                    "TotalPointCount": 0,
                    "UserDataCount": 1,
                    "TotalUserDataSize": 0
                },
                "Curves": [],
                "UserData": [
                    {
                        "Time": 0.0,
                        "Value": ""
                    }
                ]
            };
            // motion3Json.Meta.TotalSegmentCount = obj.ParameterIds * 10
            // motion3Json.Meta.TotalSegmentCount = obj.ParameterIds * 15
            let TotalSegmentCount = 0
            let maxTime = 0.0
            for (let i = 0; i < obj.ParameterCurves.length; i++) {
                let Segments = []
                for (let j = 0; j < obj.ParameterCurves[i].m_Curve.length; j++) {
                    TotalSegmentCount++;
                    Segments.push(obj.ParameterCurves[i].m_Curve[j].time ?? 0)
                    Segments.push(obj.ParameterCurves[i].m_Curve[j].value ?? 0)
                    Segments.push(obj.ParameterCurves[i].m_Curve[j].weightedMode ?? 0)
                    maxTime = maxTime > obj.ParameterCurves[i].m_Curve[j].time ? maxTime : obj.ParameterCurves[i].m_Curve[j].time
                }
                Segments.pop()
                motion3Json.Curves.push({
                    "Target": "Parameter",
                    "Id": obj.ParameterIds[i],
                    "Segments": Segments
                })
            }
            motion3Json.Meta.CurveCount = obj.ParameterIds.length
            motion3Json.Meta.Duration = maxTime
            motion3Json.Meta.TotalSegmentCount = TotalSegmentCount
            motion3Json.Meta.TotalPointCount = obj.ParameterIds.length + TotalSegmentCount
            fs.writeFileSync(path.join(dirPath, `${fileName}.motion3.json`), JSON.stringify(motion3Json, '\t'));
            console.log(path.join(dirPath, `${fileName}.motion3.json`) + "已生成");
        } else if (file.endsWith('CubismPhysicsController.json')) {
            const data = fs.readFileSync(filePath, 'utf8');
            const obj = JSON.parse(data);
            let physicsJson = {
                "Version": 3,
                "Meta": {
                    "PhysicsSettingCount": 0,
                    "TotalInputCount": 0,
                    "TotalOutputCount": 0,
                    "VertexCount": 0,
                    "Fps": 0,
                    "EffectiveForces": {
                    },
                    "PhysicsDictionary": [
                    ]
                },
                "PhysicsSettings": []
            }
            physicsJson.Meta.EffectiveForces.Gravity = obj?._rig?.Gravity
            physicsJson.Meta.EffectiveForces.Wind = obj?._rig?.Wind
            physicsJson.Meta.Fps = obj._rig.Fps ?? 60
            for (let i = 0; i < obj._rig?.SubRigs?.length ?? 0; i++) {
                let physicsSetting = {
                    "Id": "PhysicsSetting",
                    "Input": [
                    ],
                    "Output": [
                    ],
                    "Vertices": [
                    ],
                    "Normalization": {
                    }
                }
                let rig = obj._rig.SubRigs[i]
                physicsSetting.Id = physicsSetting.Id + (i + 1)
                physicsJson.Meta.PhysicsDictionary.push({
                    "Id": physicsSetting.Id,
                    "Name": i + 1 + ""
                })
                for (let j = 0; j < rig?.Input.length ?? 0; j++) {
                    physicsSetting.Input.push({
                        "Source": {
                            "Target": "Parameter",
                            "Id": rig.Input[j].SourceId
                        },
                        "Weight": rig.Input[j].Weight,
                        "Type": rig.Input[j].AngleScale || rig.Input[j].AngleScale === 0 ? "Angle" : "X",
                        "Reflect": false
                    })
                }
                for (let j = 0; j < rig?.Output.length ?? 0; j++) {
                    physicsSetting.Output.push({
                        "Destination": {
                            "Target": "Parameter",
                            "Id": rig.Output[j].DestinationId
                        },
                        "VertexIndex": 1,
                        "Scale": rig.Output[j].AngleScale ?? 1,
                        "Weight": rig.Output[j].Weight,
                        "Type": rig.Output[j].AngleScale || rig.Output[j].AngleScale === 0 ? "Angle" : "X",
                        "Reflect": false
                    })
                }
                for(let j = 0; j < rig?.Particles?.length; j++) {
                    physicsSetting.Vertices.push(                        {
                        "Position": rig?.Particles[j].InitialPosition,
                        "Mobility": rig?.Particles[j].Mobility,
                        "Delay": rig?.Particles[j].Delay,
                        "Acceleration": rig?.Particles[j].Acceleration,
                        "Radius": rig?.Particles[j].Radius
                    })
                }
                physicsSetting.Normalization = rig.Normalization
                physicsJson.PhysicsSettings.push(physicsSetting)
            }
            fs.writeFileSync(path.join(dirPath, `l2d.physics3.json`), JSON.stringify(physicsJson, '\t'));
            console.log(path.join(dirPath, `l2d.physics3.json`) + "已生成");
        }
    }
}



processFadeFiles(".");

最后一行processFadeFiles为目录

psk模型

导入解释


psk模型会自动加载json文件路径内的东西,但需要注意的是,为解包的json文件,里面的路径为虚拟路径,我们需要使用脚本或者手动的把路径进行修改。

一般处理情况是需要我们手动的添加贴图。

web游戏解包

资源获取

抓包

这里的资源是
https://web-assets.otogi-frontier.com/prodassets/GeneralWebGL/AssetBundlePatch/AdditionalAssets.csv 237

下图是游戏资源下载时使用的路径,所以我们推断上面的资源需要加载如图下一样的前缀https://web-assets.otogi-frontier.com/prodassets//GeneralWebGL/Assets/ 123
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
大概整理为如下

fbx图形处理

教程

转换

GMS解包

类型识别

(如上图)游戏资源基本都在data.win中

UndertaleModTool解包

项目地址

使用次工具进行解包

直接将data.win拖入undertalModtool即可

rpgmvp文件提取

Petschkos RPG-Maker

项目地址


网站公告

今日签到

点亮在社区的每一天
去签到