- 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)) if (await NeedDownload(version, versionIndex.MappedPath))
{ {
Logger.Info("Downloading..."); Logger.Info("Downloading...");
var json = await DownloadString(url, TimeSpan.FromMinutes(1)); var json = await DownloadString(url, TimeSpan.FromMinutes(2));
if (string.IsNullOrEmpty(json)) if (string.IsNullOrEmpty(json))
{ {
Logger.Warning("Could not load AI !!"); Logger.Warning("Could not load AI !!");

View File

@@ -8,6 +8,7 @@ using Newtonsoft.Json.Converters;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Xml; using System.Xml;
using System.Text;
namespace AssetStudio namespace AssetStudio
{ {
@@ -82,10 +83,11 @@ namespace AssetStudio
public static void BuildMap(string[] files, string mapName, string baseFolder, Game game) public static void BuildMap(string[] files, string mapName, string baseFolder, Game game)
{ {
Logger.Info($"Processing..."); Logger.Info($"Building Map...");
try try
{ {
Map.Clear(); Map.Clear();
Progress.Reset();
var collision = 0; var collision = 0;
BaseFolder = baseFolder; BaseFolder = baseFolder;
assetsManager.Game = game; assetsManager.Game = game;
@@ -118,7 +120,8 @@ namespace AssetStudio
} }
Map.Add(assetsFile.fileName, entry); 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(); 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) 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(); Map.Clear();
Progress.Reset();
var collision = 0; var collision = 0;
BaseFolder = baseFolder; BaseFolder = baseFolder;
assetsManager.Game = game; assetsManager.Game = game;
@@ -203,74 +208,89 @@ namespace AssetStudio
Source = file, Source = file,
PathID = objectReader.m_PathID, PathID = objectReader.m_PathID,
Type = objectReader.type, Type = objectReader.type,
Container = "" Container = string.Empty,
Name = string.Empty
}; };
var exportable = true; var exportable = true;
switch (objectReader.type) try
{ {
case ClassIDType.AssetBundle: switch (objectReader.type)
var assetBundle = new AssetBundle(objectReader); {
foreach (var m_Container in assetBundle.m_Container) case ClassIDType.AssetBundle:
{ var assetBundle = new AssetBundle(objectReader);
var preloadIndex = m_Container.Value.preloadIndex; foreach (var m_Container in assetBundle.m_Container)
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)); 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;
obj = null; asset.Name = assetBundle.m_Name;
asset.Name = assetBundle.m_Name; exportable = false;
exportable = false; break;
break; case ClassIDType.GameObject:
case ClassIDType.GameObject: var gameObject = new GameObject(objectReader);
var gameObject = new GameObject(objectReader); obj = gameObject;
obj = gameObject; asset.Name = gameObject.m_Name;
asset.Name = gameObject.m_Name; exportable = false;
exportable = false; break;
break; case ClassIDType.Shader:
case ClassIDType.Shader: asset.Name = objectReader.ReadAlignedString();
asset.Name = objectReader.ReadAlignedString(); if (string.IsNullOrEmpty(asset.Name))
if (string.IsNullOrEmpty(asset.Name)) {
{ var m_parsedForm = new SerializedShader(objectReader);
var m_parsedForm = new SerializedShader(objectReader); asset.Name = m_parsedForm.m_Name;
asset.Name = m_parsedForm.m_Name; }
} break;
break; case ClassIDType.Animator:
case ClassIDType.Animator: var component = new PPtr<Object>(objectReader);
var component = new PPtr<Object>(objectReader); animators.Add((component, asset));
animators.Add((component, asset)); break;
break; case ClassIDType.MiHoYoBinData:
case ClassIDType.MiHoYoBinData: var MiHoYoBinData = new MiHoYoBinData(objectReader);
var MiHoYoBinData = new MiHoYoBinData(objectReader); obj = MiHoYoBinData;
obj = MiHoYoBinData; exportable = true;
exportable = true; break;
break; case ClassIDType.IndexObject:
case ClassIDType.IndexObject: var indexObject = new IndexObject(objectReader);
var indexObject = new IndexObject(objectReader); obj = null;
obj = null; foreach (var index in indexObject.AssetMap)
foreach (var index in indexObject.AssetMap) {
{ mihoyoBinDataNames.Add((index.Value.Object, index.Key));
mihoyoBinDataNames.Add((index.Value.Object, index.Key)); }
} asset.Name = "IndexObject";
asset.Name = "IndexObject"; break;
break; case ClassIDType.Font:
case ClassIDType.Font: case ClassIDType.Material:
case ClassIDType.Material: case ClassIDType.Texture:
case ClassIDType.Texture: case ClassIDType.Mesh:
case ClassIDType.Mesh: case ClassIDType.Sprite:
case ClassIDType.Sprite: case ClassIDType.TextAsset:
case ClassIDType.TextAsset: case ClassIDType.Texture2D:
case ClassIDType.Texture2D: case ClassIDType.VideoClip:
case ClassIDType.VideoClip: case ClassIDType.AudioClip:
case ClassIDType.AudioClip: asset.Name = objectReader.ReadAlignedString();
asset.Name = objectReader.ReadAlignedString(); break;
break; default:
default: exportable = false;
exportable = false; break;
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) 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(); assetsManager.Clear();
} }
@@ -421,6 +442,7 @@ namespace AssetStudio
public static AssetEntry[] BuildAssetMap(string[] files, Game game, Regex[] nameFilters = null, Regex[] containerFilters = null) public static AssetEntry[] BuildAssetMap(string[] files, Game game, Regex[] nameFilters = null, Regex[] containerFilters = null)
{ {
Progress.Reset();
assetsManager.Game = game; assetsManager.Game = game;
var assets = new List<AssetEntry>(); var assets = new List<AssetEntry>();
for (int i = 0; i < files.Length; i++) for (int i = 0; i < files.Length; i++)
@@ -448,70 +470,84 @@ namespace AssetStudio
}; };
var exportable = true; var exportable = true;
switch (objectReader.type) try
{ {
case ClassIDType.AssetBundle: switch (objectReader.type)
var assetBundle = new AssetBundle(objectReader); {
foreach (var m_Container in assetBundle.m_Container) case ClassIDType.AssetBundle:
{ var assetBundle = new AssetBundle(objectReader);
var preloadIndex = m_Container.Value.preloadIndex; foreach (var m_Container in assetBundle.m_Container)
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)); 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;
obj = null; asset.Name = assetBundle.m_Name;
asset.Name = assetBundle.m_Name; exportable = false;
exportable = false; break;
break; case ClassIDType.GameObject:
case ClassIDType.GameObject: var gameObject = new GameObject(objectReader);
var gameObject = new GameObject(objectReader); obj = gameObject;
obj = gameObject; asset.Name = gameObject.m_Name;
asset.Name = gameObject.m_Name; exportable = false;
exportable = false; break;
break; case ClassIDType.Shader:
case ClassIDType.Shader: asset.Name = objectReader.ReadAlignedString();
asset.Name = objectReader.ReadAlignedString(); if (string.IsNullOrEmpty(asset.Name))
if (string.IsNullOrEmpty(asset.Name)) {
{ var m_parsedForm = new SerializedShader(objectReader);
var m_parsedForm = new SerializedShader(objectReader); asset.Name = m_parsedForm.m_Name;
asset.Name = m_parsedForm.m_Name; }
} break;
break; case ClassIDType.Animator:
case ClassIDType.Animator: var component = new PPtr<Object>(objectReader);
var component = new PPtr<Object>(objectReader); animators.Add((component, asset));
animators.Add((component, asset)); break;
break; case ClassIDType.MiHoYoBinData:
case ClassIDType.MiHoYoBinData: var MiHoYoBinData = new MiHoYoBinData(objectReader);
var MiHoYoBinData = new MiHoYoBinData(objectReader); obj = MiHoYoBinData;
obj = MiHoYoBinData; exportable = true;
exportable = true; break;
break; case ClassIDType.IndexObject:
case ClassIDType.IndexObject: var indexObject = new IndexObject(objectReader);
var indexObject = new IndexObject(objectReader); obj = null;
obj = null; foreach (var index in indexObject.AssetMap)
foreach (var index in indexObject.AssetMap) {
{ mihoyoBinDataNames.Add((index.Value.Object, index.Key));
mihoyoBinDataNames.Add((index.Value.Object, index.Key)); }
} asset.Name = "IndexObject";
asset.Name = "IndexObject"; break;
break; case ClassIDType.Font:
case ClassIDType.Font: case ClassIDType.Material:
case ClassIDType.Material: case ClassIDType.Texture:
case ClassIDType.Texture: case ClassIDType.Mesh:
case ClassIDType.Mesh: case ClassIDType.Sprite:
case ClassIDType.Sprite: case ClassIDType.TextAsset:
case ClassIDType.TextAsset: case ClassIDType.Texture2D:
case ClassIDType.Texture2D: case ClassIDType.VideoClip:
case ClassIDType.VideoClip: case ClassIDType.AudioClip:
case ClassIDType.AudioClip: asset.Name = objectReader.ReadAlignedString();
asset.Name = objectReader.ReadAlignedString(); break;
break; default:
default: exportable = false;
exportable = false; break;
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) 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(); assetsManager.Clear();
} }

View File

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

View File

@@ -672,7 +672,7 @@ namespace AssetStudio
{ {
m_ConstantClip = new ConstantClip(reader); 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); m_ACLClip.Read(reader);
} }

View File

@@ -41,26 +41,6 @@ namespace AssetStudio
var name = m_External.fileName; var name = m_External.fileName;
if (!assetsFileIndexCache.TryGetValue(name, out index)) 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)); index = assetsFileList.FindIndex(x => x.fileName.Equals(name, StringComparison.OrdinalIgnoreCase));
assetsFileIndexCache.Add(name, index); assetsFileIndexCache.Add(name, index);
} }

View File

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

View File

@@ -585,7 +585,7 @@ namespace AssetStudio
m_BlobIndex = reader.ReadUInt32(); m_BlobIndex = reader.ReadUInt32();
m_Channels = new ParserBindChannels(reader); 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(); var m_GlobalKeywordIndices = reader.ReadUInt16Array();
reader.AlignStream(); reader.AlignStream();
@@ -605,17 +605,6 @@ namespace AssetStudio
m_GpuProgramType = (ShaderGpuProgramType)reader.ReadSByte(); m_GpuProgramType = (ShaderGpuProgramType)reader.ReadSByte();
reader.AlignStream(); 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) || if ((version[0] == 2020 && version[1] > 3) ||
(version[0] == 2020 && version[1] == 3 && version[2] >= 2) || //2020.3.2f1 and up (version[0] == 2020 && version[1] == 3 && version[2] >= 2) || //2020.3.2f1 and up
(version[0] > 2021) || (version[0] > 2021) ||
@@ -697,6 +686,16 @@ namespace AssetStudio
var m_ShaderRequirements = reader.ReadInt32(); 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 public static class BlkUtils
{ {
private const int DataPos = 0x2A; private const int DataOffset = 0x2A;
private const int KeySize = 0x1000; private const int KeySize = 0x1000;
private const int SeedBlockSize = 0x800; private const int SeedBlockSize = 0x800;
@@ -54,7 +54,7 @@ namespace AssetStudio
BinaryPrimitives.WriteUInt64LittleEndian(xorpad.AsSpan(i, 8), mt64.Int64()); 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; 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 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 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 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, 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_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.SR_CB3, Mr0kExpansionKey, initVector: Mr0kInitVector, blockKey: Mr0kBlockKey));
Games.Add(index++, new Mr0k(GameType.ZZZ_CB1, 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_CB3,
GI_CB3Pre, GI_CB3Pre,
BH3, BH3,
BH3_Pre,
ZZZ_CB1, ZZZ_CB1,
SR_CB2, SR_CB2,
SR_CB3, SR_CB3,
@@ -143,6 +145,7 @@ namespace AssetStudio
public static bool IsGICB3(this GameType type) => type == GameType.GI_CB3; 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 IsGICB3Pre(this GameType type) => type == GameType.GI_CB3Pre;
public static bool IsBH3(this GameType type) => type == GameType.BH3; 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 IsZZZCB1(this GameType type) => type == GameType.ZZZ_CB1;
public static bool IsSRCB2(this GameType type) => type == GameType.SR_CB2; public static bool IsSRCB2(this GameType type) => type == GameType.SR_CB2;
public static bool IsSRCB3(this GameType type) => type == GameType.SR_CB3; public static bool IsSRCB3(this GameType type) => type == GameType.SR_CB3;
@@ -161,6 +164,12 @@ namespace AssetStudio
_ => false, _ => 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 public static bool IsSRGroup(this GameType type) => type switch
{ {
GameType.SR_CB2 or GameType.SR_CB3 => true, GameType.SR_CB2 or GameType.SR_CB3 => true,
@@ -169,7 +178,7 @@ namespace AssetStudio
public static bool IsBlockFile(this GameType type) => type switch 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, _ => false,
}; };

View File

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

View File

@@ -21,7 +21,7 @@ namespace AssetStudio
public int[] version => assetsFile.version; public int[] version => assetsFile.version;
public BuildType buildType => assetsFile.buildType; 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; this.assetsFile = assetsFile;
Game = game; Game = game;
@@ -41,9 +41,11 @@ namespace AssetStudio
m_Version = assetsFile.header.m_Version; m_Version = assetsFile.header.m_Version;
} }
public bool Match(string hash) => Convert.ToHexString(serializedType.m_OldTypeHash) == hash;
public void Reset() 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 namespace AssetStudio
{ {
public class XORStream : BlockStream public class XORStream : SubStream
{ {
private readonly byte[] _xorpad; 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; _xorpad = xorpad;
_offset = offset;
} }
public override int Read(byte[] buffer, int offset, int count) public override int Read(byte[] buffer, int offset, int count)
{ {
var pos = RelativePosition; var pos = AbsolutePosition - _offset;
var read = base.Read(buffer, offset, count); var read = base.Read(buffer, offset, count);
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {

View File

@@ -79,8 +79,9 @@ namespace AssetStudioCLI
assemblyLoader.Load(o.DummyDllFolder.FullName); 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 }; 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)); Logger.Info(string.Format("Found {0} file(s)", files.Length));
if (o.MapOp.HasFlag(MapOpType.Build)) if (o.MapOp.HasFlag(MapOpType.Build))
@@ -107,6 +108,12 @@ namespace AssetStudioCLI
AssetsHelper.ExportAssetsMap(assets, o.MapName, o.Output.FullName, o.MapType, resetEvent); AssetsHelper.ExportAssetsMap(assets, o.MapName, o.Output.FullName, o.MapType, resetEvent);
resetEvent.WaitOne(); 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)) if (o.MapOp.Equals(MapOpType.None) || o.MapOp.HasFlag(MapOpType.Load))
{ {
var i = 0; var i = 0;

View File

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

View File

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

View File

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