BlueRoseNote/06-DCC/Blender/导出PSK_PSA脚本 & 部分UModel AES Key.md

44 KiB
Raw Blame History

导出PSK插件地址

清空数据

bpy.ops.wm.read_factory_settings(use_empty=True)

PSK

import bpy
import os
import math
from io_import_scene_unreal_psa_psk_280 import *

pskPath = 'C:\\Users\\BlueRose\\Desktop\\BLUEPROTOCOL\\Game\\Character\\Enemy'

for root, subdirs, files in os.walk(pskPath):
    for dirName in subdirs:
        print(root + '\\'+ dirName)
        for modelroot, modeldirs, modelfiles in os.walk(root + '\\'+ dirName +'\\Model'):
            for f in modelfiles:
                if f.endswith('.psk'):
                    psk_file = os.path.join(modelroot, f)
                    fbx_file = os.path.splitext(psk_file)[0] + ".fbx"
                    # 清空场景
                    bpy.ops.object.select_all(action='SELECT')
                    bpy.ops.object.delete()
                    pskimport(psk_file, bReorientBones = False)
                    # 使用SmoothShading
                    for obj in bpy.data.objects:
                        if obj.type=='MESH':
                            obj.data.use_auto_smooth = True  
                            obj.data.auto_smooth_angle = math.radians(180)
                            currentmesh = obj.data
                            for setting in currentmesh.polygons:
                                setting.use_smooth = True
                                
                    # 输出骨骼模型
                    bpy.ops.object.select_all(action='SELECT')
                    bpy.ops.export_scene.fbx(filepath=fbx_file,add_leaf_bones=False)

直接生成fbx版本

import bpy
import os
import math
from io_import_scene_unreal_psa_psk_280 import *

pskPath = 'C:\\Users\\BlueRose\\Desktop\\ScarletNexus\\Game\\Character\\wp\\wp0100\\04_Mesh'

for modelroot, modeldirs, modelfiles in os.walk(pskPath):
    for f in modelfiles:
        if f.endswith('.psk'):
            psk_file = os.path.join(modelroot, f)
            fbx_file = os.path.splitext(psk_file)[0] + ".fbx"
            # 清空场景
            bpy.ops.object.select_all(action='SELECT')
            bpy.ops.object.delete()
            pskimport(psk_file, bReorientBones = False)
            # 使用SmoothShading
            for obj in bpy.data.objects:
                if obj.type=='MESH':
                    obj.data.use_auto_smooth = True  
                    obj.data.auto_smooth_angle = math.radians(180)
                    currentmesh = obj.data
                    for setting in currentmesh.polygons:
                        setting.use_smooth = True
                        
            # 输出骨骼模型
            bpy.ops.object.select_all(action='SELECT')
            bpy.ops.export_scene.fbx(filepath=fbx_file,add_leaf_bones=False)

使用BetterExport的版本

import bpy
import os
import math
from io_import_scene_unreal_psa_psk_280 import *
from better_fbx import *

pskPath = 'C:\\Users\\BlueRose\\Desktop\\ScarletNexus\\Game\\Character\\wp\\wp0100\\04_Mesh'

for modelroot, modeldirs, modelfiles in os.walk(pskPath):
    for f in modelfiles:
        if f.endswith('.psk'):
            psk_file = os.path.join(modelroot, f)
            fbx_file = os.path.splitext(psk_file)[0] + ".fbx"
            # 清空场景
            bpy.ops.object.select_all(action='SELECT')
            bpy.ops.object.delete()
            pskimport(psk_file, bReorientBones = False)
            # 使用SmoothShading
            for obj in bpy.data.objects:
                if obj.type=='MESH':
                    obj.data.use_auto_smooth = True  
                    obj.data.auto_smooth_angle = math.radians(180)
                    currentmesh = obj.data
                    for setting in currentmesh.polygons:
                        setting.use_smooth = True
                        
		    bpy.ops.object.select_all(action='SELECT')
			bpy.ops.better_export.fbx(filepath=fbx_file,my_fbx_format="binary",use_selection=True,use_visible=True,use_only_root_empty_node=False)

PSA

注意该脚本因为Blender运行bug会导致输出的FBX文件越来越大越来越慢。一次转换的量不建议超过50个。建议分几个文件夹同时开多个Blender一起转换。

import bpy
import os
import math
from io_import_scene_unreal_psa_psk_280 import *
pskPath = 'C:\\Users\\BlueRose\\Desktop\\BLUEPROTOCOL\\Game\\Character\\Enemy\\E000_00\\Model\\SK_CH_E000_00.psk'
psaDirPath = 'C:\\Users\\BlueRose\\Desktop\\BLUEPROTOCOL\\Game\\Animations\\Enemy\\E000'

# 清空场景并且导入模型
for root, subdirs, files in os.walk(psaDirPath):
    for f in files:
        if f.endswith('.psa'):
	        bpy.ops.scene.new(type='EMPTY')
            bpy.ops.object.select_all(action='SELECT')
            bpy.ops.object.delete()
            pskimport(pskPath, bReorientBones = False, bImportmesh = False, bImportbone = True)

            psa_file = os.path.join(root, f)
            pskDirPath = os.path.dirname(pskPath)
            fbx_file = os.path.join(pskDirPath,f) 
            fbx_file = os.path.splitext(fbx_file)[0] + ".fbx"
            print(fbx_file)
            
            psaimport(psa_file)
            
            bpy.ops.object.select_all(action='SELECT')
            bpy.ops.export_scene.fbx(filepath=fbx_file,add_leaf_bones=False)
			
			bpy.ops.scene.new(type='EMPTY')
			bpy.ops.object.select_all(action='SELECT')
			bpy.ops.object.delete()

绯红结系批量转换代码

import bpy
import os
import math
from io_import_scene_unreal_psa_psk_280 import *
from better_fbx import * 

for action in bpy.data.actions:
    bpy.data.actions.remove(action) 

# 'C:\\Users\\BlueRose\\Desktop\\ScarletNexus\\Game\\Character\\ch\\ch0100\\04_Mesh\\SM_ch0100.pskx'
# 'C:\\Users\\BlueRose\\Desktop\\ScarletNexus\\Game\\Character\\no\\no0000\\04_Mesh\\SM_no0000_orbit.psk'
pskPath = 'C:\\Users\\BlueRose\\Desktop\\ScarletNexus\\Game\\Character\\ch\\ch0100\\04_Mesh\\SM_ch0100.pskx'
psaPath = 'C:\\Users\\BlueRose\\Desktop\\ScarletNexus\\Game\\Character\\ch\\ch0100\\25_SpAttack\\Battle\\c04\\AS_ElectricPole_ch0100_c04_ch0100.psa'
fbxExportPath = 'C:\\Users\\BlueRose\\Desktop\\ScarletNexus\\Game\\Character\\ch\\ch0100\\25_SpAttack\\Battle\\c04\\'

# 清空并且设置为60fps
bpy.ops.scene.new(type='EMPTY')
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()
bpy.context.scene.render.fps = 60


# 导入骨骼以及动画(所有NLA)
pskimport(pskPath, bReorientBones = False, bImportmesh = False, bImportbone = True)
psaimport(psaPath, bActionsToTrack = True)

for obj in bpy.data.objects:
    print(obj.type)
    if obj.type=='ARMATURE':
        currentSkeleton = obj

print(currentSkeleton.animation_data.action)

for action in bpy.data.actions:
    # print(action)
    currentSkeleton.animation_data.action = action
    bpy.ops.better_export.fbx(filepath= fbxExportPath+ action.name + ".fbx",my_fbx_format="binary",use_selection=False,use_visible=True)
    # print(currentSkeleton.animation_data.action)
    # print(bpy.data.objects["SM_ch0100.ao"].animation_data.action)

鸣潮批量转换脚本

import bpy
import os
import math
from io_import_scene_unreal_psa_psk_280 import *
from better_fbx import * 
pskPath = 'C:\\Users\\BlueRose\\Desktop\\WutheringWaves\\Game\\Aki\\Character\\Role\\FemaleM\\Yangyang\\R2T1YangyangMd10011\\Model\\R2T1YangyangMd10011.psk'
psaDirPath = 'C:\\Users\\BlueRose\\Desktop\\WutheringWaves\\Game\\Aki\\Character\\Role\\FemaleM\\Yangyang\\CommonAnim'

# 清空场景并且导入模型
for root, subdirs, files in os.walk(psaDirPath):
    for f in files:
        if f.endswith('.psa'):
	        # 清空并且设置为30fps(大部分是30fps)
			bpy.ops.scene.new(type='EMPTY')
			bpy.ops.object.select_all(action='SELECT')
			bpy.ops.object.delete()
			bpy.context.scene.render.fps = 30
            pskimport(pskPath, bReorientBones = False, bImportmesh = False, bImportbone = True)

            psa_file = os.path.join(root, f)
            psaDirPath = os.path.dirname(psa_file)
            fbx_file = os.path.join(psaDirPath,f) 
            fbx_file = os.path.splitext(fbx_file)[0] + ".fbx"
            print(fbx_file)
            
            psaimport(psa_file)
            
            bpy.ops.object.select_all(action='SELECT')
            #bpy.ops.export_scene.fbx(filepath=fbx_file,add_leaf_bones=False)
            bpy.ops.better_export.fbx(filepath=fbx_file,my_fbx_format="binary",use_selection=False,use_visible=True)
			
			bpy.ops.scene.new(type='EMPTY')
			bpy.ops.object.select_all(action='SELECT')
			bpy.ops.object.delete()

添加平滑组

import bpy
import os
import math

for obj in bpy.data.objects:
    if obj.type=='MESH':
        obj.data.use_auto_smooth = True  
        obj.data.auto_smooth_angle = math.radians(180)
        currentmesh = obj.data
        for setting in currentmesh.polygons:
            setting.use_smooth = True

NLA Action遍历

import bpy
import os
import math

for action in bpy.data.actions:
	print(action)
	action.name
	
	bpy.data.actions["AS_ch0100_016_AL_walk"].name = "AS_ch0100_016_AL_walk"


bpy.context.object.animation_data.nla_tracks.active = bpy.context.object.animation_data.nla_tracks[0]


# You can set an object's action like:
# object.animation_data.action = action
print(bpy.data.objects["SM_ch0100.ao"].animation_data.action)

取得当前激活状态的NLA Action
```python
for i in bpy.data.objects:
    for j in i.animation_data.nla_tracks.values():
        for k in j.strips.values():
            if k.active:
                print(k)

其他

获取UModel解包游戏所需的AES Key:https://cs.rin.ru/forum/viewtopic.php?t=100672

蓝色协议

AES Key:0xEA9051DDACE1CCF98A0510F0E370BD986A75C74756E0309E6A578A47AF564255 UE版本为4.27.2

D:\OtherTools\GameAssets\umodel_win32_1590\umodel_64.exe -path=D:\Games\BlueProtocol\BLUEPROTOCOL -out=C:\Users\BlueRose\Desktop\BlueProtocol\ -game=ue4.27 -aes=0xEA9051DDACE1CCF98A0510F0E370BD986A75C74756E0309E6A578A47AF564255

绯红结系

AES Key:0x48AE8311350417BDC50A440FCD0E98B2FA6BCEAE3EDA8D0E24881F205E6C4540 UE版本为4.26

D:\OtherTools\GameAssets\umodel_win32_1590\umodel_scarlet_nexus_v5.exe -path=C:\Game\ScarletNexus -out=C:\Users\BlueRose\Desktop\ScarletNexus -game=ue4.26 -aes=0x48AE8311350417BDC50A440FCD0E98B2FA6BCEAE3EDA8D0E24881F205E6C4540
D:\OtherTools\GameAssets\umodel_win32_1590\umodel_scarlet_nexus_v5.exe -path=D:\SteamLibrary\steamapps\common\ScarletNexus -out=C:\Users\BlueRose\Desktop\ScarletNexus -game=ue4.26 -aes=0x48AE8311350417BDC50A440FCD0E98B2FA6BCEAE3EDA8D0E24881F205E6C4540
D:\OtherTools\GameAssets\umodel_win32_1590\umodel_scarlet_nexus_legacy.exe -path=D:\SteamLibrary\steamapps\common\ScarletNexus -out=C:\Users\BlueRose\Desktop\ScarletNexus -game=ue4.26 -aes=0x48AE8311350417BDC50A440FCD0E98B2FA6BCEAE3EDA8D0E24881F205E6C4540

需要特殊的UModel进行提取:https://www.gildor.org/smf/index.php/topic,7814.0.html#msg40636 下载地址:https://drive.google.com/file/d/1S_037OmnxY28cQymu8NMwFApsBahFqx6/view

导出动画方法:

  1. 选择骨骼模型右键点击Open(add to locate set)之后按o。
  2. 选择动画资产AS开头右键点击Open(add to locate set)。
  3. 点击Tools - ExportCurrentObject就可以导出指定的动画。
  4. 如果选择多个动画的情况下在Blender psk/psa插件中可以勾选All actions to NLA track
  5. 导出动画时在烘焙动画选项中选择全部动作、NLA片段。所有动画数据都会以NLA Clip的方式存储。
  6. 导入UE就可以显示所有的动画数据。 导出的时候还需要注意1、FPS是不是设置成60默认是24。2、导出选项需要去掉添加叶子骨骼节点的选项

资产笔记

角色模型分为Ch与Co前缀Ch是Ch/Chxxx中的模型Co是00_Common/Coxxx中的模型。

  • Effect游戏特效里面的一些贴图与Mesh可以拿来做刀光。
  • Character
    • 00_Common主角插在身上的Link线以及其他长在怪物身上的电灯泡与花。
    • ch各种角色相关资产。
      • Animation各种动画。
        • 400_DM角色受到攻击。
      • SAS男主的SAS技能。
      • BrainCrash击破处决动画。
      • DriveMode驱动模式。随机触发的爆发状态
      • BrainField一些Qte 伤害动画。主要是一些Boss的
      • GameOver游戏失败的角色动画。
      • SPAttack游戏中的SP攻击也就是Qte念力攻击。
      • Intercept拦截游戏中另一个主角的SPAttack。
    • wp各个角色的武器
      • wp0100男主武器。
      • wp0200女主武器。
      • wp0300花火武器。棍子
      • wp0400拳套。
      • wp0800警棍。
      • wp0900手弩。
      • wp1000电锯。
      • wp1100手里剑。
    • em怪物模型。
      • BrainCrash里面主要是怪物处决的Qte动画。AS_BC_emxxxx_前缀后面代表对应的角色模型。
    • npNPC模型。

长得像石头的

  • /Game/Character/00_Common/04_Mesh/SM_CableEnergyMesh.uasset
  • /Game/Character/no/no0000/04_Mesh/SM_no0000_orbit.uasset
  • /Game/Character/00_Common/co0300/04_Mesh/SM_co0300.uasset
  • /Game/DLC/Ver0102/Environment/Location/LC70/SM_pr_LC70_Block.uasset

UModel

快捷键:

  • h显示所有命令。
  • 空格:播放动画。
  • s显示骨骼。
  • 【】:选择下/上一个动画。
  • 《》:选择下/上一帧。

CMD启动

D:\OtherTools\GameAssets\umodel_win32_1590\umodel_scarlet_nexus_v5.exe -path=C:\Game\ScarletNexus -out=C:\Users\BlueRose\Desktop\ScarletNexus -game=ue4.26 -aes=0x48AE8311350417BDC50A440FCD0E98B2FA6BCEAE3EDA8D0E24881F205E6C4540

显示所有命令选项:

D:\OtherTools\GameAssets\umodel_win32_1590\umodel_scarlet_nexus_v5.exe -help
Usage: umodel [command] [options] <package> [<object> [<class>]]
       umodel [command] [options] <directory>
       umodel @<response_file>

    <package>       name of package to load - this could be a file name
                    with or without extension, or wildcard
    <object>        name of object to load
    <class>         class of object to load (useful, when trying to load
                    object with ambiguous name)
    <directory>     path to the game (see -path option)

Commands:
    -view           (default) visualize object; when no <object> specified
                    will load whole package
    -list           list contents of package
    -export         export specified object or whole package
    -save           save specified packages

Help information:
    -help           display this help page
    -version        display umodel version information
    -taglist        list of tags to override game autodetection (for -game=nnn option)
    -gamelist       list of supported games

Developer commands:
    -log=file       write log to the specified file
    -dump           dump object information to console
    -pkginfo        load package and display its information
    -testexport     perform fake export

Options:
    -path=PATH      path to game installation directory; if not specified,
                    program will search for packages in current directory
    -game=tag       override game autodetection (see -taglist for variants)
    -pkgver=nnn     override package version (advanced option!)
    -pkg=package    load extra package (in addition to <package>)
    -obj=object     specify object(s) to load
    -gui            force startup UI to appear
    -aes=key        provide AES decryption key for encrypted pak files,
                    key is ASCII or hex string (hex format is 0xAABBCCDD),
                    multiple options could be provided for multi-key games
    -aes=@file.txt  read AES decryption key(s) from a text file

Compatibility options:
    -nomesh         disable loading of SkeletalMesh classes in a case of
                    unsupported data format
    -noanim         disable loading of MeshAnimation classes
    -nostat         disable loading of StaticMesh class
    -novert         disable loading of VertMesh class
    -notex          disable loading of Material classes
    -nomorph        disable loading of MorphTarget class
    -nolightmap     disable loading of Lightmap textures
    -sounds         allow export of sounds
    -3rdparty       allow 3rd party asset export (ScaleForm, FaceFX)
    -lzo|lzx|zlib   force compression method for UE3 fully-compressed packages

Platform selection:
    -ps3            Playstation 3
    -ps4            Playstation 4
    -nsw            Nintendo Switch
    -ios            iOS (iPhone/iPad)
    -android        Android

Viewer options:
    -meshes         view meshes only
    -materials      view materials only (excluding textures)
    -anim=<set>     specify AnimSet to automatically attach to mesh

Export options:
    -out=PATH       export everything into PATH instead of the current directory
    -all            used with -dump, will dump all objects instead of specified one
    -uncook         use original package name as a base export directory (UE3)
    -groups         use group names instead of class names for directories (UE1-3)
    -uc             create unreal script when possible
    -psk            use ActorX format for meshes (default)
    -md5            use md5mesh/md5anim format for skeletal mesh
    -gltf           use glTF 2.0 format for mesh
    -lods           export all available mesh LOD levels
    -dds            export textures in DDS format whenever possible
    -png            export textures in PNG format instead of TGA
    -notgacomp      disable TGA compression
    -nooverwrite    prevent existing files from being overwritten (better
                    performance)

取得资产路径

  1. 批量选择动画资产
  2. 右键点击Copy package path

/Game/Character/ch/ch0200/05_Animation/AS_ch0200_906_FC_smile_m.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_901_FC_default_m.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_906_FC_smile_m.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_911_FC_angry_m.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_916_FC_sad_m.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_921_FC_surprise_m.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_926_FC_fear_m.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_931_FC_aversion_m.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_936_FC_damage_m.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_937_FC_dead.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_941_FC_serious_m.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_946_FC_blushing_m.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_951_FC_ex01_m.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_AL_pose001_ST.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_AL_pose002_ST.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_AL_pose003_ST.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_AL_pose004_ST.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_AL_pose005_ST.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_AL_pose006_ST.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_AL_pose007_ST.uasset

解决Blender导出问题的根骨骼问题

使用Blender的Better Fbx Importer & Exporter addon就可以解决问题。

Umodel源码

UModel提取声音教程

https://www.gildor.org/smf/index.php/topic,6389.msg34339.html#msg34339 https://www.gildor.org/smf/index.php?topic=7271.0 https://www.youtube.com/watch?v=Nxl0OcoArZ8

  1.  使用 Umodel 或 QuickBMS 与 Unreal Tournament 4 脚本来提取 UEXP
    1. 下载QuickBMS https://aluigi.altervista.org/quickbms.htm
  2. 使用 VGMToolbox 在其“Common Archives”部分 。中解压 UEXP 。VGMToolbox\Misc.Tools\Extraction Tools\Common Archives\CRI ACB/AWB Archive Extractor选项,并将所有提取的 UEXP 包拖到右侧窗口中。
    1. https://sourceforge.net/projects/vgmtoolbox/
  3. 这将获取 .bin可以使用“Streams”部分中的 VGMToolbox 的 HCA 提取器将这些文件提取为 HCA。
  4. 然后可以使用以下任何工具将 HCA 转换为 wav
    1. Foobar with VGMStream http://www.foobar2000.org/download http: //www.foobar2000.org/components/view/foo_input_vgmstream
    2. Thealexbarney 的 VGAudio https://github.com/Thealexbarney/VGAudio
    3. Nyagamon 的 HCADecoder https://github.com/Nyagamon/HCADecoder

ffmpeg -i input.awb -acodec libmp3lame -ab 192k output.mp3

鸣潮

鸣潮个版本ASE:https://github.com/ClostroOffi/wuwa-aes-archive

Version Operating System Build Server Main AES Keys pakchunk-5 AES Keys Status
0.1.0 Windows Technical Test Build CN 0x773630F7D0C0516311D0598C538EB94AABAD423208F3633A98E34513557DE2C5 None Functional
0.7.0 Windows CBT1 (Closed Beta Test) Build OS & CN 0x3CD6493EFB7AE59DC3452D0F9CFF44D2CAD4BDC1FBA00DF7CC4FA4707A81AD75 None Functional
0.8.0 Windows pre-CBT2 (NDA Test Build) CN 0xF259C330E6B308BF34086CF30013241A1277F6E25D8F580746C2E8829EA1E15F 0xAEA4CBC6E1B6CCCD49F2426087AFDDC1F84662B45019BB6CBFFD62F470AFCDD5 Functional
0.9.0 Windows CBT2 (Closed Beta Test) Build OS & CN 0xF259C330E6B308BF34086CF30013241A1277F6E25D8F580746C2E8829EA1E15F 0xF0ABFF9E59F0A1C5723AA146CBC190B641E5528E18F2AF1AA364A741E80E3EC8 Functional
0.10.0 Windows Localization Pre-release Test Build CN 0xE0D4C0AA387A268B29C397E3C0CAD934522EFC96BE5526D6288EA26351CDACC9 None Functional
1.0 Windows Release Build OS 0xE0D4C0AA387A268B29C397E3C0CAD934522EFC96BE5526D6288EA26351CDACC9 0xE8F50588DEDAE3C5158E78FA7349A2C1E09CEC20B4A0FA4B2CF82C0ADDEFE2EA Functional
1.0 Android Release Build OS 0xE0D4C0AA387A268B29C397E3C0CAD934522EFC96BE5526D6288EA26351CDACC9 0x3BE656CDF13186E14E15E2A088FDB6D888DF8890501305F882B6751D655BC91D Untested
1.0 iOS & iPad OS Release Build OS 0xE0D4C0AA387A268B29C397E3C0CAD934522EFC96BE5526D6288EA26351CDACC9 0x78D8F3814E6EB5AE71466DEDE1BA6F1C22DB5769455924B9D10A4C3E89E364D9 Untested
1.0 Windows Release Build CN 0xE0D4C0AA387A268B29C397E3C0CAD934522EFC96BE5526D6288EA26351CDACC9 0x030115E0B73D89503D64D89FA4CA4966C16FC3B366AEE6A1BE3954F5EDD09661 Functional
1.0 Android Release Build CN 0xE0D4C0AA387A268B29C397E3C0CAD934522EFC96BE5526D6288EA26351CDACC9 0x9D0B462C529DBA54DF23E18FAA758591798ED477AC664316F1C1F97992F0C3CE Untested
1.0 iOS & iPad OS Release Build CN 0xE0D4C0AA387A268B29C397E3C0CAD934522EFC96BE5526D6288EA26351CDACC9 0x71278FF9F0C0B607908EFCAC4EA412E763144F011746EE0BF55A86EE950DE787 Untested
1.1 Windows Beta Build CN 0x47445D63F10E5EB004B8D3352A58C3E8444E1F7D1907A442D204161C71C567DC None Functional
1.1 Windows Release Build OS 0x43C51CC2369B9DD195EDCF426C78E30E99D7514DC14E8C03A831E128A3941010 0x52B3F2003A28C3145C98866BEECC3F884051140E03CC42946A89DB126AD55E9C Functional
1.1 Android Release Build OS 0x43C51CC2369B9DD195EDCF426C78E30E99D7514DC14E8C03A831E128A3941010 0xB7370A8CB4BF5BECACA1325DDC0FF17F7E20C1145962D2F5CEA4CD0BB984EE Untested
1.1 iOS & iPad OS Release Build OS 0x43C51CC2369B9DD195EDCF426C78E30E99D7514DC14E8C03A831E128A3941010 0x274CEFEA28768C320ECA33736968D98EE31DEC6C32536A862C8E7954EA0EB3F7 Untested
1.1 Windows Release Build CN 0x43C51CC2369B9DD195EDCF426C78E30E99D7514DC14E8C03A831E128A3941010 0xC6F1A514CC58A88C1FE934E93DBD6E532EA5A00C2A2C9E4AE7774832BFF0686D Functional
1.1 Android Release Build CN 0x43C51CC2369B9DD195EDCF426C78E30E99D7514DC14E8C03A831E128A3941010 0x6926CEAC6639B4E18DA566BB00BC4F0BE67E98768E4121C733F3AC033E1D4586 Untested
1.1 iOS & iPad OS Release Build CN 0x43C51CC2369B9DD195EDCF426C78E30E99D7514DC14E8C03A831E128A3941010 0xA10A653F18F3497FABC2F8EC39EACA8AC3379CD53B92CEA17C05B41A0C4507C8 Untested
1.2 Windows Beta Build CN 0x4D65747EDEB74A1DE116B1DD147CF79CD6C082F0DB7908E1BBD37F0428426469 None Functional
1.2 Windows Release Build OS 0x4D65747EDEB74A1DE116B1DD147CF79CD6C082F0DB7908E1BBD37F0428426469 0xB8B2D6B3DE6DA30113D7139BA95BD62E5E91EEAAAA3EBA7F7CD8261EEAA7F992 Functional
1.2 Android Release Build OS 0x4D65747EDEB74A1DE116B1DD147CF79CD6C082F0DB7908E1BBD37F0428426469 0x1EB5B065960D66252A35CD1AC712078E8F471C0BA93B0CFFFC958F3D2112B9F1F Untested
1.2 iOS & iPad OS Release Build OS 0x4D65747EDEB74A1DE116B1DD147CF79CD6C082F0DB7908E1BBD37F0428426469 0xCCCCC77B8CE81898DA9AE9FDBF7AB5B2A80A70C38177A5999010C75193586231 Untested
1.2 Windows Release Build CN 0x4D65747EDEB74A1DE116B1DD147CF79CD6C082F0DB7908E1BBD37F0428426469 0x1454A21FD285F51959AF0164EEBD8AE81D725BF499F13525539E7A1568F0E9D8 Functional
1.2 Android Release Build CN 0x4D65747EDEB74A1DE116B1DD147CF79CD6C082F0DB7908E1BBD37F0428426469 0xE8A45AF0BB37E139C328309340E0958292A8A07007FE7E3A771AA8D74099F5D6 Untested
1.2 iOS & iPad OS Release Build CN 0x4D65747EDEB74A1DE116B1DD147CF79CD6C082F0DB7908E1BBD37F0428426469 0x6651BB4632FE9D8FC5B78BF8F41DB0DF4EFFB338C127434CD093D6AA368050E3 Untested
1.3 Windows Beta Build CN 0x63B32F1F0DFB84CD4763EA5BC430D305AFDF126774CC3CCDB6CE0CEF3115256F None Functional
1.3 Windows Release Build OS 0x0B3915926D75EC12C602DB59000FC1F8AD5BA45D3A6A5B1675E4B7B54E703684 0xBDA94387C9E7F157CE31B25144A2DF54E3A1BF011A69E86485470D4132F23BC0 Functional
1.3 Android Release Build OS 0x0B3915926D75EC12C602DB59000FC1F8AD5BA45D3A6A5B1675E4B7B54E703684 TBD Untested
1.3 iOS & iPad OS Release Build OS 0x0B3915926D75EC12C602DB59000FC1F8AD5BA45D3A6A5B1675E4B7B54E703684 TBD Untested
1.3 Windows Release Build CN 0x0B3915926D75EC12C602DB59000FC1F8AD5BA45D3A6A5B1675E4B7B54E703684 TBD Functional
1.3 Android Release Build CN 0x0B3915926D75EC12C602DB59000FC1F8AD5BA45D3A6A5B1675E4B7B54E703684 TBD Untested
1.3 iOS & iPad OS Release Build CN 0x0B3915926D75EC12C602DB59000FC1F8AD5BA45D3A6A5B1675E4B7B54E703684 TBD Untested
1.4 Windows Beta Build CN 0xF031394DB4776A6DF94A04DA25774655406C4A6C6941C3788FB51C2D423206C2 None Functional

https://www.gildor.org/smf/index.php?topic=8258.0 UE4 version: 4.26 Wuthering Waves (beta)  0xF259C330E6B308BF34086CF30013241A1277F6E25D8F580746C2E8829EA1E15F

//D:\OtherTools\GameAssets\umodel_win32_1590\umodel_materials.exe -path=D:\Games\WutheringWaves -out=C:\Users\BlueRose\Desktop\WutheringWaves -game=wuther -aes=0xF259C330E6B308BF34086CF30013241A1277F6E25D8F580746C2E8829EA1E15F

//D:\OtherTools\GameAssets\umodel_win32_1590\umodel_materials.exe -path=D:\Games\WutheringWaves -out=C:\Users\BlueRose\Desktop\WutheringWaves -game=wuther -aes=0x0B3915926D75EC12C602DB59000FC1F8AD5BA45D3A6A5B1675E4B7B54E703684

国际服

D:\OtherTools\GameAssets\umodel_win32_1590\umodel_materials.exe -path=D:\Games\WutheringWaves_global -out=C:\Users\BlueRose\Desktop\WutheringWaves -game=wuther -aes=0x0B3915926D75EC12C602DB59000FC1F8AD5BA45D3A6A5B1675E4B7B54E703684

以秧秧为例:

  • 模型位于:/Game/Aki/Character/Role/FemaleM/Yangyang/R2T1YangyangMd10011/Model
  • 动画:
    • 公共动画:/Game/Aki/Character/Role/FemaleM/BaseAnim
    • 角色动画:/Game/Aki/Character/Role/FemaleM/Yangyang/CommonAnim/
      • Attack01~05
      • AirAttack系列
      • Burst01
      • Skill01~02
      • SkillQte
      • Stand1_Action01~03
      • Stand2
      • StandChange
    • AimOffset动画/Game/Aki/Sequence/SequenceAnim/DialogAnim/FemaleM/Yangyang/AnimOffset
    • 身体动画(大量动画):/Game/Aki/Sequence/SequenceAnim/DialogAnim/FemaleM/Yangyang/Body
      • cs文件夹: Apology01、Promise、Walk
      • fuyong文件夹Walk、Run
    • 面部相关动画:/Game/Aki/Sequence/SequenceAnim/DialogAnim/FemaleM/Yangyang/Face
      • ShengQi02
    • UI相关动画/Game/Aki/Character/Role/FemaleM/Yangyang/CommonAnim/UIAnim
    • 角色飘带动画:/Game/Aki/Character/Role/FemaleM/Yangyang/R2T1YangyangMd10011/RibbonAnim

植被模型存在较大概率提取出错的问题

  • Scene
    • Assets
      • Common
      • Levels
        • HeiHaiAn
        • HuangLong
          • FuBen
            • FB_ZheZhi
        • LiangDao
        • WuYinQu
      • PCG
      • SkyBox
      • Temp

罪恶装备

  1. Guilty Gear: Strive 0x3D96F3E41ED4B90B6C96CA3B2393F8911A5F6A48FE71F54B495E8F1AFD94CD73
  2. UE 4.25
//经过测试可以导出
D:\OtherTools\GameAssets\umodel_win32_1590\umodel_64.exe -path=D:\Games\Guilty.Gear.STRIVE.Deluxe.Edition -out=C:\Users\BlueRose\Desktop\GuiltyGear -game=ue4.25+ -aes=0x3D96F3E41ED4B90B6C96CA3B2393F8911A5F6A48FE71F54B495E8F1AFD94CD73

提取模型参考:



import bpy
import os
import math
from io_import_scene_unreal_psa_psk_280 import *
from better_fbx import * 

for action in bpy.data.actions:
    bpy.data.actions.remove(action) 

pskPath = 'C:\\Users\\BlueRose\\Desktop\\GranblueFantasyVersusRising\\Chara\\ZET\\Costume01\\Mesh\\zet_weapon_kaihou.psk'
psaPath = 'C:\\Users\\BlueRose\\Desktop\\GranblueFantasyVersusRising\\Chara\\ZET\\Costume01\\Animation\\Default\\weapon\\zet001.psa'
fbxExportPath = 'C:\\Users\\BlueRose\\Desktop\\GranblueFantasyVersusRising\\Chara\\ZET\\Costume01\\Animation\\Default\\weapon\\'

# 清空并且设置为60fps
bpy.ops.scene.new(type='EMPTY')
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()
bpy.context.scene.render.fps = 60


# 导入骨骼以及动画(所有NLA)
pskimport(pskPath, bReorientBones = False, bImportmesh = False, bImportbone = True)
psaimport(psaPath, bActionsToTrack = True)

for obj in bpy.data.objects:
    print(obj.type)
    if obj.type=='ARMATURE':
        currentSkeleton = obj

print(currentSkeleton.animation_data.action)

for action in bpy.data.actions:
    # print(action)
    currentSkeleton.animation_data.action = action
    bpy.ops.better_export.fbx(filepath= fbxExportPath+ action.name + ".fbx",my_fbx_format="binary",use_selection=False,use_visible=True)
    # print(currentSkeleton.animation_data.action)
    # print(bpy.data.objects["SM_ch0100.ao"].animation_data.action)

# 导入模型
import bpy
import os
import math
from io_import_scene_unreal_psa_psk_280 import *
from better_fbx import * 

for action in bpy.data.actions:
    bpy.data.actions.remove(action) 

pskPath = 'C:\\Users\\BlueRose\\Desktop\\GuiltyGear\\Chara\\\BGT\\Costume01\\Mesh\\bgt_body.psk'
# psaPath = 'C:\\Users\\BlueRose\\Desktop\\GranblueFantasyVersusRising\\Chara\\ZET\\Costume01\\Animation\\Default\\weapon\\zet001.psa'
fbxExportPath = 'C:\\Users\\BlueRose\\Desktop\\GuiltyGear\\Chara\\BGT\\Costume01\\Mesh\\'

# 清空并且设置为60fps
bpy.ops.scene.new(type='EMPTY')
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()
bpy.context.scene.render.fps = 60


# 导入骨骼以及动画(所有NLA)
pskimport(pskPath, bReorientBones = False, bImportmesh = True, bImportbone = True)
# psaimport(psaPath, bActionsToTrack = True)

fileName = os.path.basename(pskPath)
bpy.ops.better_export.fbx(filepath= fbxExportPath + fileName.split('.')[0] + ".fbx",my_fbx_format="binary",use_selection=False,use_visible=True)

提取角色

碧蓝幻想Rising

  1. Granblue Fantasy Versus 0x2A472D4B6150645367566B597033733676397924423F4528482B4D6251655468
  2. UE 4.21
//经过测试可以导出
D:\OtherTools\GameAssets\umodel_win32_1590\umodel_materials.exe -path=D:\Games\GranblueFantasyVersusRising -out=C:\Users\BlueRose\Desktop\GranblueFantasyVersusRising -game=ue4.27 -aes=0x6470C12A9B471BBA1D89A72D4F9B84EF709A65B88A85F240B8E99CD631751437

需要特殊构建版本 https://www.gildor.org/smf/index.php/topic,7906.0.html

提取角色

  • ZET 赛达
    • zet000
    • zet001
  • NRM 梅露娜
    • nrm400
    • nrm000
  • DJT 姬塔
    • djt000
    • djt610
  • CAL 卡莉奥丝特罗
    • cal000
  • 2B
    • twb000
  • YUE 尤艾尔
    • yue000

破晓传说

  • Tales of Arise 0x61FC71A1376A29598393E2BFB976B275705F460E29399CFD1F9C060C8CD6CE40
  • UE 4.20 ? 4.25?
  • UModel 1571

碧蓝幻想Relink

https://www.bilibili.com/video/BV15M4m197TG/?spm_id_from=333.337.search-card.all.click&vd_source=f333e2edf5f07eeccd47911824e738a3

尘白禁区

umodel_acl_2.1 UE4.26 0xC14735FB5A872D2AFA76A5C38521AB8B8E21072C08525B913307608BD1182FA7 https://live2dhub.com/t/topic/3103/11

少女前线2

https://github.com/nijinekoyo/GF2AssetBundleDecryption https://blog.csdn.net/weixin_45346745/article/details/137476906

原神 & 星穹铁道

  • Star Rail\Game\StarRail_Data\StreamingAssets\Asb\Windows
  • Genshin Impact\Genshin Impact Game\YuanShen_Data\StreamingAssets
    • 1.36

原神

CaravanSandwitch

D:\OtherTools\GameAssets\umodel_win32_1590\umodel_materials_ue5.exe -path=D:\Games\CaravanSandwitch -out=C:\Users\BlueRose\Desktop\CaravanSandwitch -game=ue5.4

Pak反汇编&UModel

Pak AdditiveAnimation 恢复

脚本分析

BaseAnimation

主要逻辑:

  1. 根据路径导入fbx文件。
  2. 获取所有骨骼数据。
  3. 骨骼进行缩放。
on BtnBase pressed  do
(
	local filename = getOpenFileName types:"Base Animation (*.fbx)|*.fbx|All (*.*)|*.*|" filename:g_lastDir1
	if filename != undefined then
	(
		BaseAnimFileName = filename
		g_lastDir1 = getFilenamePath BaseAnimFileName
		if DoesFileExist BaseAnimFileName then
		(
			importFile BaseAnimFileName #noPrompt using:Wavefront_Object
			local BaseAnimBones = FindAllBones()
			scale BaseAnimBones[1] [(50.0/127.0),(50.0/127.0),(50.0/127.0)]
		)
	)
)

AdditiveAnimation

将路径正确的AdditiveAnimation动画路径加入LstAnims中。

Anims = #()
if DotNetObject == undefined then
(
	local filename = getOpenFileName types:"Additive Anim (*.fbx)|*.fbx|All (*.*)|*.*" filename:g_lastDir2
	if filename != undefined then
	(
		AddAnimFileName = filename
		g_lastDir2 = getFilenamePath AddAnimFileName
	)
)
else
(
	local filenames = getMultiOpenFilenames types:"Additive Anim (*.fbx)|*.fbx|All (*.*)|*.*" filename:g_lastDir2
	if filenames != undefined then
	(
		for filename in filenames do
		(
			AddAnimFileName = filename
			g_lastDir2 = getFilenamePath AddAnimFileName
			if DoesFileExist AddAnimFileName then
			(
				ImportAddAnimFile filename
				LstAnims.items = for a in Anims collect (a.Name)
			)
		)
	)
)

ExportAnimation

核心逻辑 BtnExport遍历AdditiveAnim并调用CalculateAddRotation。 追要参数为:

  • XMap
  • YMap
  • ZMap
  • AixMap
  • boneNum是否包含pelvis骨骼
on BtnExport pressed  do
(
	BoneRotationArray = #()
	if (LstAnims.items.count>0 and BaseAnimFileName != undefined) then
	(
		XMap = DDLX.selection
		YMap = DDLY.selection
		ZMap = DDLZ.selection
		AixMap = DDLAix.selection
		if g_pelvis then 
		(
			boneNum = 1
		)
		else boneNum = 2
		for i=1 to LstAnims.items.count do
		(
			CalculateAddRotation i
		)
	)
)

CalculateAddRotation:

fn CalculateAddRotation index =
(
	//导入AdditiveAnim
	importFile Anims[index].Address #noPrompt using:Wavefront_Object
	local bones = FindAllBones()
	//清空数组
	BoneRotationArray = #()

	//遍历骨骼从第二根骨骼也就是Pelvis开始。仅仅针对UE骨骼Bip得重新考虑
	for j=1 to bones.count do
	(
		if j>boneNum then 
		(
			//计算当前骨骼相对父骨骼的相对旋转。
			local Value_X = ((at time 0 bones[j].rotInParent).x - (at time 0 bones[j].parent.rotInParent).x)
			local Value_Y = ((at time 0 bones[j].rotInParent).y - (at time 0 bones[j].parent.rotInParent).y)
			local Value_Z = ((at time 0 bones[j].rotInParent).z - (at time 0 bones[j].parent.rotInParent).z)
			//针对XYZ情况的不同将数据存入BoneRotationArray数组。
			if (abs Value_X) > 0.05 then
			(
				if (abs Value_Y) > 0.05 then
				(
					if (abs Value_Z) > 0.05 then
					(
						append BoneRotationArray (SaveValue j bones[j].name Value_X Value_Y Value_Z)	
					)
					else
					(
						append BoneRotationArray (SaveValue j bones[j].name Value_X Value_Y 0)
					)
				)
				else
				(
					if (abs Value_Z) > 0.05 then
					(
						append BoneRotationArray (SaveValue j bones[j].name Value_X 0 Value_Z)	
					)
					else
					(
						append BoneRotationArray (SaveValue j bones[j].name Value_X 0 0)
					)
				)
			)
			else
			(
				if (abs Value_Y) > 0.05 then
				(
					if (abs Value_Z) > 0.05 then
					(
						append BoneRotationArray (SaveValue j bones[j].name 0 Value_Y Value_Z)
					)
					else
					(
						append BoneRotationArray (SaveValue j bones[j].name 0 Value_Y 0)
					)
				)
				else
				(
					if (abs Value_Z) > 0.05 then
					(
						append BoneRotationArray (SaveValue j bones[j].name 0 0 Value_Z)
					)
				)
			)
		)
	)
	--Set Base Animation Rotation
	//导入BaseAnim
	if DoesFileExist BaseAnimFileName then
	(
		importFile BaseAnimFileName #noPrompt using:Wavefront_Object
		local BaseAnimBones = FindAllBones()
		scale BaseAnimBones[1] [(50.0/127.0),(50.0/127.0),(50.0/127.0)]
		//调用RotationBone()来设置新的旋转数值
		case AixMap of
		(
			1:(	RotationBone 1 0 0 BoneRotationArray XMap BaseAnimBones
				RotationBone 0 1 0 BoneRotationArray YMap BaseAnimBones
				RotationBone 0 0 1 BoneRotationArray ZMap BaseAnimBones)
			2:( RotationBone 1 0 0 BoneRotationArray XMap BaseAnimBones
				RotationBone 0 0 1 BoneRotationArray ZMap BaseAnimBones
				RotationBone 0 1 0 BoneRotationArray YMap BaseAnimBones)
			3:( RotationBone 0 1 0 BoneRotationArray YMap BaseAnimBones
				RotationBone 1 0 0 BoneRotationArray XMap BaseAnimBones
				RotationBone 0 0 1 BoneRotationArray ZMap BaseAnimBones)
			4:( RotationBone 0 1 0 BoneRotationArray YMap BaseAnimBones
				RotationBone 0 0 1 BoneRotationArray ZMap BaseAnimBones
				RotationBone 1 0 0 BoneRotationArray XMap BaseAnimBones)
			5:( RotationBone 0 0 1 BoneRotationArray ZMap BaseAnimBones
				RotationBone 1 0 0 BoneRotationArray XMap BaseAnimBones
				RotationBone 0 1 0 BoneRotationArray YMap BaseAnimBones)
			6:( RotationBone 0 0 1 BoneRotationArray ZMap BaseAnimBones
				RotationBone 0 1 0 BoneRotationArray YMap BaseAnimBones
				RotationBone 1 0 0 BoneRotationArray XMap BaseAnimBones)
		)

		ExportAnim Anims[index].Address
	)
)

RotationBone()

fn RotationBone X Y Z BoneArray Map BaseAnimBones =
(
	local RotationValue = 0
	for a=1 to BoneArray.count do
	(
		case Map of
		(
			1:(RotationValue = BoneArray[a].ValueX)
			2:(RotationValue = BoneArray[a].ValueY)
			3:(RotationValue = BoneArray[a].ValueZ)
			4:(RotationValue = (BoneArray[a].ValueX)*(-1))
			5:(RotationValue = (BoneArray[a].ValueY)*(-1))
			6:(RotationValue = (BoneArray[a].ValueZ)*(-1))
			--default:(RotationValue = BoneRotationArray[a].ValueX)
		)
		in coordsys world rotate BaseAnimBones[(BoneArray[a].Index)] (EulerAngles (RotationValue*X) (RotationValue*Y) (RotationValue*Z))
	)
)

适配鸣潮的思路

  1. 修改骨盆骨骼与脊椎骨骼相关逻辑。

    1. FindAllBones_Recurse。
  2. 增加BoneFilter移除其他WeaponBone的影响。

  3. 增加对Bip骨骼的适配逻辑。

  4. g_spineName  = "spine_01"

  5. parent.name == "pelvis"

其他提取鸣潮资源与Renderdoc截帧方法

!提取鸣潮资产与截帧.png