UnrealPak

UnrealPak 是 UE 的资源打包工具,当进行打包和热更资源时需要把 Cook 后的 uasset 资源打包成一个 pak 文件,供运行时加载。
它支持的参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Usage:
UnrealPak <PakFilename> -Test
UnrealPak <PakFilename> -List [-ExcludeDeleted]
UnrealPak <PakFilename> <GameUProjectName> <GameFolderName> -ExportDependencies=<OutputFileBase> -NoAssetRegistryCache -ForceDependsGathering
UnrealPak <PakFilename> -Extract <ExtractDir> [-Filter=<filename>]
UnrealPak <PakFilename> -Create=<ResponseFile> [Options]
UnrealPak <PakFilename> -Dest=<MountPoint>
UnrealPak <PakFilename> -Repack [-Output=Path] [-ExcludeDeleted] [Options]
UnrealPak <PakFilename1> <PakFilename2> -diff
UnrealPak <PakFolder> -AuditFiles [-OnlyDeleted] [-CSV=<filename>] [-order=<OrderingFile>] [-SortByOrdering]
UnrealPak <PakFilename> -WhatsAtOffset [offset1] [offset2] [offset3] [...]
UnrealPak <PakFolder> -GeneratePIXMappingFile -OutputPath=<Path>
UnrealPak -TestEncryption
Options:
-blocksize=<BlockSize>
-bitwindow=<BitWindow>
-compress
-encrypt
-order=<OrderingFile>
-diff (requires 2 filenames first)
-enginedir (specify engine dir for when using ini encryption configs)
-projectdir (specify project dir for when using ini encryption configs)
-encryptionini (specify ini base name to gather encryption settings from)
-extracttomountpoint (Extract to mount point path of pak file)
-encryptindex (encrypt the pak file index, making it unusable in unrealpak without supplying the key)
-compressionformat[s]=<Format[,format2,...]> (set the format(s) to compress with, falling back on failures)

直接在编辑器中执行打包的命令(UE4.22.3,这里在指定了加密):

1
2
3
4
5
6
7
8
9
"D:\UnrealEngine\UE_4.22\Engine\Binaries\Win64\UnrealPak.exe" "C:\Users\Administrator\Documents\Unreal Projects\Mobile422\Saved\StagedBuilds\WindowsNoEditor\Mobile422\Content\Paks\Mobile422-WindowsNoEditor.pak"
-create="C:\Users\Administrator\AppData\Roaming\Unreal Engine\AutomationTool\Logs\C+Program+Files+Epic+Games+UE_4.22\PakList_Mobile422-WindowsNoEditor.txt"
-cryptokeys="C:\Users\Administrator\Documents\Unreal Projects\Mobile422\Saved\Cooked\WindowsNoEditor\Mobile422\Metadata\Crypto.json"
-order="C:\Users\Administrator\Documents\Unreal Projects\Mobile422\Build\WindowsNoEditor\FileOpenOrder\CookerOpenOrder.log"
-encryptindex
-patchpaddingalign=2048
-compressionformats= "C:\Users\Administrator\Documents\Unreal Projects\Mobile422\Mobile422.uproject"
-multiprocess
-abslog="C:\Users\Administrator\AppData\Roaming\Unreal Engine\AutomationTool\Logs\C+Program+Files+Epic+Games+UE_4.22\UnrealPak-Mobile422-WindowsNoEditor-2020.02.11-18.48.15.txt"

cryptokeys 指定参数的结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
{
"$types":{
"UnrealBuildTool.EncryptionAndSigning+CryptoSettings, UnrealBuildTool, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null":"1",
"UnrealBuildTool.EncryptionAndSigning+EncryptionKey, UnrealBuildTool, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null":"2",
"UnrealBuildTool.EncryptionAndSigning+SigningKeyPair, UnrealBuildTool, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null":"3",
"UnrealBuildTool.EncryptionAndSigning+SigningKey, UnrealBuildTool, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null":"4"
},
"$type":"1",
"EncryptionKey":{
"$type":"2",
"Name":"Embedded",
"Guid":"00000000-0000-0000-0000-000000000000",
"Key":"+c0unLTPVpJ8E2MAs3SrUh/bRHasFOvq1kKXnmZBZcw="
},
"SigningKey":{
"$type":"3",
"PublicKey":{
"$type":"4",
"Exponent":"AQAB",
"Modulus":"m48Hq1rQKqljGUBCVku+qxFoa1oVBXghOWKSPArwl9uixba6pxlgqyV/BINWRYQMzgcdKPvGgusRIlalPqEEQB9XibCqJahUpxszoNkhH33cWSpMKZ8XWNmnvAZvtebpqtJYVP1ebqNGvCEm+e54dsxvRJJGmcOB6Wi36/l4c9/+zgNF5BZQItOlDc8OCYlttAvDhgExDow4leNuU/nBh12rcKD4P0KmIYdPzFBYgTe18DyJ12GKf2rStFF9VDlTsp+Gl7ZejPzQ7/CsX0eSHHplsZjjCB6mlmBStFOY7OWMLS/pnYAQ6Ywnaf+tBNz4Fd9eBBxwD0Vn+dU4YDxEJYvZuVkOqyio581jHn5U7rjTofzTyHvy5tzWuYF52R2dGkcTBgjkbmE0Z6nkZ2TdPEog15eZClsHHMseyzhBa3LCC2nmnvZqppV4+Ijj5e1XKEobPOgVZMejXNukW1dzfX9gMf1NrOD2KX2cnyPT6IvP6zu5Cztiy3rVtBiDtQIaQnaCsix8t0+oh5aqzhj6GrblcYs1JX/uy0OYO+hO/yTLET378TehhRjYbnHQsS2PBqBVM6i7hK7xT1TxuUE5oJYJULDWmOn9AZKmnzkx+VyQgmqmNA/L0vJqOhsDvvA29UGNEn5junydqkQlLP4OovlZcoic2R/tYPFcwMKPp68="
},
"PrivateKey":{
"$type":"4",
"Exponent":"URhnOOSPQp7VG5FxGq2wLQ+k3FJUQEy+GWOYxbk8KKNwtbt/Fy+agBMtuZOuLKmhjWrIpU0+i78rM7cFur3qXI5TUFZ1Jj2eIhxrphoKnu99FmcLEIICtFp0Y/rbyv1RmBSvh9E+kMebvw2KGlVVl8JNhRnyZtdHEoWN7YV+zeSNuGDh8h6pJkN8b15PuXZhQw9RKa3WRsNy/3PCVkNILsOI7L+VZTQ3Zl0q3DghlaHNZlkeyLp/uSnArayUdWlTBq8H0ZtyaAlgBhFNnh/CkQFenpnz2EKSO4aMGo6NkIjbYEYoK5t119z0S56JbzGA71iDqW3VxK5KxZcDLItU8yGp7zi6SPAJXvUxg/8rnXRZ/nm+8KVLwi3Itog8DWYR7zx4weNd2Tx4JvHx+FatLnM3ut6yiUiaW6uL0Zg+yLg1PXy5bEwlAlke/9c/Z7gyY+cLQVMv525LgMQQDje/wNaQT2QosN5Cum+5jPq/0BYB4kXeotvASS2FLRJyo43JjmyfP6oQLOtSNDqbf1jl4JtjoCXBFAZKz6cR4RP1CWst4mMLUJqgOjQeSdd5ihdYZErKPzlLpghQixC/dx3W0ISzkp9b22Ee302bHQHwio+4Gc8B2e/Iabd8TQZuq8LY9BSrllHda8NsG3TFt3hEC+fjdhbz5ur4nd1xgtPtP00=",
"Modulus":"m48Hq1rQKqljGUBCVku+qxFoa1oVBXghOWKSPArwl9uixba6pxlgqyV/BINWRYQMzgcdKPvGgusRIlalPqEEQB9XibCqJahUpxszoNkhH33cWSpMKZ8XWNmnvAZvtebpqtJYVP1ebqNGvCEm+e54dsxvRJJGmcOB6Wi36/l4c9/+zgNF5BZQItOlDc8OCYlttAvDhgExDow4leNuU/nBh12rcKD4P0KmIYdPzFBYgTe18DyJ12GKf2rStFF9VDlTsp+Gl7ZejPzQ7/CsX0eSHHplsZjjCB6mlmBStFOY7OWMLS/pnYAQ6Ywnaf+tBNz4Fd9eBBxwD0Vn+dU4YDxEJYvZuVkOqyio581jHn5U7rjTofzTyHvy5tzWuYF52R2dGkcTBgjkbmE0Z6nkZ2TdPEog15eZClsHHMseyzhBa3LCC2nmnvZqppV4+Ijj5e1XKEobPOgVZMejXNukW1dzfX9gMf1NrOD2KX2cnyPT6IvP6zu5Cztiy3rVtBiDtQIaQnaCsix8t0+oh5aqzhj6GrblcYs1JX/uy0OYO+hO/yTLET378TehhRjYbnHQsS2PBqBVM6i7hK7xT1TxuUE5oJYJULDWmOn9AZKmnzkx+VyQgmqmNA/L0vJqOhsDvvA29UGNEn5junydqkQlLP4OovlZcoic2R/tYPFcwMKPp68="
}
},
"bEnablePakSigning":true,
"bEnablePakIndexEncryption":true,
"bEnablePakIniEncryption":true,
"bEnablePakUAssetEncryption":true,
"bEnablePakFullAssetEncryption":true,
"bDataCryptoRequired":true,
"PakEncryptionRequired":true,
"PakSigningRequired":true,
"SecondaryEncryptionKeys":[
{
"$type":"2",
"Name":"Key1",
"Guid":"757096074E55F5FCA962949C55209CCB",
"Key":"SsukCy4DShNsMi5e9jUpMWJi5qtTA+rLeIoTHRtjM0o="
}
]
}

它是根据 Project Setting-Crypto 中的设置生成的。

UnrealPak 比较常用的命令组合:

1
2
3
4
5
6
7
8
# 将 COOCKED_ASSET_FOLDER 路径下所有 cookedd 的资源打包到一个 pak 里面,并执行压缩 
unrealpak.exe NEW_PAK_FILE_NAME.pak -create=COOCKED_ASSET_FOLDER -compress
# 加密 pak,需要指定32 位的 AES key,要执行加密 -encrypt/-encrtptindex/-aes 缺一不可。
unrealpak.exe NEW_PAK_FILE_NAME.pak -create=COOCKED_ASSET_FOLDER -compress -encrypt -encryptindex -aes=32BIT_AES_KEY
# 查看 pak 中的资源列表
unrealpak.exe PAK_FILE_NAME.pak -list
# 查看加密的 pak 中的资源列表
unrealpak.exe NEW_PAK_FILE_NAME.pak -list -aes=32BIT_AES_KEY

相关工具:

Project Setting-Project-Crypto 中可以选择加密项目并且直接生成EncryptionKey

UnrealPak 是一个 StandaloneApplication 程序,它的启动代码在:Engine/Source/Programs/UnrealPak/Private/UnrealPak.cpp.
但是它只是一个转发函数,真正的实现代码是在引擎中的 ExecuteUnrealPak 函数,其在引擎中的位置为Engine/Source/Developer/PakFileUtilities/Private/PakFileUtilities.cpp

UnrealPak 为 pak 生成 sig:

注意:在 UE4.23+ 引擎版本中,在指定 cryptokeys 参数时,需要同时指定-sign

1
$ UnrealPak.exe D:\TEST_SIG.pak -create="XXXXXXX.txt" -cryptokeys="Crypto.json"

其中 Crypto.json 文件在你使用编辑器打包的时候会生成,它是根据 Project Setting-Crypto 中的设置生成的,保存在路径:

1
PROJECT_DIRECTORY\Saved\Cooked\WindowsNoEditor\PROJECT_NAME\Metadata\Crypto.json

注意 PROJECT_DIRECTORYPROJECT_NAME都换成你自己的。

Crypto.json这个文件的生成是在 AutomationTool 这个工具里面的,相关的代码在:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// Source\Programs\AutomationTool\Scripts\CopyBuildToStagingDirectory.Automation.cs

/// <summary>
/// Creates a pak file using staging context (single manifest)
/// </summary>
/// <param name="Params"></param>
/// <param name="SC"></param>
private static void CreatePakUsingStagingManifest(ProjectParams Params, DeploymentContext SC)
{
LogInformation("Creating pak using staging manifest.");

DumpManifest(SC, CombinePaths(CmdEnv.LogFolder, "PrePak" + (SC.DedicatedServer ? "_Server" : "")));

var UnrealPakResponseFile = CreatePakResponseFileFromStagingManifest(SC, SC.FilesToStage.UFSFiles);

List<PakFileRules> PakRulesList = GetPakFileRules(Params, SC);

List<string> PakList = new List<string>();
List<string> FilesToRemove = new List<string>();

// Apply the pak file rules, this can remove things but will not override the pak file name
foreach (var StagingFile in UnrealPakResponseFile)
{
bool bExcludeFromPaks = false;
ApplyPakFileRules(PakRulesList, StagingFile, PakList, out bExcludeFromPaks);

if (bExcludeFromPaks)
{
FilesToRemove.Add(StagingFile.Key);
}
}

foreach (var FileToRemove in FilesToRemove)
{
UnrealPakResponseFile.Remove(FileToRemove);
}

EncryptionAndSigning.CryptoSettings PakCryptoSettings = EncryptionAndSigning.ParseCryptoSettings(DirectoryReference.FromFile(Params.RawProjectPath), SC.StageTargetPlatform.IniPlatformType);
FileReference CryptoKeysCacheFilename = FileReference.Combine(SC.MetadataDir, "Crypto.json");
PakCryptoSettings.Save(CryptoKeysCacheFilename);

List<CreatePakParams> PakInputs = new List<CreatePakParams>();
PakInputs.Add(new CreatePakParams(SC.ShortProjectName, UnrealPakResponseFile, Params.Compressed, null));
CreatePaks(Params, SC, PakInputs, PakCryptoSettings, CryptoKeysCacheFilename);
}

EncryptionAndSigning这个类是定义在UnrealBuildTool\System\EncryptionAndSigning,在里面可以看到对 DefaultCrypto.ini 文件的解析。