- temp fix for shader class.
- added new entry.
- more updates to CLI
- fixes #3
- add #4
This commit is contained in:
Razmoth
2023-04-27 23:05:10 +04:00
parent c7d60450f8
commit 7b0d563de1
20 changed files with 436 additions and 332 deletions

View File

@@ -78,7 +78,7 @@ namespace AssetStudio
if (await NeedDownload(version, versionIndex.MappedPath))
{
Logger.Info("Downloading...");
var json = await DownloadString(url, TimeSpan.FromMinutes(1));
var json = await DownloadString(url, TimeSpan.FromMinutes(2));
if (string.IsNullOrEmpty(json))
{
Logger.Warning("Could not load AI !!");

View File

@@ -8,6 +8,7 @@ using Newtonsoft.Json.Converters;
using Newtonsoft.Json;
using System.Text.RegularExpressions;
using System.Xml;
using System.Text;
namespace AssetStudio
{
@@ -82,10 +83,11 @@ namespace AssetStudio
public static void BuildMap(string[] files, string mapName, string baseFolder, Game game)
{
Logger.Info($"Processing...");
Logger.Info($"Building Map...");
try
{
Map.Clear();
Progress.Reset();
var collision = 0;
BaseFolder = baseFolder;
assetsManager.Game = game;
@@ -118,7 +120,8 @@ namespace AssetStudio
}
Map.Add(assetsFile.fileName, entry);
}
Logger.Info($"Processed {Path.GetFileName(file)}");
Logger.Info($"[{i + 1}/{files.Length}] Processed {Path.GetFileName(file)}");
Progress.Report(i + 1, files.Length);
}
assetsManager.Clear();
}
@@ -156,7 +159,9 @@ namespace AssetStudio
public static void BuildBoth(string[] files, string mapName, string baseFolder, Game game, string savePath, ExportListType exportListType, ManualResetEvent resetEvent = null, Regex[] nameFilters = null, Regex[] containerFilters = null)
{
Logger.Info($"Building Both...");
Map.Clear();
Progress.Reset();
var collision = 0;
BaseFolder = baseFolder;
assetsManager.Game = game;
@@ -203,74 +208,89 @@ namespace AssetStudio
Source = file,
PathID = objectReader.m_PathID,
Type = objectReader.type,
Container = ""
Container = string.Empty,
Name = string.Empty
};
var exportable = true;
switch (objectReader.type)
try
{
case ClassIDType.AssetBundle:
var assetBundle = new AssetBundle(objectReader);
foreach (var m_Container in assetBundle.m_Container)
{
var preloadIndex = m_Container.Value.preloadIndex;
var preloadSize = m_Container.Value.preloadSize;
var preloadEnd = preloadIndex + preloadSize;
for (int k = preloadIndex; k < preloadEnd; k++)
switch (objectReader.type)
{
case ClassIDType.AssetBundle:
var assetBundle = new AssetBundle(objectReader);
foreach (var m_Container in assetBundle.m_Container)
{
containers.Add((assetBundle.m_PreloadTable[k], m_Container.Key));
var preloadIndex = m_Container.Value.preloadIndex;
var preloadSize = m_Container.Value.preloadSize;
var preloadEnd = preloadIndex + preloadSize;
for (int k = preloadIndex; k < preloadEnd; k++)
{
containers.Add((assetBundle.m_PreloadTable[k], m_Container.Key));
}
}
}
obj = null;
asset.Name = assetBundle.m_Name;
exportable = false;
break;
case ClassIDType.GameObject:
var gameObject = new GameObject(objectReader);
obj = gameObject;
asset.Name = gameObject.m_Name;
exportable = false;
break;
case ClassIDType.Shader:
asset.Name = objectReader.ReadAlignedString();
if (string.IsNullOrEmpty(asset.Name))
{
var m_parsedForm = new SerializedShader(objectReader);
asset.Name = m_parsedForm.m_Name;
}
break;
case ClassIDType.Animator:
var component = new PPtr<Object>(objectReader);
animators.Add((component, asset));
break;
case ClassIDType.MiHoYoBinData:
var MiHoYoBinData = new MiHoYoBinData(objectReader);
obj = MiHoYoBinData;
exportable = true;
break;
case ClassIDType.IndexObject:
var indexObject = new IndexObject(objectReader);
obj = null;
foreach (var index in indexObject.AssetMap)
{
mihoyoBinDataNames.Add((index.Value.Object, index.Key));
}
asset.Name = "IndexObject";
break;
case ClassIDType.Font:
case ClassIDType.Material:
case ClassIDType.Texture:
case ClassIDType.Mesh:
case ClassIDType.Sprite:
case ClassIDType.TextAsset:
case ClassIDType.Texture2D:
case ClassIDType.VideoClip:
case ClassIDType.AudioClip:
asset.Name = objectReader.ReadAlignedString();
break;
default:
exportable = false;
break;
obj = null;
asset.Name = assetBundle.m_Name;
exportable = false;
break;
case ClassIDType.GameObject:
var gameObject = new GameObject(objectReader);
obj = gameObject;
asset.Name = gameObject.m_Name;
exportable = false;
break;
case ClassIDType.Shader:
asset.Name = objectReader.ReadAlignedString();
if (string.IsNullOrEmpty(asset.Name))
{
var m_parsedForm = new SerializedShader(objectReader);
asset.Name = m_parsedForm.m_Name;
}
break;
case ClassIDType.Animator:
var component = new PPtr<Object>(objectReader);
animators.Add((component, asset));
break;
case ClassIDType.MiHoYoBinData:
var MiHoYoBinData = new MiHoYoBinData(objectReader);
obj = MiHoYoBinData;
exportable = true;
break;
case ClassIDType.IndexObject:
var indexObject = new IndexObject(objectReader);
obj = null;
foreach (var index in indexObject.AssetMap)
{
mihoyoBinDataNames.Add((index.Value.Object, index.Key));
}
asset.Name = "IndexObject";
break;
case ClassIDType.Font:
case ClassIDType.Material:
case ClassIDType.Texture:
case ClassIDType.Mesh:
case ClassIDType.Sprite:
case ClassIDType.TextAsset:
case ClassIDType.Texture2D:
case ClassIDType.VideoClip:
case ClassIDType.AudioClip:
asset.Name = objectReader.ReadAlignedString();
break;
default:
exportable = false;
break;
}
}
catch (Exception e)
{
var sb = new StringBuilder();
sb.AppendLine("Unable to load object")
.AppendLine($"Assets {assetsFile.fileName}")
.AppendLine($"Path {assetsFile.originalPath}")
.AppendLine($"Type {objectReader.type}")
.AppendLine($"PathID {objectReader.m_PathID}")
.Append(e);
Logger.Error(sb.ToString());
}
if (obj != null)
{
@@ -319,7 +339,8 @@ namespace AssetStudio
}
}
}
Logger.Info($"Processed {Path.GetFileName(file)}");
Logger.Info($"[{i + 1}/{files.Length}] Processed {Path.GetFileName(file)}");
Progress.Report(i + 1, files.Length);
}
assetsManager.Clear();
}
@@ -421,6 +442,7 @@ namespace AssetStudio
public static AssetEntry[] BuildAssetMap(string[] files, Game game, Regex[] nameFilters = null, Regex[] containerFilters = null)
{
Progress.Reset();
assetsManager.Game = game;
var assets = new List<AssetEntry>();
for (int i = 0; i < files.Length; i++)
@@ -448,70 +470,84 @@ namespace AssetStudio
};
var exportable = true;
switch (objectReader.type)
try
{
case ClassIDType.AssetBundle:
var assetBundle = new AssetBundle(objectReader);
foreach (var m_Container in assetBundle.m_Container)
{
var preloadIndex = m_Container.Value.preloadIndex;
var preloadSize = m_Container.Value.preloadSize;
var preloadEnd = preloadIndex + preloadSize;
for (int k = preloadIndex; k < preloadEnd; k++)
switch (objectReader.type)
{
case ClassIDType.AssetBundle:
var assetBundle = new AssetBundle(objectReader);
foreach (var m_Container in assetBundle.m_Container)
{
containers.Add((assetBundle.m_PreloadTable[k], m_Container.Key));
var preloadIndex = m_Container.Value.preloadIndex;
var preloadSize = m_Container.Value.preloadSize;
var preloadEnd = preloadIndex + preloadSize;
for (int k = preloadIndex; k < preloadEnd; k++)
{
containers.Add((assetBundle.m_PreloadTable[k], m_Container.Key));
}
}
}
obj = null;
asset.Name = assetBundle.m_Name;
exportable = false;
break;
case ClassIDType.GameObject:
var gameObject = new GameObject(objectReader);
obj = gameObject;
asset.Name = gameObject.m_Name;
exportable = false;
break;
case ClassIDType.Shader:
asset.Name = objectReader.ReadAlignedString();
if (string.IsNullOrEmpty(asset.Name))
{
var m_parsedForm = new SerializedShader(objectReader);
asset.Name = m_parsedForm.m_Name;
}
break;
case ClassIDType.Animator:
var component = new PPtr<Object>(objectReader);
animators.Add((component, asset));
break;
case ClassIDType.MiHoYoBinData:
var MiHoYoBinData = new MiHoYoBinData(objectReader);
obj = MiHoYoBinData;
exportable = true;
break;
case ClassIDType.IndexObject:
var indexObject = new IndexObject(objectReader);
obj = null;
foreach (var index in indexObject.AssetMap)
{
mihoyoBinDataNames.Add((index.Value.Object, index.Key));
}
asset.Name = "IndexObject";
break;
case ClassIDType.Font:
case ClassIDType.Material:
case ClassIDType.Texture:
case ClassIDType.Mesh:
case ClassIDType.Sprite:
case ClassIDType.TextAsset:
case ClassIDType.Texture2D:
case ClassIDType.VideoClip:
case ClassIDType.AudioClip:
asset.Name = objectReader.ReadAlignedString();
break;
default:
exportable = false;
break;
obj = null;
asset.Name = assetBundle.m_Name;
exportable = false;
break;
case ClassIDType.GameObject:
var gameObject = new GameObject(objectReader);
obj = gameObject;
asset.Name = gameObject.m_Name;
exportable = false;
break;
case ClassIDType.Shader:
asset.Name = objectReader.ReadAlignedString();
if (string.IsNullOrEmpty(asset.Name))
{
var m_parsedForm = new SerializedShader(objectReader);
asset.Name = m_parsedForm.m_Name;
}
break;
case ClassIDType.Animator:
var component = new PPtr<Object>(objectReader);
animators.Add((component, asset));
break;
case ClassIDType.MiHoYoBinData:
var MiHoYoBinData = new MiHoYoBinData(objectReader);
obj = MiHoYoBinData;
exportable = true;
break;
case ClassIDType.IndexObject:
var indexObject = new IndexObject(objectReader);
obj = null;
foreach (var index in indexObject.AssetMap)
{
mihoyoBinDataNames.Add((index.Value.Object, index.Key));
}
asset.Name = "IndexObject";
break;
case ClassIDType.Font:
case ClassIDType.Material:
case ClassIDType.Texture:
case ClassIDType.Mesh:
case ClassIDType.Sprite:
case ClassIDType.TextAsset:
case ClassIDType.Texture2D:
case ClassIDType.VideoClip:
case ClassIDType.AudioClip:
asset.Name = objectReader.ReadAlignedString();
break;
default:
exportable = false;
break;
}
}
catch (Exception e)
{
var sb = new StringBuilder();
sb.AppendLine("Unable to load object")
.AppendLine($"Assets {assetsFile.fileName}")
.AppendLine($"Path {assetsFile.originalPath}")
.AppendLine($"Type {objectReader.type}")
.AppendLine($"PathID {objectReader.m_PathID}")
.Append(e);
Logger.Error(sb.ToString());
}
if (obj != null)
{
@@ -560,7 +596,8 @@ namespace AssetStudio
}
}
}
Logger.Info($"Processed {Path.GetFileName(file)}");
Logger.Info($"[{i + 1}/{files.Length}] Processed {Path.GetFileName(file)}");
Progress.Report(i + 1, files.Length);
}
assetsManager.Clear();
}

View File

@@ -107,11 +107,6 @@ namespace AssetStudio
Logger.Info("Loading files has been aborted !!");
break;
}
if (!SkipProcess && !tokenSource.IsCancellationRequested)
{
ReadAssets();
ProcessAssets();
}
}
importFiles.Clear();
@@ -120,12 +115,12 @@ namespace AssetStudio
assetsFileListHash.Clear();
AssetsHelper.ClearOffsets();
//if (!SkipProcess && !tokenSource.IsCancellationRequested)
//{
// ReadAssets();
// ProcessAssets();
//}
if (!SkipProcess && !tokenSource.IsCancellationRequested)
{
ReadAssets();
ProcessAssets();
}
}
private void LoadFile(string fullName)
{
@@ -177,36 +172,36 @@ namespace AssetStudio
assetsFileList.Add(assetsFile);
assetsFileListHash.Add(assetsFile.fileName);
foreach (var sharedFile in assetsFile.m_Externals)
{
var sharedFileName = sharedFile.fileName;
foreach (var sharedFile in assetsFile.m_Externals)
{
var sharedFileName = sharedFile.fileName;
if (!importFilesHash.Contains(sharedFileName))
if (!importFilesHash.Contains(sharedFileName))
{
var sharedFilePath = Path.Combine(Path.GetDirectoryName(reader.FullPath), sharedFileName);
if (!noexistFiles.Contains(sharedFilePath))
{
var sharedFilePath = Path.Combine(Path.GetDirectoryName(reader.FullPath), sharedFileName);
if (!noexistFiles.Contains(sharedFilePath))
if (!File.Exists(sharedFilePath))
{
if (!File.Exists(sharedFilePath))
var findFiles = Directory.GetFiles(Path.GetDirectoryName(reader.FullPath), sharedFileName, SearchOption.AllDirectories);
if (findFiles.Length > 0)
{
var findFiles = Directory.GetFiles(Path.GetDirectoryName(reader.FullPath), sharedFileName, SearchOption.AllDirectories);
if (findFiles.Length > 0)
{
sharedFilePath = findFiles[0];
}
}
if (File.Exists(sharedFilePath))
{
importFiles.Add(sharedFilePath);
importFilesHash.Add(sharedFileName);
}
else
{
noexistFiles.Add(sharedFilePath);
sharedFilePath = findFiles[0];
}
}
if (File.Exists(sharedFilePath))
{
importFiles.Add(sharedFilePath);
importFilesHash.Add(sharedFileName);
}
else
{
noexistFiles.Add(sharedFilePath);
}
}
}
}
}
catch (Exception e)
{
Logger.Error($"Error while reading assets file {reader.FullPath}", e);
@@ -237,37 +232,37 @@ namespace AssetStudio
assetsFileList.Add(assetsFile);
assetsFileListHash.Add(assetsFile.fileName);
//if (ResolveDependencies)
//{
// foreach (var sharedFile in assetsFile.m_Externals)
// {
// var sharedFileName = sharedFile.fileName;
//
// if (!importFilesHash.Contains(sharedFileName))
// {
// var sharedFilePath = Path.Combine(Path.GetDirectoryName(originalPath), sharedFileName);
// if (!noexistFiles.Contains(sharedFilePath))
// {
// if (AssetsHelper.TryAdd(sharedFileName, out var path))
// {
// sharedFilePath = path;
// }
// if (File.Exists(sharedFilePath))
// {
// if (!importFiles.Contains(sharedFilePath))
// {
// importFiles.Add(sharedFilePath);
// }
// importFilesHash.Add(sharedFileName);
// }
// else
// {
// noexistFiles.Add(sharedFilePath);
// }
// }
// }
// }
//}
if (ResolveDependencies)
{
foreach (var sharedFile in assetsFile.m_Externals)
{
var sharedFileName = sharedFile.fileName;
if (!importFilesHash.Contains(sharedFileName))
{
var sharedFilePath = Path.Combine(Path.GetDirectoryName(originalPath), sharedFileName);
if (!noexistFiles.Contains(sharedFilePath))
{
if (AssetsHelper.TryAdd(sharedFileName, out var path))
{
sharedFilePath = path;
}
if (File.Exists(sharedFilePath))
{
if (!importFiles.Contains(sharedFilePath))
{
importFiles.Add(sharedFilePath);
}
importFilesHash.Add(sharedFileName);
}
else
{
noexistFiles.Add(sharedFilePath);
}
}
}
}
}
}
catch (Exception e)
{
@@ -279,9 +274,12 @@ namespace AssetStudio
Logger.Info($"Skipping {originalPath} ({reader.FileName})");
}
private void LoadBundleFile(FileReader reader, string originalPath = null, long originalOffset = 0)
private void LoadBundleFile(FileReader reader, string originalPath = null, long originalOffset = 0, bool log = true)
{
Logger.Info("Loading " + reader.FullPath);
if (log)
{
Logger.Info("Loading " + reader.FullPath);
}
try
{
var bundleFile = new BundleFile(reader, Game);
@@ -459,15 +457,18 @@ namespace AssetStudio
Logger.Info("Loading " + reader.FullPath);
try
{
using var stream = new BlockStream(reader.BaseStream, 0);
using var stream = new SubStream(reader.BaseStream, 0);
if (AssetsHelper.TryGet(reader.FullPath, out var offsets))
{
foreach (var offset in offsets)
{
var name = offset.ToString("X8");
Logger.Info($"Loading Block {name}");
stream.Offset = offset;
var dummyPath = Path.Combine(reader.FileName, offset.ToString("X8"));
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), name);
var subReader = new FileReader(dummyPath, stream, true);
LoadBundleFile(subReader, reader.FullPath, offset);
LoadBundleFile(subReader, reader.FullPath, offset, false);
}
AssetsHelper.Remove(reader.FullPath);
}
@@ -475,12 +476,15 @@ namespace AssetStudio
{
do
{
stream.Offset = stream.RelativePosition;
var dummyPath = Path.Combine(reader.FileName, stream.RelativePosition.ToString("X8"));
var name = stream.AbsolutePosition.ToString("X8");
Logger.Info($"Loading Block {name}");
stream.Offset = stream.AbsolutePosition;
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), name);
var subReader = new FileReader(dummyPath, stream, true);
LoadBundleFile(subReader, reader.FullPath, stream.RelativePosition);
LoadBundleFile(subReader, reader.FullPath, stream.AbsolutePosition, false);
} while (stream.Remaining > 0);
}
}
}
catch (Exception e)
{
@@ -501,16 +505,19 @@ namespace AssetStudio
{
foreach (var offset in offsets)
{
var name = offset.ToString("X8");
Logger.Info($"Loading Block {name}");
stream.Offset = offset;
var dummyPath = Path.Combine(reader.FileName, offset.ToString("X8"));
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), name);
var subReader = new FileReader(dummyPath, stream, true);
switch (subReader.FileType)
{
case FileType.BundleFile:
LoadBundleFile(subReader, reader.FullPath, offset);
LoadBundleFile(subReader, reader.FullPath, offset, false);
break;
case FileType.Mhy0File:
LoadMhy0File(subReader, reader.FullPath, offset);
LoadMhy0File(subReader, reader.FullPath, offset, false);
break;
}
}
@@ -520,18 +527,22 @@ namespace AssetStudio
{
do
{
stream.Offset = stream.RelativePosition;
var dummyPath = Path.Combine(reader.FileName, stream.RelativePosition.ToString("X8"));
var name = stream.Position.ToString("X8");
Logger.Info($"Loading Block {name}");
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), name);
var subReader = new FileReader(dummyPath, stream, true);
switch (subReader.FileType)
{
case FileType.BundleFile:
LoadBundleFile(subReader, reader.FullPath, stream.RelativePosition);
LoadBundleFile(subReader, reader.FullPath, stream.Position, false);
break;
case FileType.Mhy0File:
LoadMhy0File(subReader, reader.FullPath, stream.RelativePosition);
LoadMhy0File(subReader, reader.FullPath, stream.Position, false);
break;
}
stream.Offset += stream.Position;
} while (stream.Remaining > 0);
}
}
@@ -548,9 +559,12 @@ namespace AssetStudio
reader.Dispose();
}
}
private void LoadMhy0File(FileReader reader, string originalPath = null, long originalOffset = 0)
private void LoadMhy0File(FileReader reader, string originalPath = null, long originalOffset = 0, bool log = true)
{
Logger.Info("Loading " + reader.FullPath);
if (log)
{
Logger.Info("Loading " + reader.FullPath);
}
try
{
var mhy0File = new Mhy0File(reader, reader.FullPath, (Mhy0)Game);

View File

@@ -1,59 +0,0 @@
using System;
using System.IO;
namespace AssetStudio
{
public class BlockStream : Stream
{
private readonly Stream _baseStream;
private readonly long _origin;
private long _startPosition;
public override bool CanRead => _baseStream.CanRead;
public override bool CanSeek => _baseStream.CanSeek;
public override bool CanWrite => false;
public override long Length => _baseStream.Length - _startPosition;
public override long Position
{
get => _baseStream.Position - _startPosition;
set => Seek(value, SeekOrigin.Begin);
}
public long Offset
{
get => _startPosition;
set => _startPosition = value + _origin;
}
public long RelativePosition => _baseStream.Position - _origin;
public long Remaining => Length - Position;
public BlockStream(Stream stream, long offset)
{
_baseStream = stream;
_origin = offset;
_startPosition = offset;
Seek(0, SeekOrigin.Begin);
}
public override long Seek(long offset, SeekOrigin origin)
{
var target = origin switch
{
SeekOrigin.Begin => offset + _startPosition,
SeekOrigin.Current => offset + _baseStream.Position,
SeekOrigin.End => offset + Length,
_ => throw new NotSupportedException()
};
_baseStream.Seek(target, SeekOrigin.Begin);
return Position;
}
public override int Read(byte[] buffer, int offset, int count) => _baseStream.Read(buffer, offset, count);
public override void Write(byte[] buffer, int offset, int count) => throw new NotImplementedException();
public override void SetLength(long value) => throw new NotImplementedException();
public override void Flush() => throw new NotImplementedException();
}
}

View File

@@ -112,7 +112,7 @@ namespace AssetStudio
switch (header.signature)
{
case "UnityFS":
if (Game.Type.IsBH3())
if (Game.Type.IsBH3Group())
{
var version = reader.ReadUInt32();
if (version > 11)
@@ -277,7 +277,7 @@ namespace AssetStudio
private void ReadHeader(FileReader reader)
{
if (Game.Type.IsBH3() && XORShift128.Init)
if ((Game.Type.IsBH3Group()) && XORShift128.Init)
{
m_Header.flags = (ArchiveFlags)reader.ReadUInt32();
m_Header.size = reader.ReadInt64();

View File

@@ -672,7 +672,7 @@ namespace AssetStudio
{
m_ConstantClip = new ConstantClip(reader);
}
if (reader.Game.Type.IsGIGroup() || reader.Game.Type.IsBH3() || reader.Game.Type.IsZZZCB1())
if (reader.Game.Type.IsGIGroup() || reader.Game.Type.IsBH3Group() || reader.Game.Type.IsZZZCB1())
{
m_ACLClip.Read(reader);
}

View File

@@ -41,26 +41,6 @@ namespace AssetStudio
var name = m_External.fileName;
if (!assetsFileIndexCache.TryGetValue(name, out index))
{
if (assetsManager.ResolveDependencies && !assetsManager.importFilesHash.Contains(name))
{
var sharedFilePath = Path.Combine(Path.GetDirectoryName(assetsFile.originalPath), name);
if (!assetsManager.noexistFiles.Contains(sharedFilePath))
{
if (TryAdd(name, out var path))
{
sharedFilePath = path;
}
if (File.Exists(sharedFilePath))
{
assetsManager.importFilesHash.Add(name);
assetsManager.LoadFiles(sharedFilePath);
}
else
{
assetsManager.noexistFiles.Add(sharedFilePath);
}
}
}
index = assetsFileList.FindIndex(x => x.fileName.Equals(name, StringComparison.OrdinalIgnoreCase));
assetsFileIndexCache.Add(name, index);
}

View File

@@ -48,7 +48,7 @@ namespace AssetStudio
{
var m_DynamicOccludee = reader.ReadByte();
}
if (reader.Game.Type.IsBH3())
if (reader.Game.Type.IsBH3Group())
{
var m_AllowHalfResolution = reader.ReadByte();
}

View File

@@ -585,7 +585,7 @@ namespace AssetStudio
m_BlobIndex = reader.ReadUInt32();
m_Channels = new ParserBindChannels(reader);
if ((version[0] >= 2019 && version[0] < 2021) || (version[0] == 2021 && version[1] < 2)) //2019 ~2021.1
if ((version[0] >= 2019 && version[0] < 2021) || (version[0] == 2021 && version[1] < 2) || reader.Match("E99740711222CD922E9A6F92FF1EB07A") || reader.Match("450A058C218DAF000647948F2F59DA6D")) //2019 ~2021.1
{
var m_GlobalKeywordIndices = reader.ReadUInt16Array();
reader.AlignStream();
@@ -605,17 +605,6 @@ namespace AssetStudio
m_GpuProgramType = (ShaderGpuProgramType)reader.ReadSByte();
reader.AlignStream();
if (reader.Game.Type.IsGI() && (m_GpuProgramType == ShaderGpuProgramType.Unknown || !Enum.IsDefined(typeof(ShaderGpuProgramType), m_GpuProgramType)))
{
reader.Position -= 4;
var m_LocalKeywordIndices = reader.ReadUInt16Array();
reader.AlignStream();
m_ShaderHardwareTier = reader.ReadSByte();
m_GpuProgramType = (ShaderGpuProgramType)reader.ReadSByte();
reader.AlignStream();
}
if ((version[0] == 2020 && version[1] > 3) ||
(version[0] == 2020 && version[1] == 3 && version[2] >= 2) || //2020.3.2f1 and up
(version[0] > 2021) ||
@@ -697,6 +686,16 @@ namespace AssetStudio
var m_ShaderRequirements = reader.ReadInt32();
}
}
if (reader.Match("E99740711222CD922E9A6F92FF1EB07A"))
{
int numInstancedStructuredBuffers = reader.ReadInt32();
var m_InstancedStructuredBuffers = new ConstantBuffer[numInstancedStructuredBuffers];
for (int i = 0; i < numInstancedStructuredBuffers; i++)
{
m_InstancedStructuredBuffers[i] = new ConstantBuffer(reader);
}
}
}
}

View File

@@ -5,7 +5,7 @@ namespace AssetStudio
{
public static class BlkUtils
{
private const int DataPos = 0x2A;
private const int DataOffset = 0x2A;
private const int KeySize = 0x1000;
private const int SeedBlockSize = 0x800;
@@ -54,7 +54,7 @@ namespace AssetStudio
BinaryPrimitives.WriteUInt64LittleEndian(xorpad.AsSpan(i, 8), mt64.Int64());
}
return new XORStream(reader.BaseStream, DataPos, xorpad);
return new XORStream(reader.BaseStream, DataOffset, xorpad);
}
}
}

View File

@@ -126,6 +126,14 @@ namespace AssetStudio
}
return true;
}
public static bool IsReadable(string path, Game game)
{
var reader = new FileReader(path);
reader = reader.PreProcessing(game);
reader.Dispose();
return reader.FileType != FileType.ResourceFile;
}
}
public static class FileReaderExtensions

View File

@@ -19,6 +19,7 @@ namespace AssetStudio
Games.Add(index++, new Blk(GameType.GI_CB3, GI_CBXExpansionKey, initVector: GI_CBXInitVector, initSeed: GI_CBXInitSeed));
Games.Add(index++, new Mhy0(GameType.GI_CB3Pre, GI_CBXMhy0ShiftRow, GI_CBXMhy0Key, GI_CBXMhy0Mul, GI_CBXExpansionKey, GI_CBXSBox, GI_CBXInitVector, GI_CBXInitSeed));
Games.Add(index++, new Mr0k(GameType.BH3, BH3ExpansionKey, BH3SBox, BH3InitVector, BH3BlockKey));
Games.Add(index++, new Mr0k(GameType.BH3_Pre, PackExpansionKey, blockKey: PackBlockKey));
Games.Add(index++, new Mr0k(GameType.SR_CB2, Mr0kExpansionKey, initVector: Mr0kInitVector, blockKey: Mr0kBlockKey));
Games.Add(index++, new Mr0k(GameType.SR_CB3, Mr0kExpansionKey, initVector: Mr0kInitVector, blockKey: Mr0kBlockKey));
Games.Add(index++, new Mr0k(GameType.ZZZ_CB1, Mr0kExpansionKey, initVector: Mr0kInitVector, blockKey: Mr0kBlockKey));
@@ -120,6 +121,7 @@ namespace AssetStudio
GI_CB3,
GI_CB3Pre,
BH3,
BH3_Pre,
ZZZ_CB1,
SR_CB2,
SR_CB3,
@@ -143,6 +145,7 @@ namespace AssetStudio
public static bool IsGICB3(this GameType type) => type == GameType.GI_CB3;
public static bool IsGICB3Pre(this GameType type) => type == GameType.GI_CB3Pre;
public static bool IsBH3(this GameType type) => type == GameType.BH3;
public static bool IsBH3Pre(this GameType type) => type == GameType.BH3_Pre;
public static bool IsZZZCB1(this GameType type) => type == GameType.ZZZ_CB1;
public static bool IsSRCB2(this GameType type) => type == GameType.SR_CB2;
public static bool IsSRCB3(this GameType type) => type == GameType.SR_CB3;
@@ -161,6 +164,12 @@ namespace AssetStudio
_ => false,
};
public static bool IsBH3Group(this GameType type) => type switch
{
GameType.BH3 or GameType.BH3_Pre => true,
_ => false,
};
public static bool IsSRGroup(this GameType type) => type switch
{
GameType.SR_CB2 or GameType.SR_CB3 => true,
@@ -169,7 +178,7 @@ namespace AssetStudio
public static bool IsBlockFile(this GameType type) => type switch
{
GameType.BH3 or GameType.SR_CB3 or GameType.GI_Pack or GameType.TOT => true,
GameType.BH3 or GameType.BH3_Pre or GameType.SR_CB3 or GameType.GI_Pack or GameType.TOT => true,
_ => false,
};

View File

@@ -279,7 +279,7 @@ namespace AssetStudio
var idx = data.Search("UnityFS");
if (idx != -1)
{
stream = new BlockStream(stream, idx);
stream = new SubStream(stream, idx);
}
return new FileReader(reader.FullPath, stream);
@@ -295,11 +295,11 @@ namespace AssetStudio
var idx2 = data[(idx + 1)..].Search("UnityFS");
if (idx2 != -1)
{
stream = new BlockStream(stream, idx + idx2 + 1);
stream = new SubStream(stream, idx + idx2 + 1);
}
else
{
stream = new BlockStream(stream, idx);
stream = new SubStream(stream, idx);
}
}
@@ -371,7 +371,7 @@ namespace AssetStudio
reader.Position = 0;
return reader;
}
var stream = new BlockStream(reader.BaseStream, idx);
var stream = new SubStream(reader.BaseStream, idx);
return new FileReader(reader.FullPath, stream);
}
public static FileReader ParseHelixWaltz2(FileReader reader)

View File

@@ -21,7 +21,7 @@ namespace AssetStudio
public int[] version => assetsFile.version;
public BuildType buildType => assetsFile.buildType;
public ObjectReader(EndianBinaryReader reader, SerializedFile assetsFile, ObjectInfo objectInfo, Game game) : base(reader.BaseStream, reader.Endian)
public ObjectReader(EndianBinaryReader reader, SerializedFile assetsFile, ObjectInfo objectInfo, Game game) : base(new SubStream(reader.BaseStream, objectInfo.byteStart, objectInfo.byteSize), reader.Endian)
{
this.assetsFile = assetsFile;
Game = game;
@@ -41,9 +41,11 @@ namespace AssetStudio
m_Version = assetsFile.header.m_Version;
}
public bool Match(string hash) => Convert.ToHexString(serializedType.m_OldTypeHash) == hash;
public void Reset()
{
Position = byteStart;
Position = 0;
}
}
}

103
AssetStudio/SubStream.cs Normal file
View File

@@ -0,0 +1,103 @@
using System;
using System.IO;
namespace AssetStudio
{
public class SubStream : Stream
{
private readonly Stream _baseStream;
private long _offset;
private long _size;
public override bool CanRead => _baseStream.CanRead;
public override bool CanSeek => _baseStream.CanSeek;
public override bool CanWrite => false;
public long Size
{
get => _size;
set
{
if (value < 0 || value > _baseStream.Length || value + _offset > _baseStream.Length)
{
throw new IOException($"{nameof(Size)} is out of stream bound");
}
_size = value;
}
}
public long Offset
{
get => _offset;
set
{
if (value < 0 || value > _baseStream.Length)
{
throw new IOException($"{nameof(Offset)} is out of stream bound");
}
if (value + _size > _baseStream.Length)
{
_size = _baseStream.Length - value;
}
_offset = value;
}
}
public long AbsolutePosition => _baseStream.Position;
public long Remaining => Length - Position;
public override long Length => Size;
public override long Position
{
get => _baseStream.Position - _offset;
set => Seek(value, SeekOrigin.Begin);
}
public SubStream(Stream stream, long offset)
{
_baseStream = stream;
Offset = offset;
Size = _baseStream.Length - _offset;
Seek(0, SeekOrigin.Begin);
}
public SubStream(Stream stream, long offset, long size)
{
_baseStream = stream;
Offset = offset;
Size = size;
Seek(0, SeekOrigin.Begin);
}
public override long Seek(long offset, SeekOrigin origin)
{
if (offset > _size)
{
throw new IOException("Unable to seek beyond stream bound");
}
var target = origin switch
{
SeekOrigin.Begin => offset + _offset,
SeekOrigin.Current => offset + Position,
SeekOrigin.End => offset + _size,
_ => throw new NotSupportedException()
};
_baseStream.Seek(target, SeekOrigin.Begin);
return Position;
}
public override int Read(byte[] buffer, int offset, int count)
{
if (offset > _size || Position + count > _size)
{
throw new IOException("Unable to read beyond stream bound");
}
return _baseStream.Read(buffer, offset, count);
}
public override void Write(byte[] buffer, int offset, int count) => throw new NotImplementedException();
public override void SetLength(long value) => throw new NotImplementedException();
public override void Flush() => throw new NotImplementedException();
}
}

View File

@@ -2,18 +2,20 @@
namespace AssetStudio
{
public class XORStream : BlockStream
public class XORStream : SubStream
{
private readonly byte[] _xorpad;
private readonly long _offset;
public XORStream(Stream stream, long pos, byte[] xorpad) : base(stream, pos)
public XORStream(Stream stream, long offset, byte[] xorpad) : base(stream, offset)
{
_xorpad = xorpad;
_offset = offset;
}
public override int Read(byte[] buffer, int offset, int count)
{
var pos = RelativePosition;
var pos = AbsolutePosition - _offset;
var read = base.Read(buffer, offset, count);
for (int i = 0; i < count; i++)
{

View File

@@ -79,8 +79,9 @@ namespace AssetStudioCLI
assemblyLoader.Load(o.DummyDllFolder.FullName);
}
Logger.Info("Scanning for files");
Logger.Info("Scanning for files...");
var files = o.Input.Attributes.HasFlag(FileAttributes.Directory) ? Directory.GetFiles(o.Input.FullName, "*.*", SearchOption.AllDirectories).OrderBy(x => x.Length).ToArray() : new string[] { o.Input.FullName };
files = files.Where(x => FileReader.IsReadable(x, game)).ToArray();
Logger.Info(string.Format("Found {0} file(s)", files.Length));
if (o.MapOp.HasFlag(MapOpType.Build))
@@ -107,6 +108,12 @@ namespace AssetStudioCLI
AssetsHelper.ExportAssetsMap(assets, o.MapName, o.Output.FullName, o.MapType, resetEvent);
resetEvent.WaitOne();
}
if (o.MapOp.HasFlag(MapOpType.Both))
{
var resetEvent = new ManualResetEvent(false);
AssetsHelper.BuildBoth(files, o.MapName, o.Input.FullName, game, o.Output.FullName, o.MapType, resetEvent, o.NameFilter, o.ContainerFilter);
resetEvent.WaitOne();
}
if (o.MapOp.Equals(MapOpType.None) || o.MapOp.HasFlag(MapOpType.Load))
{
var i = 0;

View File

@@ -20,9 +20,9 @@ namespace AssetStudioCLI
None,
Load,
Build,
Both,
List,
All = Build | Load | List
List = 4,
Both = 8,
All = Both | Load,
}
public enum AssetGroupOption
@@ -127,8 +127,8 @@ namespace AssetStudioCLI
using var stream = BlkUtils.Decrypt(reader, (Blk)Game);
do
{
stream.Offset = stream.RelativePosition;
var dummyPath = Path.Combine(reader.FullPath, stream.RelativePosition.ToString("X8"));
stream.Offset = stream.AbsolutePosition;
var dummyPath = Path.Combine(reader.FullPath, stream.AbsolutePosition.ToString("X8"));
var subReader = new FileReader(dummyPath, stream, true);
var subSavePath = Path.Combine(savePath, reader.FileName + "_unpacked");
switch (subReader.FileType)
@@ -153,12 +153,12 @@ namespace AssetStudioCLI
{
int total = 0;
Logger.Info($"Decompressing {reader.FileName} ...");
using var stream = new BlockStream(reader.BaseStream, 0);
using var stream = new SubStream(reader.BaseStream, 0);
do
{
stream.Offset = stream.RelativePosition;
stream.Offset = stream.AbsolutePosition;
var subSavePath = Path.Combine(savePath, reader.FileName + "_unpacked");
var dummyPath = Path.Combine(reader.FullPath, stream.RelativePosition.ToString("X8"));
var dummyPath = Path.Combine(reader.FullPath, stream.AbsolutePosition.ToString("X8"));
var subReader = new FileReader(dummyPath, stream, true);
total += ExtractBundleFile(subReader, subSavePath);
} while (stream.Remaining > 0);

View File

@@ -1899,8 +1899,9 @@ namespace AssetStudioGUI
openFolderDialog.Title = "Select Game Folder";
if (openFolderDialog.ShowDialog(this) == DialogResult.OK)
{
Logger.Info("Scanning for files");
Logger.Info("Scanning for files...");
var files = Directory.GetFiles(openFolderDialog.Folder, "*.*", SearchOption.AllDirectories).ToArray();
files = files.Where(x => FileReader.IsReadable(x, Studio.Game)).ToArray();
Logger.Info($"Found {files.Length} files");
await Task.Run(() => AssetsHelper.BuildMap(files, name, openFolderDialog.Folder, Studio.Game));
}
@@ -2017,8 +2018,9 @@ namespace AssetStudioGUI
openFolderDialog.Title = $"Select Game Folder";
if (openFolderDialog.ShowDialog(this) == DialogResult.OK)
{
Logger.Info("Scanning for files");
Logger.Info("Scanning for files...");
var files = Directory.GetFiles(openFolderDialog.Folder, "*.*", SearchOption.AllDirectories).ToArray();
files = files.Where(x => FileReader.IsReadable(x, Studio.Game)).ToArray();
Logger.Info($"Found {files.Length} files");
var saveFolderDialog = new OpenFolderDialog();
@@ -2317,7 +2319,7 @@ namespace AssetStudioGUI
private void InitOpenTK()
{
ChangeGLSize(glControl.Size);
GL.ClearColor(Color4.Darkgray);
GL.ClearColor(Color4.Cadetblue);
pgmID = GL.CreateProgram();
LoadShader("vs", ShaderType.VertexShader, pgmID, out ShaderHandle vsID);
LoadShader("fs", ShaderType.FragmentShader, pgmID, out ShaderHandle fsID);

View File

@@ -128,8 +128,8 @@ namespace AssetStudioGUI
using var stream = BlkUtils.Decrypt(reader, (Blk)Game);
do
{
stream.Offset = stream.RelativePosition;
var dummyPath = Path.Combine(reader.FullPath, stream.RelativePosition.ToString("X8"));
stream.Offset = stream.AbsolutePosition;
var dummyPath = Path.Combine(reader.FullPath, stream.AbsolutePosition.ToString("X8"));
var subReader = new FileReader(dummyPath, stream, true);
var subSavePath = Path.Combine(savePath, reader.FileName + "_unpacked");
switch (subReader.FileType)
@@ -154,12 +154,12 @@ namespace AssetStudioGUI
{
int total = 0;
StatusStripUpdate($"Decompressing {reader.FileName} ...");
using var stream = new BlockStream(reader.BaseStream, 0);
using var stream = new SubStream(reader.BaseStream, 0);
do
{
stream.Offset = stream.RelativePosition;
stream.Offset = stream.AbsolutePosition;
var subSavePath = Path.Combine(savePath, reader.FileName + "_unpacked");
var dummyPath = Path.Combine(reader.FullPath, stream.RelativePosition.ToString("X8"));
var dummyPath = Path.Combine(reader.FullPath, stream.AbsolutePosition.ToString("X8"));
var subReader = new FileReader(dummyPath, stream, true);
total += ExtractBundleFile(subReader, subSavePath);
} while (stream.Remaining > 0);