v0.90.10
This commit is contained in:
@@ -2,9 +2,9 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
|
||||
<Version>0.90.00</Version>
|
||||
<AssemblyVersion>0.90.00</AssemblyVersion>
|
||||
<FileVersion>0.90.00</FileVersion>
|
||||
<Version>0.90.10</Version>
|
||||
<AssemblyVersion>0.90.10</AssemblyVersion>
|
||||
<FileVersion>0.90.10</FileVersion>
|
||||
<Copyright>Copyright © Perfare 2020-2022; Copyright © hozuki 2020</Copyright>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
|
||||
<Version>0.90.00</Version>
|
||||
<AssemblyVersion>0.90.00</AssemblyVersion>
|
||||
<FileVersion>0.90.00</FileVersion>
|
||||
<Version>0.90.10</Version>
|
||||
<AssemblyVersion>0.90.10</AssemblyVersion>
|
||||
<FileVersion>0.90.10</FileVersion>
|
||||
<Copyright>Copyright © Razmoth 2022; Copyright © Perfare 2018-2022</Copyright>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="K4os.Compression.LZ4" Version="1.3.5" />
|
||||
<PackageReference Include="K4os.Compression.LZ4" Version="1.3.6" />
|
||||
<PackageReference Include="MessagePack" Version="2.6.100-alpha" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="ZstdSharp.Port" Version="0.7.2" />
|
||||
|
||||
@@ -36,7 +36,9 @@ namespace AssetStudio
|
||||
{
|
||||
Directory.CreateDirectory(MapName);
|
||||
var files = Directory.GetFiles(MapName, "*.bin", SearchOption.TopDirectoryOnly);
|
||||
return files.Select(Path.GetFileNameWithoutExtension).ToArray();
|
||||
var mapNames = files.Select(Path.GetFileNameWithoutExtension).ToArray();
|
||||
Logger.Verbose($"Found {mapNames.Length} CABMaps under Maps folder");
|
||||
return mapNames;
|
||||
}
|
||||
|
||||
public static void Clear()
|
||||
@@ -48,34 +50,20 @@ namespace AssetStudio
|
||||
tokenSource.Dispose();
|
||||
tokenSource = new CancellationTokenSource();
|
||||
|
||||
GC.WaitForPendingFinalizers();
|
||||
GC.Collect();
|
||||
Logger.Verbose("Cleared AssetsHelper successfully !!");
|
||||
}
|
||||
|
||||
public static void ClearOffsets() => Offsets.Clear();
|
||||
|
||||
public static void Remove(string path) => Offsets.Remove(path);
|
||||
|
||||
public static bool TryAdd(string name, out string path)
|
||||
public static void ClearOffsets()
|
||||
{
|
||||
if (CABMap.TryGetValue(name, out var entry))
|
||||
{
|
||||
path = Path.Combine(BaseFolder, entry.Path);
|
||||
if (!Offsets.ContainsKey(path))
|
||||
{
|
||||
Offsets.Add(path, new HashSet<long>());
|
||||
}
|
||||
Offsets[path].Add(entry.Offset);
|
||||
return true;
|
||||
}
|
||||
path = string.Empty;
|
||||
return false;
|
||||
Offsets.Clear();
|
||||
Logger.Verbose("Cleared cached offsets");
|
||||
}
|
||||
|
||||
public static bool TryGet(string path, out long[] offsets)
|
||||
{
|
||||
if (Offsets.TryGetValue(path, out var list))
|
||||
if (Offsets.TryGetValue(path, out var list) && list.Count > 0)
|
||||
{
|
||||
Logger.Verbose($"Found {list.Count} offsets for path {path}");
|
||||
offsets = list.ToArray();
|
||||
return true;
|
||||
}
|
||||
@@ -83,6 +71,67 @@ namespace AssetStudio
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void AddCABOffsets(string[] paths, List<string> cabs)
|
||||
{
|
||||
for (int i = 0; i < cabs.Count; i++)
|
||||
{
|
||||
var cab = cabs[i];
|
||||
if (CABMap.TryGetValue(cab, out var entry))
|
||||
{
|
||||
var fullPath = Path.Combine(BaseFolder, entry.Path);
|
||||
Logger.Verbose($"Found {cab} in {fullPath}");
|
||||
if (!paths.Contains(fullPath))
|
||||
{
|
||||
Offsets.TryAdd(fullPath, new HashSet<long>());
|
||||
Offsets[fullPath].Add(entry.Offset);
|
||||
Logger.Verbose($"Added {fullPath} to Offsets, at offset {entry.Offset}");
|
||||
}
|
||||
foreach (var dep in entry.Dependencies)
|
||||
{
|
||||
if (!cabs.Contains(dep))
|
||||
cabs.Add(dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool FindCAB(string path, out List<string> cabs)
|
||||
{
|
||||
var relativePath = Path.GetRelativePath(BaseFolder, path);
|
||||
cabs = CABMap.AsParallel().Where(x => x.Value.Path.Equals(relativePath, StringComparison.OrdinalIgnoreCase)).Select(x => x.Key).Distinct().ToList();
|
||||
Logger.Verbose($"Found {cabs.Count} that belongs to {relativePath}");
|
||||
return cabs.Count != 0;
|
||||
}
|
||||
|
||||
public static string[] ProcessFiles(string[] files)
|
||||
{
|
||||
foreach (var file in files)
|
||||
{
|
||||
Offsets.TryAdd(file, new HashSet<long>());
|
||||
Logger.Verbose($"Added {file} to Offsets dictionary");
|
||||
if (FindCAB(file, out var cabs))
|
||||
{
|
||||
AddCABOffsets(files, cabs);
|
||||
}
|
||||
}
|
||||
Logger.Verbose($"Finished resolving dependncies, the original {files.Length} files will be loaded entirely, and the {Offsets.Count - files.Length} dependicnes will be loaded from cached offsets only");
|
||||
return Offsets.Keys.ToArray();
|
||||
}
|
||||
|
||||
public static string[] ProcessDependencies(string[] files)
|
||||
{
|
||||
if (CABMap.Count == 0)
|
||||
{
|
||||
Logger.Warning("CABMap is not build, skip resolving dependencies...");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info("Resolving Dependencies...");
|
||||
files = ProcessFiles(files);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
public static void BuildCABMap(string[] files, string mapName, string baseFolder, Game game)
|
||||
{
|
||||
Logger.Info("Building CABMap...");
|
||||
@@ -220,6 +269,7 @@ namespace AssetStudio
|
||||
CABMap.Add(cab, entry);
|
||||
}
|
||||
}
|
||||
Logger.Verbose($"Initialized CABMap with {CABMap.Count} entries");
|
||||
Logger.Info($"Loaded {mapName} !!");
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -342,7 +392,7 @@ namespace AssetStudio
|
||||
case ClassIDType.Texture2D:
|
||||
case ClassIDType.VideoClip:
|
||||
case ClassIDType.AudioClip:
|
||||
case ClassIDType.AnimationClip:
|
||||
case ClassIDType.AnimationClip when AnimationClip.Parsable:
|
||||
asset.Name = objectReader.ReadAlignedString();
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -56,6 +56,8 @@ namespace AssetStudio
|
||||
var path = Path.GetDirectoryName(Path.GetFullPath(files[0]));
|
||||
MergeSplitAssets(path);
|
||||
var toReadFile = ProcessingSplitFiles(files.ToList());
|
||||
if (ResolveDependencies)
|
||||
toReadFile = AssetsHelper.ProcessDependencies(toReadFile);
|
||||
Load(toReadFile);
|
||||
|
||||
if (Silent)
|
||||
@@ -89,6 +91,7 @@ namespace AssetStudio
|
||||
{
|
||||
foreach (var file in files)
|
||||
{
|
||||
Logger.Verbose($"caching {file} path and name to filter out duplicates");
|
||||
importFiles.Add(file);
|
||||
importFilesHash.Add(Path.GetFileName(file));
|
||||
}
|
||||
@@ -171,6 +174,7 @@ namespace AssetStudio
|
||||
|
||||
foreach (var sharedFile in assetsFile.m_Externals)
|
||||
{
|
||||
Logger.Verbose($"{assetsFile.fileName} needs external file {sharedFile.fileName}, attempting to look it up...");
|
||||
var sharedFileName = sharedFile.fileName;
|
||||
|
||||
if (!importFilesHash.Contains(sharedFileName))
|
||||
@@ -183,6 +187,7 @@ namespace AssetStudio
|
||||
var findFiles = Directory.GetFiles(Path.GetDirectoryName(reader.FullPath), sharedFileName, SearchOption.AllDirectories);
|
||||
if (findFiles.Length > 0)
|
||||
{
|
||||
Logger.Verbose($"Found {findFiles.Length} matching files, picking first file {findFiles[0]} !!");
|
||||
sharedFilePath = findFiles[0];
|
||||
}
|
||||
}
|
||||
@@ -193,6 +198,7 @@ namespace AssetStudio
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Verbose("Nothing was found, caching into non existant files to avoid repeated searching !!");
|
||||
noexistFiles.Add(sharedFilePath);
|
||||
}
|
||||
}
|
||||
@@ -214,6 +220,7 @@ namespace AssetStudio
|
||||
|
||||
private void LoadAssetsFromMemory(FileReader reader, string originalPath, string unityVersion = null, long originalOffset = 0)
|
||||
{
|
||||
Logger.Verbose($"Loading asset file {reader.FileName} with version {unityVersion} from {originalPath} at offset 0x{originalOffset:X8}");
|
||||
if (!assetsFileListHash.Contains(reader.FileName))
|
||||
{
|
||||
try
|
||||
@@ -228,38 +235,6 @@ namespace AssetStudio
|
||||
CheckStrippedVersion(assetsFile);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -290,6 +265,7 @@ namespace AssetStudio
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Verbose("Caching resource stream");
|
||||
resourceFileReaders[file.fileName] = subReader; //TODO
|
||||
}
|
||||
}
|
||||
@@ -335,6 +311,7 @@ namespace AssetStudio
|
||||
LoadWebFile(subReader);
|
||||
break;
|
||||
case FileType.ResourceFile:
|
||||
Logger.Verbose("Caching resource stream");
|
||||
resourceFileReaders[file.fileName] = subReader; //TODO
|
||||
break;
|
||||
}
|
||||
@@ -358,8 +335,7 @@ namespace AssetStudio
|
||||
using (ZipArchive archive = new ZipArchive(reader.BaseStream, ZipArchiveMode.Read))
|
||||
{
|
||||
List<string> splitFiles = new List<string>();
|
||||
// register all files before parsing the assets so that the external references can be found
|
||||
// and find split files
|
||||
Logger.Verbose("Register all files before parsing the assets so that the external references can be found and find split files");
|
||||
foreach (ZipArchiveEntry entry in archive.Entries)
|
||||
{
|
||||
if (entry.Name.Contains(".split"))
|
||||
@@ -378,7 +354,7 @@ namespace AssetStudio
|
||||
}
|
||||
}
|
||||
|
||||
// merge split files and load the result
|
||||
Logger.Verbose("Merge split files and load the result");
|
||||
foreach (string basePath in splitFiles)
|
||||
{
|
||||
try
|
||||
@@ -406,15 +382,14 @@ namespace AssetStudio
|
||||
}
|
||||
}
|
||||
|
||||
// load all entries
|
||||
Logger.Verbose("Load all entries");
|
||||
Logger.Verbose($"Found {archive.Entries.Count} entries");
|
||||
foreach (ZipArchiveEntry entry in archive.Entries)
|
||||
{
|
||||
try
|
||||
{
|
||||
string dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), reader.FileName, entry.FullName);
|
||||
// create a new stream
|
||||
// - to store the deflated stream in
|
||||
// - to keep the data for later extraction
|
||||
Logger.Verbose("Create a new stream to store the deflated stream in and keep the data for later extraction");
|
||||
Stream streamReader = new MemoryStream();
|
||||
using (Stream entryStream = entry.Open())
|
||||
{
|
||||
@@ -429,6 +404,7 @@ namespace AssetStudio
|
||||
entryReader.Position = 0;
|
||||
if (!resourceFileReaders.ContainsKey(entry.Name))
|
||||
{
|
||||
Logger.Verbose("Caching resource file");
|
||||
resourceFileReaders.Add(entry.Name, entryReader);
|
||||
}
|
||||
}
|
||||
@@ -459,27 +435,14 @@ namespace AssetStudio
|
||||
{
|
||||
foreach (var offset in offsets)
|
||||
{
|
||||
var name = offset.ToString("X8");
|
||||
Logger.Info($"Loading Block {name}");
|
||||
|
||||
stream.Offset = offset;
|
||||
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), name);
|
||||
var subReader = new FileReader(dummyPath, stream, true);
|
||||
LoadBundleFile(subReader, reader.FullPath, offset, false);
|
||||
LoadBlockSubFile(reader.FullPath, stream, offset);
|
||||
}
|
||||
AssetsHelper.Remove(reader.FullPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
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.AbsolutePosition, false);
|
||||
LoadBlockSubFile(reader.FullPath, stream, stream.AbsolutePosition);
|
||||
} while (stream.Remaining > 0);
|
||||
}
|
||||
}
|
||||
@@ -492,20 +455,27 @@ namespace AssetStudio
|
||||
reader.Dispose();
|
||||
}
|
||||
}
|
||||
private void LoadBlockSubFile(string path, OffsetStream stream, long offset)
|
||||
{
|
||||
var name = offset.ToString("X8");
|
||||
Logger.Info($"Loading Block {name}");
|
||||
|
||||
stream.Offset = offset;
|
||||
var dummyPath = Path.Combine(Path.GetDirectoryName(path), name);
|
||||
var subReader = new FileReader(dummyPath, stream, true);
|
||||
LoadBundleFile(subReader, path, offset, false);
|
||||
}
|
||||
private void LoadBlkFile(FileReader reader)
|
||||
{
|
||||
Logger.Info("Loading " + reader.FullPath);
|
||||
try
|
||||
{
|
||||
using var stream = BlkUtils.Decrypt(reader, (Blk)Game);
|
||||
if (AssetsHelper.TryGet(reader.FullPath, out var offsets))
|
||||
{
|
||||
foreach (var offset in offsets)
|
||||
foreach (var offset in stream.GetOffsets(reader.FullPath))
|
||||
{
|
||||
var name = offset.ToString("X8");
|
||||
Logger.Info($"Loading Block {name}");
|
||||
|
||||
stream.Offset = offset;
|
||||
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), name);
|
||||
var subReader = new FileReader(dummyPath, stream, true);
|
||||
switch (subReader.FileType)
|
||||
@@ -518,30 +488,6 @@ namespace AssetStudio
|
||||
break;
|
||||
}
|
||||
}
|
||||
AssetsHelper.Remove(reader.FullPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
var name = stream.AbsolutePosition.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.AbsolutePosition, false);
|
||||
break;
|
||||
case FileType.Mhy0File:
|
||||
LoadMhy0File(subReader, reader.FullPath, stream.AbsolutePosition, false);
|
||||
break;
|
||||
}
|
||||
|
||||
stream.Offset = stream.AbsolutePosition;
|
||||
} while (stream.Remaining > 0);
|
||||
}
|
||||
}
|
||||
catch (InvalidCastException)
|
||||
{
|
||||
@@ -565,6 +511,7 @@ namespace AssetStudio
|
||||
try
|
||||
{
|
||||
var mhy0File = new Mhy0File(reader, reader.FullPath, (Mhy0)Game);
|
||||
Logger.Verbose($"mhy0 total size: {mhy0File.TotalSize:X8}");
|
||||
foreach (var file in mhy0File.fileList)
|
||||
{
|
||||
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), file.fileName);
|
||||
@@ -575,6 +522,7 @@ namespace AssetStudio
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Verbose("Caching resource stream");
|
||||
resourceFileReaders[file.fileName] = cabReader; //TODO
|
||||
}
|
||||
}
|
||||
@@ -612,6 +560,8 @@ namespace AssetStudio
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
Logger.Verbose("Cleaning up...");
|
||||
|
||||
foreach (var assetsFile in assetsFileList)
|
||||
{
|
||||
assetsFile.Objects.Clear();
|
||||
@@ -653,103 +603,40 @@ namespace AssetStudio
|
||||
var objectReader = new ObjectReader(assetsFile.reader, assetsFile, objectInfo, Game);
|
||||
try
|
||||
{
|
||||
Object obj;
|
||||
switch (objectReader.type)
|
||||
Object obj = objectReader.type switch
|
||||
{
|
||||
case ClassIDType.Animation:
|
||||
obj = new Animation(objectReader);
|
||||
break;
|
||||
case ClassIDType.AnimationClip:
|
||||
obj = new AnimationClip(objectReader);
|
||||
break;
|
||||
case ClassIDType.Animator:
|
||||
obj = new Animator(objectReader);
|
||||
break;
|
||||
case ClassIDType.AnimatorController:
|
||||
obj = new AnimatorController(objectReader);
|
||||
break;
|
||||
case ClassIDType.AnimatorOverrideController:
|
||||
obj = new AnimatorOverrideController(objectReader);
|
||||
break;
|
||||
case ClassIDType.AssetBundle:
|
||||
obj = new AssetBundle(objectReader);
|
||||
break;
|
||||
case ClassIDType.AudioClip:
|
||||
obj = new AudioClip(objectReader);
|
||||
break;
|
||||
case ClassIDType.Avatar:
|
||||
obj = new Avatar(objectReader);
|
||||
break;
|
||||
case ClassIDType.Font:
|
||||
obj = new Font(objectReader);
|
||||
break;
|
||||
case ClassIDType.GameObject:
|
||||
obj = new GameObject(objectReader);
|
||||
break;
|
||||
case ClassIDType.IndexObject:
|
||||
obj = new IndexObject(objectReader);
|
||||
break;
|
||||
case ClassIDType.Material:
|
||||
obj = new Material(objectReader);
|
||||
break;
|
||||
case ClassIDType.Mesh:
|
||||
obj = new Mesh(objectReader);
|
||||
break;
|
||||
case ClassIDType.MeshFilter:
|
||||
obj = new MeshFilter(objectReader);
|
||||
break;
|
||||
case ClassIDType.MeshRenderer when Renderer.Parsable:
|
||||
obj = new MeshRenderer(objectReader);
|
||||
break;
|
||||
case ClassIDType.MiHoYoBinData:
|
||||
obj = new MiHoYoBinData(objectReader);
|
||||
break;
|
||||
case ClassIDType.MonoBehaviour:
|
||||
obj = new MonoBehaviour(objectReader);
|
||||
break;
|
||||
case ClassIDType.MonoScript:
|
||||
obj = new MonoScript(objectReader);
|
||||
break;
|
||||
case ClassIDType.MovieTexture:
|
||||
obj = new MovieTexture(objectReader);
|
||||
break;
|
||||
case ClassIDType.PlayerSettings:
|
||||
obj = new PlayerSettings(objectReader);
|
||||
break;
|
||||
case ClassIDType.RectTransform:
|
||||
obj = new RectTransform(objectReader);
|
||||
break;
|
||||
case ClassIDType.Shader when Shader.Parsable:
|
||||
obj = new Shader(objectReader);
|
||||
break;
|
||||
case ClassIDType.SkinnedMeshRenderer when Renderer.Parsable:
|
||||
obj = new SkinnedMeshRenderer(objectReader);
|
||||
break;
|
||||
case ClassIDType.Sprite:
|
||||
obj = new Sprite(objectReader);
|
||||
break;
|
||||
case ClassIDType.SpriteAtlas:
|
||||
obj = new SpriteAtlas(objectReader);
|
||||
break;
|
||||
case ClassIDType.TextAsset:
|
||||
obj = new TextAsset(objectReader);
|
||||
break;
|
||||
case ClassIDType.Texture2D:
|
||||
obj = new Texture2D(objectReader);
|
||||
break;
|
||||
case ClassIDType.Transform:
|
||||
obj = new Transform(objectReader);
|
||||
break;
|
||||
case ClassIDType.VideoClip:
|
||||
obj = new VideoClip(objectReader);
|
||||
break;
|
||||
case ClassIDType.ResourceManager:
|
||||
obj = new ResourceManager(objectReader);
|
||||
break;
|
||||
default:
|
||||
obj = new Object(objectReader);
|
||||
break;
|
||||
}
|
||||
ClassIDType.Animation => new Animation(objectReader),
|
||||
ClassIDType.AnimationClip when AnimationClip.Parsable => new AnimationClip(objectReader),
|
||||
ClassIDType.Animator => new Animator(objectReader),
|
||||
ClassIDType.AnimatorController => new AnimatorController(objectReader),
|
||||
ClassIDType.AnimatorOverrideController => new AnimatorOverrideController(objectReader),
|
||||
ClassIDType.AssetBundle => new AssetBundle(objectReader),
|
||||
ClassIDType.AudioClip => new AudioClip(objectReader),
|
||||
ClassIDType.Avatar => new Avatar(objectReader),
|
||||
ClassIDType.Font => new Font(objectReader),
|
||||
ClassIDType.GameObject => new GameObject(objectReader),
|
||||
ClassIDType.IndexObject => new IndexObject(objectReader),
|
||||
ClassIDType.Material => new Material(objectReader),
|
||||
ClassIDType.Mesh => new Mesh(objectReader),
|
||||
ClassIDType.MeshFilter => new MeshFilter(objectReader),
|
||||
ClassIDType.MeshRenderer when Renderer.Parsable => new MeshRenderer(objectReader),
|
||||
ClassIDType.MiHoYoBinData => new MiHoYoBinData(objectReader),
|
||||
ClassIDType.MonoBehaviour => new MonoBehaviour(objectReader),
|
||||
ClassIDType.MonoScript => new MonoScript(objectReader),
|
||||
ClassIDType.MovieTexture => new MovieTexture(objectReader),
|
||||
ClassIDType.PlayerSettings => new PlayerSettings(objectReader),
|
||||
ClassIDType.RectTransform => new RectTransform(objectReader),
|
||||
ClassIDType.Shader when Shader.Parsable => new Shader(objectReader),
|
||||
ClassIDType.SkinnedMeshRenderer when Renderer.Parsable => new SkinnedMeshRenderer(objectReader),
|
||||
ClassIDType.Sprite => new Sprite(objectReader),
|
||||
ClassIDType.SpriteAtlas => new SpriteAtlas(objectReader),
|
||||
ClassIDType.TextAsset => new TextAsset(objectReader),
|
||||
ClassIDType.Texture2D => new Texture2D(objectReader),
|
||||
ClassIDType.Transform => new Transform(objectReader),
|
||||
ClassIDType.VideoClip => new VideoClip(objectReader),
|
||||
ClassIDType.ResourceManager => new ResourceManager(objectReader),
|
||||
_ => new Object(objectReader),
|
||||
};
|
||||
assetsFile.AddObject(obj);
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -784,6 +671,7 @@ namespace AssetStudio
|
||||
}
|
||||
if (obj is GameObject m_GameObject)
|
||||
{
|
||||
Logger.Verbose($"GameObject with {m_GameObject.m_PathID} in file {m_GameObject.assetsFile.fileName} has {m_GameObject.m_Components.Length} components, Attempting to fetch them...");
|
||||
foreach (var pptr in m_GameObject.m_Components)
|
||||
{
|
||||
if (pptr.TryGet(out var m_Component))
|
||||
@@ -791,21 +679,27 @@ namespace AssetStudio
|
||||
switch (m_Component)
|
||||
{
|
||||
case Transform m_Transform:
|
||||
Logger.Verbose($"Fetched Transform component with {m_Transform.m_PathID} in file {m_Transform.assetsFile.fileName}, assigning to GameObject components...");
|
||||
m_GameObject.m_Transform = m_Transform;
|
||||
break;
|
||||
case MeshRenderer m_MeshRenderer:
|
||||
Logger.Verbose($"Fetched MeshRenderer component with {m_MeshRenderer.m_PathID} in file {m_MeshRenderer.assetsFile.fileName}, assigning to GameObject components...");
|
||||
m_GameObject.m_MeshRenderer = m_MeshRenderer;
|
||||
break;
|
||||
case MeshFilter m_MeshFilter:
|
||||
Logger.Verbose($"Fetched MeshFilter component with {m_MeshFilter.m_PathID} in file {m_MeshFilter.assetsFile.fileName}, assigning to GameObject components...");
|
||||
m_GameObject.m_MeshFilter = m_MeshFilter;
|
||||
break;
|
||||
case SkinnedMeshRenderer m_SkinnedMeshRenderer:
|
||||
Logger.Verbose($"Fetched SkinnedMeshRenderer component with {m_SkinnedMeshRenderer.m_PathID} in file {m_SkinnedMeshRenderer.assetsFile.fileName}, assigning to GameObject components...");
|
||||
m_GameObject.m_SkinnedMeshRenderer = m_SkinnedMeshRenderer;
|
||||
break;
|
||||
case Animator m_Animator:
|
||||
Logger.Verbose($"Fetched Animator component with {m_Animator.m_PathID} in file {m_Animator.assetsFile.fileName}, assigning to GameObject components...");
|
||||
m_GameObject.m_Animator = m_Animator;
|
||||
break;
|
||||
case Animation m_Animation:
|
||||
Logger.Verbose($"Fetched Animation component with {m_Animation.m_PathID} in file {m_Animation.assetsFile.fileName}, assigning to GameObject components...");
|
||||
m_GameObject.m_Animation = m_Animation;
|
||||
break;
|
||||
}
|
||||
@@ -816,12 +710,14 @@ namespace AssetStudio
|
||||
{
|
||||
if (m_SpriteAtlas.m_RenderDataMap.Count > 0)
|
||||
{
|
||||
Logger.Verbose($"SpriteAtlas with {m_SpriteAtlas.m_PathID} in file {m_SpriteAtlas.assetsFile.fileName} has {m_SpriteAtlas.m_PackedSprites.Length} packed sprites, Attempting to fetch them...");
|
||||
foreach (var m_PackedSprite in m_SpriteAtlas.m_PackedSprites)
|
||||
{
|
||||
if (m_PackedSprite.TryGet(out var m_Sprite))
|
||||
{
|
||||
if (m_Sprite.m_SpriteAtlas.IsNull)
|
||||
{
|
||||
Logger.Verbose($"Fetched Sprite with {m_Sprite.m_PathID} in file {m_Sprite.assetsFile.fileName}, assigning to parent SpriteAtlas...");
|
||||
m_Sprite.m_SpriteAtlas.Set(m_SpriteAtlas);
|
||||
}
|
||||
else
|
||||
@@ -829,6 +725,7 @@ namespace AssetStudio
|
||||
m_Sprite.m_SpriteAtlas.TryGet(out var m_SpriteAtlaOld);
|
||||
if (m_SpriteAtlaOld.m_IsVariant)
|
||||
{
|
||||
Logger.Verbose($"Fetched Sprite with {m_Sprite.m_PathID} in file {m_Sprite.assetsFile.fileName} has a variant of the origianl SpriteAtlas, disposing of the variant and assinging to the parent SpriteAtlas...");
|
||||
m_Sprite.m_SpriteAtlas.Set(m_SpriteAtlas);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Data;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
@@ -49,6 +50,20 @@ namespace AssetStudio
|
||||
public uint compressedBlocksInfoSize;
|
||||
public uint uncompressedBlocksInfoSize;
|
||||
public ArchiveFlags flags;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append($"signature: {signature} | ");
|
||||
sb.Append($"version: {version} | ");
|
||||
sb.Append($"unityVersion: {unityVersion} | ");
|
||||
sb.Append($"unityRevision: {unityRevision} | ");
|
||||
sb.Append($"size: 0x{size:X8} | ");
|
||||
sb.Append($"compressedBlocksInfoSize: 0x{compressedBlocksInfoSize:X8} | ");
|
||||
sb.Append($"uncompressedBlocksInfoSize: 0x{uncompressedBlocksInfoSize:X8} | ");
|
||||
sb.Append($"flags: 0x{(int)flags:X8}");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public class StorageBlock
|
||||
@@ -56,6 +71,15 @@ namespace AssetStudio
|
||||
public uint compressedSize;
|
||||
public uint uncompressedSize;
|
||||
public StorageBlockFlags flags;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append($"compressedSize: 0x{compressedSize:X8} | ");
|
||||
sb.Append($"uncompressedSize: 0x{uncompressedSize:X8} | ");
|
||||
sb.Append($"flags: 0x{(int)flags:X8}");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public class Node
|
||||
@@ -64,6 +88,16 @@ namespace AssetStudio
|
||||
public long size;
|
||||
public uint flags;
|
||||
public string path;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append($"offset: 0x{offset:X8} | ");
|
||||
sb.Append($"size: 0x{size:X8} | ");
|
||||
sb.Append($"flags: {flags} | ");
|
||||
sb.Append($"path: {path}");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private Game Game;
|
||||
@@ -121,6 +155,7 @@ namespace AssetStudio
|
||||
{
|
||||
Header header = new Header();
|
||||
header.signature = reader.ReadStringToNull(20);
|
||||
Logger.Verbose($"Parsed signature {header.signature}");
|
||||
switch (header.signature)
|
||||
{
|
||||
case "UnityFS":
|
||||
@@ -129,6 +164,7 @@ namespace AssetStudio
|
||||
var version = reader.ReadUInt32();
|
||||
if (version > 11)
|
||||
{
|
||||
Logger.Verbose($"Encrypted bundle header with key {version}");
|
||||
XORShift128.InitSeed(version);
|
||||
header.version = 6;
|
||||
header.unityVersion = "5.x.x";
|
||||
@@ -205,6 +241,7 @@ namespace AssetStudio
|
||||
{
|
||||
Stream blocksStream;
|
||||
var uncompressedSizeSum = m_BlocksInfo.Sum(x => x.uncompressedSize);
|
||||
Logger.Verbose($"Total size of decompressed blocks: {uncompressedSizeSum}");
|
||||
if (uncompressedSizeSum >= int.MaxValue)
|
||||
{
|
||||
/*var memoryMappedFile = MemoryMappedFile.CreateNew(null, uncompressedSizeSum);
|
||||
@@ -220,26 +257,25 @@ namespace AssetStudio
|
||||
|
||||
private void ReadBlocksAndDirectory(FileReader reader, Stream blocksStream)
|
||||
{
|
||||
Logger.Verbose($"Writing block and directory to blocks stream...");
|
||||
|
||||
var isCompressed = m_Header.signature == "UnityWeb";
|
||||
foreach (var blockInfo in m_BlocksInfo)
|
||||
{
|
||||
var uncompressedBytes = reader.ReadBytes((int)blockInfo.compressedSize);
|
||||
if (isCompressed)
|
||||
{
|
||||
using (var memoryStream = new MemoryStream(uncompressedBytes))
|
||||
{
|
||||
using (var decompressStream = SevenZipHelper.StreamDecompress(memoryStream))
|
||||
{
|
||||
using var memoryStream = new MemoryStream(uncompressedBytes);
|
||||
using var decompressStream = SevenZipHelper.StreamDecompress(memoryStream);
|
||||
uncompressedBytes = decompressStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
blocksStream.Write(uncompressedBytes, 0, uncompressedBytes.Length);
|
||||
}
|
||||
blocksStream.Position = 0;
|
||||
var blocksReader = new EndianBinaryReader(blocksStream);
|
||||
var nodesCount = blocksReader.ReadInt32();
|
||||
m_DirectoryInfo = new Node[nodesCount];
|
||||
Logger.Verbose($"Directory count: {nodesCount}");
|
||||
for (int i = 0; i < nodesCount; i++)
|
||||
{
|
||||
m_DirectoryInfo[i] = new Node
|
||||
@@ -253,6 +289,8 @@ namespace AssetStudio
|
||||
|
||||
public void ReadFiles(Stream blocksStream, string path)
|
||||
{
|
||||
Logger.Verbose($"Writing files from blocks stream...");
|
||||
|
||||
fileList = new StreamFile[m_DirectoryInfo.Length];
|
||||
for (int i = 0; i < m_DirectoryInfo.Length; i++)
|
||||
{
|
||||
@@ -286,6 +324,7 @@ namespace AssetStudio
|
||||
m_Header.uncompressedBlocksInfoSize ^= XORShift128.NextDecryptUInt();
|
||||
m_Header.compressedBlocksInfoSize ^= XORShift128.NextDecryptUInt();
|
||||
XORShift128.Init = false;
|
||||
Logger.Verbose($"Bundle header decrypted");
|
||||
}
|
||||
|
||||
private void ReadHeader(FileReader reader)
|
||||
@@ -317,10 +356,13 @@ namespace AssetStudio
|
||||
m_Header.compressedBlocksInfoSize -= 0xCA;
|
||||
m_Header.uncompressedBlocksInfoSize -= 0xCA;
|
||||
}
|
||||
|
||||
Logger.Verbose($"Bundle header Info: {m_Header}");
|
||||
}
|
||||
|
||||
private void ReadUnityCN(EndianBinaryReader reader)
|
||||
private void ReadUnityCN(FileReader reader)
|
||||
{
|
||||
Logger.Verbose($"Attempting to decrypt file {reader.FileName} with UnityCN encryption");
|
||||
ArchiveFlags mask;
|
||||
|
||||
var version = ParseVersion();
|
||||
@@ -339,8 +381,11 @@ namespace AssetStudio
|
||||
HasBlockInfoNeedPaddingAtStart = true;
|
||||
}
|
||||
|
||||
Logger.Verbose($"Mask set to 0x{mask:X8}");
|
||||
|
||||
if ((m_Header.flags & mask) != 0)
|
||||
{
|
||||
Logger.Verbose($"Encryption flag exist, file is encrypted, attempting to decrypt");
|
||||
UnityCN = new UnityCN(reader);
|
||||
}
|
||||
}
|
||||
@@ -367,6 +412,7 @@ namespace AssetStudio
|
||||
var blocksInfoBytesSpan = blocksInfoBytes.AsSpan();
|
||||
var uncompressedSize = m_Header.uncompressedBlocksInfoSize;
|
||||
var compressionType = (CompressionType)(m_Header.flags & ArchiveFlags.CompressionTypeMask);
|
||||
Logger.Verbose($"BlockInfo compression type: {compressionType}");
|
||||
switch (compressionType) //kArchiveCompressionTypeMask
|
||||
{
|
||||
case CompressionType.None: //None
|
||||
@@ -399,6 +445,7 @@ namespace AssetStudio
|
||||
case CompressionType.Lz4Mr0k: //Lz4Mr0k
|
||||
if (Mr0kUtils.IsMr0k(blocksInfoBytesSpan))
|
||||
{
|
||||
Logger.Verbose($"Header encrypted with mr0k, decrypting...");
|
||||
blocksInfoBytesSpan = Mr0kUtils.Decrypt(blocksInfoBytesSpan, (Mr0k)Game).ToArray();
|
||||
}
|
||||
goto case CompressionType.Lz4HC;
|
||||
@@ -413,6 +460,7 @@ namespace AssetStudio
|
||||
}
|
||||
var blocksInfoCount = blocksInfoReader.ReadInt32();
|
||||
m_BlocksInfo = new StorageBlock[blocksInfoCount];
|
||||
Logger.Verbose($"Blocks count: {blocksInfoCount}");
|
||||
for (int i = 0; i < blocksInfoCount; i++)
|
||||
{
|
||||
m_BlocksInfo[i] = new StorageBlock
|
||||
@@ -421,10 +469,13 @@ namespace AssetStudio
|
||||
compressedSize = blocksInfoReader.ReadUInt32(),
|
||||
flags = (StorageBlockFlags)blocksInfoReader.ReadUInt16()
|
||||
};
|
||||
|
||||
Logger.Verbose($"Block {i} Info: {m_BlocksInfo[i]}");
|
||||
}
|
||||
|
||||
var nodesCount = blocksInfoReader.ReadInt32();
|
||||
m_DirectoryInfo = new Node[nodesCount];
|
||||
Logger.Verbose($"Directory count: {nodesCount}");
|
||||
for (int i = 0; i < nodesCount; i++)
|
||||
{
|
||||
m_DirectoryInfo[i] = new Node
|
||||
@@ -434,6 +485,8 @@ namespace AssetStudio
|
||||
flags = blocksInfoReader.ReadUInt32(),
|
||||
path = blocksInfoReader.ReadStringToNull(),
|
||||
};
|
||||
|
||||
Logger.Verbose($"Directory {i} Info: {m_DirectoryInfo[i]}");
|
||||
}
|
||||
}
|
||||
if (HasBlockInfoNeedPaddingAtStart && (m_Header.flags & ArchiveFlags.BlockInfoNeedPaddingAtStart) != 0)
|
||||
@@ -444,10 +497,14 @@ namespace AssetStudio
|
||||
|
||||
private void ReadBlocks(FileReader reader, Stream blocksStream)
|
||||
{
|
||||
Logger.Verbose($"Writing block to blocks stream...");
|
||||
|
||||
for (int i = 0; i < m_BlocksInfo.Length; i++)
|
||||
{
|
||||
Logger.Verbose($"Reading block {i}...");
|
||||
var blockInfo = m_BlocksInfo[i];
|
||||
var compressionType = (CompressionType)(blockInfo.flags & StorageBlockFlags.CompressionTypeMask);
|
||||
Logger.Verbose($"Block compression type {compressionType}");
|
||||
switch (compressionType) //kStorageBlockCompressionTypeMask
|
||||
{
|
||||
case CompressionType.None: //None
|
||||
@@ -470,10 +527,12 @@ namespace AssetStudio
|
||||
var compressedBytesSpan = compressedBytes.AsSpan(0, compressedSize);
|
||||
if (compressionType == CompressionType.Lz4Mr0k && Mr0kUtils.IsMr0k(compressedBytes))
|
||||
{
|
||||
Logger.Verbose($"Block encrypted with mr0k, decrypting...");
|
||||
compressedBytesSpan = Mr0kUtils.Decrypt(compressedBytesSpan, (Mr0k)Game);
|
||||
}
|
||||
if (Game.Type.IsUnityCN() && ((int)blockInfo.flags & 0x100) != 0)
|
||||
{
|
||||
Logger.Verbose($"Decrypting block with UnityCN...");
|
||||
UnityCN.DecryptBlock(compressedBytes, compressedSize, i);
|
||||
}
|
||||
if (Game.Type.IsNetEase() && i == 0)
|
||||
|
||||
@@ -965,6 +965,8 @@ namespace AssetStudio
|
||||
|
||||
public sealed class AnimationClip : NamedObject
|
||||
{
|
||||
public static bool Parsable;
|
||||
|
||||
public AnimationType m_AnimationType;
|
||||
public bool m_Legacy;
|
||||
public bool m_Compressed;
|
||||
|
||||
@@ -37,6 +37,8 @@ namespace AssetStudio
|
||||
serializedType = reader.serializedType;
|
||||
byteSize = reader.byteSize;
|
||||
|
||||
Logger.Verbose($"Attempting to read object {type} with {m_PathID} in file {assetsFile.fileName}, starting from offset 0x{reader.byteStart:X8} with expected size of 0x{byteSize:X8} !!");
|
||||
|
||||
if (platform == BuildTarget.NoTarget)
|
||||
{
|
||||
var m_ObjectHideFlags = reader.ReadUInt32();
|
||||
@@ -81,6 +83,7 @@ namespace AssetStudio
|
||||
|
||||
public byte[] GetRawData()
|
||||
{
|
||||
Logger.Verbose($"Dumping raw bytes of the object with {m_PathID} in file {assetsFile.fileName}...");
|
||||
reader.Reset();
|
||||
return reader.ReadBytes((int)byteSize);
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ namespace AssetStudio
|
||||
var m_ReadAllowed = reader.ReadBoolean();
|
||||
}
|
||||
}
|
||||
if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 2) || reader.Game.Type.IsGI()) //2018.2 and up
|
||||
if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 2)) //2018.2 and up
|
||||
{
|
||||
var m_StreamingMipmaps = reader.ReadBoolean();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
@@ -8,16 +10,20 @@ namespace AssetStudio
|
||||
private const int DataOffset = 0x2A;
|
||||
private const int KeySize = 0x1000;
|
||||
private const int SeedBlockSize = 0x800;
|
||||
private const int BufferSize = 0x10000;
|
||||
|
||||
public static XORStream Decrypt(FileReader reader, Blk blk)
|
||||
{
|
||||
reader.Endian = EndianType.LittleEndian;
|
||||
|
||||
var signature = reader.ReadStringToNull();
|
||||
Logger.Verbose($"Signature: {signature}");
|
||||
var count = reader.ReadInt32();
|
||||
Logger.Verbose($"Key size: {count}");
|
||||
var key = reader.ReadBytes(count);
|
||||
reader.Position += count;
|
||||
var seedSize = Math.Min(reader.ReadInt16(), blk.SBox.IsNullOrEmpty() ? SeedBlockSize : SeedBlockSize * 2);
|
||||
Logger.Verbose($"Seed size: 0x{seedSize:X8}");
|
||||
|
||||
if (!blk.SBox.IsNullOrEmpty() && blk.Type.IsGI())
|
||||
{
|
||||
@@ -47,6 +53,8 @@ namespace AssetStudio
|
||||
var keyHigh = BinaryPrimitives.ReadUInt64LittleEndian(key.AsSpan(8, 8));
|
||||
var seed = keyLow ^ keyHigh ^ keySeed ^ blk.InitSeed;
|
||||
|
||||
Logger.Verbose($"Seed: 0x{seed:X8}");
|
||||
|
||||
var mt64 = new MT19937_64(seed);
|
||||
var xorpad = new byte[KeySize];
|
||||
for (int i = 0; i < KeySize; i += 8)
|
||||
@@ -56,5 +64,48 @@ namespace AssetStudio
|
||||
|
||||
return new XORStream(reader.BaseStream, DataOffset, xorpad);
|
||||
}
|
||||
|
||||
public static IEnumerable<long> GetOffsets(this XORStream stream, string path)
|
||||
{
|
||||
if (AssetsHelper.TryGet(path, out var offsets))
|
||||
{
|
||||
foreach(var offset in offsets)
|
||||
{
|
||||
stream.Offset = offset;
|
||||
yield return offset;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using var reader = new FileReader(path, stream, true);
|
||||
var signature = reader.FileType switch
|
||||
{
|
||||
FileType.BundleFile => "UnityFS\x00",
|
||||
FileType.Mhy0File => "mhy0",
|
||||
_ => throw new InvalidOperationException()
|
||||
};
|
||||
|
||||
Logger.Verbose($"Prased signature: {signature}");
|
||||
|
||||
var signatureBytes = Encoding.UTF8.GetBytes(signature);
|
||||
var buffer = BigArrayPool<byte>.Shared.Rent(BufferSize);
|
||||
while (stream.Remaining > 0)
|
||||
{
|
||||
var index = 0;
|
||||
var absOffset = stream.AbsolutePosition;
|
||||
var read = stream.Read(buffer);
|
||||
while (index < read)
|
||||
{
|
||||
index = buffer.AsSpan(0, read).Search(signatureBytes, index);
|
||||
if (index == -1) break;
|
||||
var offset = absOffset + index;
|
||||
stream.Offset = offset;
|
||||
yield return offset;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
BigArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ namespace AssetStudio
|
||||
|
||||
var encryptedBlockSize = Math.Min(0x10 * ((data.Length - 0x94) >> 7), BlockSize);
|
||||
|
||||
Logger.Verbose($"Encrypted block size: {encryptedBlockSize}");
|
||||
if (!mr0k.InitVector.IsNullOrEmpty())
|
||||
{
|
||||
for (int i = 0; i < mr0k.InitVector.Length; i++)
|
||||
@@ -47,6 +48,8 @@ namespace AssetStudio
|
||||
var seed2 = BinaryPrimitives.ReadUInt64LittleEndian(key3);
|
||||
var seed = seed2 ^ seed1 ^ (seed1 + (uint)data.Length - 20);
|
||||
|
||||
Logger.Verbose($"Seed: 0x{seed:X8}");
|
||||
|
||||
var encryptedBlock = data.Slice(0x94, encryptedBlockSize);
|
||||
var seedSpan = BitConverter.GetBytes(seed);
|
||||
for (var i = 0; i < encryptedBlockSize; i++)
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace AssetStudio
|
||||
private static readonly byte[] Signature = new byte[] { 0xEE, 0xDD };
|
||||
public static void Decrypt(Span<byte> bytes)
|
||||
{
|
||||
Logger.Verbose($"Attempting to decrypt block with NetEase encryption...");
|
||||
|
||||
var (encryptedOffset, encryptedSize) = ReadHeader(bytes);
|
||||
var encrypted = bytes.Slice(encryptedOffset, encryptedSize);
|
||||
var encryptedInts = MemoryMarshal.Cast<byte, int>(encrypted);
|
||||
@@ -77,7 +79,7 @@ namespace AssetStudio
|
||||
}
|
||||
private static (int, int) ReadHeader(Span<byte> bytes)
|
||||
{
|
||||
var index = bytes.Search(Signature, 0);
|
||||
var index = bytes.Search(Signature);
|
||||
if (index == -1 || index >= 0x40)
|
||||
{
|
||||
throw new Exception("Header not found !!");
|
||||
@@ -127,6 +129,7 @@ namespace AssetStudio
|
||||
throw new Exception("Unsupported version");
|
||||
}
|
||||
var versionString = version.ToString("X4");
|
||||
Logger.Verbose($"Bundle version: {versionString}");
|
||||
Encoding.UTF8.GetBytes(versionString, bytes);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace AssetStudio
|
||||
|
||||
public static void Decrypt(Span<byte> data, string path)
|
||||
{
|
||||
Logger.Verbose($"Attempting to decrypt block with OPFP encryption...");
|
||||
if (IsEncryptionBundle(path, out var key, out var version))
|
||||
{
|
||||
switch (version)
|
||||
@@ -41,30 +42,39 @@ namespace AssetStudio
|
||||
{
|
||||
if (V1_Prefixes.Any(prefix => relativePath.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
Logger.Verbose("Path matches with V1 prefixes, generatring key...");
|
||||
key = (byte)Path.GetFileName(relativePath).Length;
|
||||
version = 1;
|
||||
Logger.Verbose($"version: {version}, key: {key}");
|
||||
return true;
|
||||
}
|
||||
else if (V0_Prefixes.Any(prefix => relativePath.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
Logger.Verbose("Path matches with V2 prefixes, generatring key...");
|
||||
|
||||
key = (byte)relativePath.Length;
|
||||
version = 0;
|
||||
Logger.Verbose($"version: {version}, key: {key}");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Logger.Verbose($"Unknown encryption type");
|
||||
key = 0x00;
|
||||
version = 0;
|
||||
return false;
|
||||
}
|
||||
private static bool IsFixedPath(string path, out string fixedPath)
|
||||
{
|
||||
Logger.Verbose($"Fixing path before checking...");
|
||||
var dirs = path.Split(Path.DirectorySeparatorChar);
|
||||
if (dirs.Contains(BaseFolder))
|
||||
{
|
||||
var idx = Array.IndexOf(dirs, BaseFolder);
|
||||
Logger.Verbose($"Seperator found at index {idx}");
|
||||
fixedPath = string.Join(Path.DirectorySeparatorChar, dirs[(idx+1)..]).Replace("\\", "/");
|
||||
return true;
|
||||
}
|
||||
Logger.Verbose($"Unknown path");
|
||||
fixedPath = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -28,8 +28,11 @@ namespace AssetStudio
|
||||
DecryptKey(signatureKey, signatureBytes);
|
||||
|
||||
var str = Encoding.UTF8.GetString(signatureBytes);
|
||||
Logger.Verbose($"Decrypted signature is {str}");
|
||||
if (str != Signature)
|
||||
throw new Exception("Invalid Signature !!");
|
||||
{
|
||||
throw new Exception($"Invalid Signature, Expected {Signature} but found {str} instead");
|
||||
}
|
||||
|
||||
DecryptKey(infoKey, infoBytes);
|
||||
|
||||
@@ -41,19 +44,20 @@ namespace AssetStudio
|
||||
var idx = (i % 4 * 4) + (i / 4);
|
||||
Sub[idx] = subBytes[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static bool SetKey(Entry entry)
|
||||
{
|
||||
Logger.Verbose($"Initializing decryptor with key {entry.Key}");
|
||||
try
|
||||
{
|
||||
using (var aes = Aes.Create())
|
||||
{
|
||||
using var aes = Aes.Create();
|
||||
aes.Mode = CipherMode.ECB;
|
||||
aes.Key = Convert.FromHexString(entry.Key);
|
||||
|
||||
Encryptor = aes.CreateEncryptor();
|
||||
}
|
||||
Logger.Verbose($"Decryptor initialized !!");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace AssetStudio
|
||||
return buffer;
|
||||
}
|
||||
public static int Search(this byte[] src, string value, int offset = 0) => Search(src.AsSpan(), Encoding.UTF8.GetBytes(value), offset);
|
||||
public static int Search(this Span<byte> src, byte[] pattern, int offset)
|
||||
public static int Search(this Span<byte> src, byte[] pattern, int offset = 0)
|
||||
{
|
||||
int maxFirstCharSlot = src.Length - pattern.Length + 1;
|
||||
for (int i = offset; i < maxFirstCharSlot; i++)
|
||||
|
||||
@@ -13,5 +13,15 @@ namespace AssetStudio
|
||||
|
||||
//custom
|
||||
public string fileName;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append($"Guid: {guid} | ");
|
||||
sb.Append($"type: {type} | ");
|
||||
sb.Append($"pathName: {pathName} | ");
|
||||
sb.Append($"fileName: {fileName}");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,12 +27,14 @@ namespace AssetStudio
|
||||
FullPath = Path.GetFullPath(path);
|
||||
FileName = Path.GetFileName(path);
|
||||
FileType = CheckFileType();
|
||||
Logger.Verbose($"File {path} type is {FileType}");
|
||||
}
|
||||
|
||||
private FileType CheckFileType()
|
||||
{
|
||||
var signature = this.ReadStringToNull(20);
|
||||
Position = 0;
|
||||
Logger.Verbose($"Parsed signature is {signature}");
|
||||
switch (signature)
|
||||
{
|
||||
case "ENCR":
|
||||
@@ -47,46 +49,59 @@ namespace AssetStudio
|
||||
return FileType.BlkFile;
|
||||
default:
|
||||
{
|
||||
Logger.Verbose("signature does not match any of the supported string signatures, attempting to check bytes signatures");
|
||||
byte[] magic = ReadBytes(2);
|
||||
Position = 0;
|
||||
Logger.Verbose($"Parsed signature is {Convert.ToHexString(magic)}");
|
||||
if (gzipMagic.SequenceEqual(magic))
|
||||
{
|
||||
return FileType.GZipFile;
|
||||
}
|
||||
Logger.Verbose($"Parsed signature does not match with expected signature {Convert.ToHexString(gzipMagic)}");
|
||||
Position = 0x20;
|
||||
magic = ReadBytes(6);
|
||||
Position = 0;
|
||||
Logger.Verbose($"Parsed signature is {Convert.ToHexString(magic)}");
|
||||
if (brotliMagic.SequenceEqual(magic))
|
||||
{
|
||||
return FileType.BrotliFile;
|
||||
}
|
||||
Logger.Verbose($"Parsed signature does not match with expected signature {Convert.ToHexString(brotliMagic)}");
|
||||
if (IsSerializedFile())
|
||||
{
|
||||
return FileType.AssetsFile;
|
||||
}
|
||||
magic = ReadBytes(4);
|
||||
Position = 0;
|
||||
Logger.Verbose($"Parsed signature is {Convert.ToHexString(magic)}");
|
||||
if (zipMagic.SequenceEqual(magic) || zipSpannedMagic.SequenceEqual(magic))
|
||||
{
|
||||
return FileType.ZipFile;
|
||||
}
|
||||
Logger.Verbose($"Parsed signature does not match with expected signature {Convert.ToHexString(zipMagic)} or {Convert.ToHexString(zipSpannedMagic)}");
|
||||
if (mhy0Magic.SequenceEqual(magic))
|
||||
{
|
||||
return FileType.Mhy0File;
|
||||
}
|
||||
Logger.Verbose($"Parsed signature does not match with expected signature {Convert.ToHexString(mhy0Magic)}");
|
||||
magic = ReadBytes(7);
|
||||
Position = 0;
|
||||
Logger.Verbose($"Parsed signature is {Convert.ToHexString(magic)}");
|
||||
if (narakaMagic.SequenceEqual(magic))
|
||||
{
|
||||
return FileType.BundleFile;
|
||||
}
|
||||
Logger.Verbose($"Parsed signature does not match with expected signature {Convert.ToHexString(narakaMagic)}");
|
||||
magic = ReadBytes(9);
|
||||
Position = 0;
|
||||
Logger.Verbose($"Parsed signature is {Convert.ToHexString(magic)}");
|
||||
if (gunfireMagic.SequenceEqual(magic))
|
||||
{
|
||||
Position = 0x32;
|
||||
return FileType.BundleFile;
|
||||
}
|
||||
Logger.Verbose($"Parsed signature does not match with expected signature {Convert.ToHexString(gunfireMagic)}");
|
||||
Logger.Verbose($"Parsed signature does not match any of the supported signatures, assuming resource file");
|
||||
return FileType.ResourceFile;
|
||||
}
|
||||
}
|
||||
@@ -94,9 +109,12 @@ namespace AssetStudio
|
||||
|
||||
private bool IsSerializedFile()
|
||||
{
|
||||
Logger.Verbose($"Attempting to check if the file is serialized file...");
|
||||
|
||||
var fileSize = BaseStream.Length;
|
||||
if (fileSize < 20)
|
||||
{
|
||||
Logger.Verbose($"File size 0x{fileSize:X8} is too small, minimal acceptable size is 0x14, aborting...");
|
||||
return false;
|
||||
}
|
||||
var m_MetadataSize = ReadUInt32();
|
||||
@@ -109,6 +127,7 @@ namespace AssetStudio
|
||||
{
|
||||
if (fileSize < 48)
|
||||
{
|
||||
Logger.Verbose($"File size 0x{fileSize:X8} for version {m_Version} is too small, minimal acceptable size is 0x30, aborting...");
|
||||
Position = 0;
|
||||
return false;
|
||||
}
|
||||
@@ -119,12 +138,15 @@ namespace AssetStudio
|
||||
Position = 0;
|
||||
if (m_FileSize != fileSize)
|
||||
{
|
||||
Logger.Verbose($"Parsed file size 0x{m_FileSize:X8} does not match stream size {fileSize}, file might be corrupted, aborting...");
|
||||
return false;
|
||||
}
|
||||
if (m_DataOffset > fileSize)
|
||||
{
|
||||
Logger.Verbose($"Parsed data offset 0x{m_DataOffset:X8} is outside the stream of the size {fileSize}, file might be corrupted, aborting...");
|
||||
return false;
|
||||
}
|
||||
Logger.Verbose($"Valid serialized file !!");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -133,8 +155,10 @@ namespace AssetStudio
|
||||
{
|
||||
public static FileReader PreProcessing(this FileReader reader, Game game)
|
||||
{
|
||||
Logger.Verbose($"Applying preprocessing to file {reader.FileName}");
|
||||
if (reader.FileType == FileType.ResourceFile || !game.Type.IsNormal())
|
||||
{
|
||||
Logger.Verbose("File is encrypted !!");
|
||||
switch (game.Type)
|
||||
{
|
||||
case GameType.GI_Pack:
|
||||
@@ -147,16 +171,13 @@ namespace AssetStudio
|
||||
reader = DecryptEnsembleStar(reader);
|
||||
break;
|
||||
case GameType.OPFP:
|
||||
reader = ParseOPFP(reader);
|
||||
break;
|
||||
case GameType.FakeHeader:
|
||||
case GameType.ShiningNikki:
|
||||
reader = ParseFakeHeader(reader);
|
||||
break;
|
||||
case GameType.FantasyOfWind:
|
||||
reader = DecryptFantasyOfWind(reader);
|
||||
break;
|
||||
case GameType.ShiningNikki:
|
||||
reader = ParseShiningNikki(reader);
|
||||
break;
|
||||
case GameType.HelixWaltz2:
|
||||
reader = ParseHelixWaltz2(reader);
|
||||
@@ -174,6 +195,7 @@ namespace AssetStudio
|
||||
}
|
||||
if (reader.FileType == FileType.BundleFile && game.Type.IsBlockFile())
|
||||
{
|
||||
Logger.Verbose("File might have multiple bundles !!");
|
||||
try
|
||||
{
|
||||
var signature = reader.ReadStringToNull();
|
||||
@@ -183,6 +205,8 @@ namespace AssetStudio
|
||||
var size = reader.ReadInt64();
|
||||
if (!(signature == "UnityFS" && size == reader.BaseStream.Length))
|
||||
{
|
||||
Logger.Verbose($"Found signature UnityFS, expected bundle size is 0x{size:X8}, found 0x{reader.BaseStream.Length} instead !!");
|
||||
Logger.Verbose("Loading as block file !!");
|
||||
reader.FileType = FileType.BlockFile;
|
||||
}
|
||||
}
|
||||
@@ -190,6 +214,7 @@ namespace AssetStudio
|
||||
reader.Position = 0;
|
||||
}
|
||||
|
||||
Logger.Verbose("No preprocessing is needed");
|
||||
return reader;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace AssetStudio
|
||||
public class ImportedFrame
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public Vector3 LocalRotation { get; set; }
|
||||
public Quaternion LocalRotation { get; set; }
|
||||
public Vector3 LocalPosition { get; set; }
|
||||
public Vector3 LocalScale { get; set; }
|
||||
public ImportedFrame Parent { get; set; }
|
||||
@@ -248,7 +248,7 @@ namespace AssetStudio
|
||||
{
|
||||
public string Path { get; set; }
|
||||
public List<ImportedKeyframe<Vector3>> Scalings = new List<ImportedKeyframe<Vector3>>();
|
||||
public List<ImportedKeyframe<Vector3>> Rotations = new List<ImportedKeyframe<Vector3>>();
|
||||
public List<ImportedKeyframe<Quaternion>> Rotations = new List<ImportedKeyframe<Quaternion>>();
|
||||
public List<ImportedKeyframe<Vector3>> Translations = new List<ImportedKeyframe<Vector3>>();
|
||||
public ImportedBlendShape BlendShape;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
@@ -34,4 +35,39 @@ namespace AssetStudio
|
||||
Console.WriteLine("[{0}] {1}", loggerEvent, message);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class FileLogger : ILogger
|
||||
{
|
||||
private const string LogFileName = "log.txt";
|
||||
private const string PrevLogFileName = "log_prev.txt";
|
||||
private readonly object LockWriter = new object();
|
||||
|
||||
public StreamWriter Writer;
|
||||
|
||||
public FileLogger()
|
||||
{
|
||||
var logPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, LogFileName);
|
||||
var prevLogPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, PrevLogFileName);
|
||||
|
||||
if (File.Exists(logPath))
|
||||
{
|
||||
File.Move(logPath, prevLogPath, true);
|
||||
}
|
||||
Writer = new StreamWriter(logPath, true) { AutoFlush = true };
|
||||
}
|
||||
~FileLogger()
|
||||
{
|
||||
Writer?.Dispose();
|
||||
}
|
||||
public void Log(LoggerEvent loggerEvent, string message, bool silent = false)
|
||||
{
|
||||
if (silent)
|
||||
return;
|
||||
|
||||
lock (LockWriter)
|
||||
{
|
||||
Writer.WriteLine($"[{DateTime.Now}][{loggerEvent}] {message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,9 @@ namespace AssetStudio
|
||||
{
|
||||
public static void MergeSplitAssets(string path, bool allDirectories = false)
|
||||
{
|
||||
Logger.Verbose($"Processing split assets (.splitX) prior to loading files...");
|
||||
var splitFiles = Directory.GetFiles(path, "*.split0", allDirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
|
||||
Logger.Verbose($"Found {splitFiles.Length} split files, attempting to merge...");
|
||||
foreach (var splitFile in splitFiles)
|
||||
{
|
||||
var destFile = Path.GetFileNameWithoutExtension(splitFile);
|
||||
@@ -27,6 +29,7 @@ namespace AssetStudio
|
||||
if (!File.Exists(destFull))
|
||||
{
|
||||
var splitParts = Directory.GetFiles(destPath, destFile + ".split*");
|
||||
Logger.Verbose($"Creating {destFull} where split files will be combined");
|
||||
using (var destStream = File.Create(destFull))
|
||||
{
|
||||
for (int i = 0; i < splitParts.Length; i++)
|
||||
@@ -35,6 +38,7 @@ namespace AssetStudio
|
||||
using (var sourceStream = File.OpenRead(splitPart))
|
||||
{
|
||||
sourceStream.CopyTo(destStream);
|
||||
Logger.Verbose($"{splitPart} has been combined into {destFull}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,6 +48,7 @@ namespace AssetStudio
|
||||
|
||||
public static string[] ProcessingSplitFiles(List<string> selectFile)
|
||||
{
|
||||
Logger.Verbose("Filter out paths that has .split and has the same name");
|
||||
var splitFiles = selectFile.Where(x => x.Contains(".split"))
|
||||
.Select(x => Path.Combine(Path.GetDirectoryName(x), Path.GetFileNameWithoutExtension(x)))
|
||||
.Distinct()
|
||||
@@ -61,6 +66,7 @@ namespace AssetStudio
|
||||
|
||||
public static FileReader DecompressGZip(FileReader reader)
|
||||
{
|
||||
Logger.Verbose($"Decompressing GZip file {reader.FileName} into memory");
|
||||
using (reader)
|
||||
{
|
||||
var stream = new MemoryStream();
|
||||
@@ -75,6 +81,7 @@ namespace AssetStudio
|
||||
|
||||
public static FileReader DecompressBrotli(FileReader reader)
|
||||
{
|
||||
Logger.Verbose($"Decompressing Brotli file {reader.FileName} into memory");
|
||||
using (reader)
|
||||
{
|
||||
var stream = new MemoryStream();
|
||||
@@ -89,24 +96,31 @@ namespace AssetStudio
|
||||
|
||||
public static FileReader DecryptPack(FileReader reader, Game game)
|
||||
{
|
||||
Logger.Verbose($"Attempting to decrypt file {reader.FileName} with Pack encryption");
|
||||
|
||||
const int PackSize = 0x880;
|
||||
const string PackSignature = "pack";
|
||||
const string UnityFSSignature = "UnityFS";
|
||||
|
||||
var data = reader.ReadBytes((int)reader.Length);
|
||||
var idx = data.Search(PackSignature);
|
||||
if (idx == -1)
|
||||
var packIdx = data.Search(PackSignature);
|
||||
if (packIdx == -1)
|
||||
{
|
||||
Logger.Verbose($"Signature {PackSignature} was not found, aborting...");
|
||||
reader.Position = 0;
|
||||
return reader;
|
||||
}
|
||||
idx = data.Search("mr0k", idx);
|
||||
if (idx == -1)
|
||||
Logger.Verbose($"Found signature {PackSignature} at offset 0x{packIdx:X8}");
|
||||
var mr0kIdx = data.Search("mr0k", packIdx);
|
||||
if (mr0kIdx == -1)
|
||||
{
|
||||
Logger.Verbose("Signature mr0k was not found, aborting...");
|
||||
reader.Position = 0;
|
||||
return reader;
|
||||
}
|
||||
Logger.Verbose($"Found signature mr0k signature at offset 0x{mr0kIdx:X8}");
|
||||
|
||||
Logger.Verbose("Attempting to process pack chunks...");
|
||||
var ms = new MemoryStream();
|
||||
try
|
||||
{
|
||||
@@ -121,9 +135,12 @@ namespace AssetStudio
|
||||
var signature = reader.ReadStringToNull(4);
|
||||
if (signature == PackSignature)
|
||||
{
|
||||
Logger.Verbose($"Found {PackSignature} chunk at position {reader.Position - PackSignature.Length}");
|
||||
var isMr0k = reader.ReadBoolean();
|
||||
Logger.Verbose("Chunk is mr0k encrypted");
|
||||
var blockSize = BinaryPrimitives.ReadInt32LittleEndian(reader.ReadBytes(4));
|
||||
|
||||
Logger.Verbose($"Chunk size is 0x{blockSize:X8}");
|
||||
Span<byte> buffer = new byte[blockSize];
|
||||
reader.Read(buffer);
|
||||
if (isMr0k)
|
||||
@@ -134,6 +151,7 @@ namespace AssetStudio
|
||||
|
||||
if (bundleSize == 0)
|
||||
{
|
||||
Logger.Verbose("This is header chunk !! attempting to read the bundle size");
|
||||
using var blockReader = new EndianBinaryReader(new MemoryStream(buffer.ToArray()));
|
||||
var header = new Header()
|
||||
{
|
||||
@@ -144,17 +162,21 @@ namespace AssetStudio
|
||||
size = blockReader.ReadInt64()
|
||||
};
|
||||
bundleSize = header.size;
|
||||
Logger.Verbose($"Bundle size is 0x{bundleSize:X8}");
|
||||
}
|
||||
|
||||
readSize += buffer.Length;
|
||||
|
||||
if (readSize % (PackSize - 0x80) == 0)
|
||||
{
|
||||
reader.Position += PackSize - 9 - blockSize;
|
||||
var padding = PackSize - 9 - blockSize;
|
||||
reader.Position += padding;
|
||||
Logger.Verbose($"Skip 0x{padding:X8} padding");
|
||||
}
|
||||
|
||||
if (readSize == bundleSize)
|
||||
{
|
||||
Logger.Verbose($"Bundle has been read entirely !!");
|
||||
readSize = 0;
|
||||
bundleSize = 0;
|
||||
}
|
||||
@@ -166,6 +188,7 @@ namespace AssetStudio
|
||||
signature = reader.ReadStringToNull();
|
||||
if (signature == UnityFSSignature)
|
||||
{
|
||||
Logger.Verbose($"Found {UnityFSSignature} chunk at position {reader.Position - (UnityFSSignature.Length + 1)}");
|
||||
var header = new Header()
|
||||
{
|
||||
signature = reader.ReadStringToNull(),
|
||||
@@ -175,6 +198,7 @@ namespace AssetStudio
|
||||
size = reader.ReadInt64()
|
||||
};
|
||||
|
||||
Logger.Verbose($"Bundle size is 0x{header.size:X8}");
|
||||
reader.Position = pos;
|
||||
reader.BaseStream.CopyTo(ms, header.size);
|
||||
continue;
|
||||
@@ -196,15 +220,19 @@ namespace AssetStudio
|
||||
reader.Dispose();
|
||||
}
|
||||
|
||||
Logger.Verbose("Decrypted pack file successfully !!");
|
||||
ms.Position = 0;
|
||||
return new FileReader(reader.FullPath, ms);
|
||||
}
|
||||
|
||||
public static FileReader DecryptMark(FileReader reader)
|
||||
{
|
||||
Logger.Verbose($"Attempting to decrypt file {reader.FileName} with Mark encryption");
|
||||
|
||||
var signature = reader.ReadStringToNull(4);
|
||||
if (signature != "mark")
|
||||
{
|
||||
Logger.Verbose($"Expected signature mark, found {signature} instead, aborting...");
|
||||
reader.Position = 0;
|
||||
return reader;
|
||||
}
|
||||
@@ -240,6 +268,7 @@ namespace AssetStudio
|
||||
}
|
||||
}
|
||||
|
||||
Logger.Verbose("Decrypted mark file successfully !!");
|
||||
reader.Dispose();
|
||||
dataStream.Position = 0;
|
||||
return new FileReader(reader.FullPath, dataStream);
|
||||
@@ -247,8 +276,10 @@ namespace AssetStudio
|
||||
|
||||
public static FileReader DecryptEnsembleStar(FileReader reader)
|
||||
{
|
||||
Logger.Verbose($"Attempting to decrypt file {reader.FileName} with Ensemble Star encryption");
|
||||
if (Path.GetExtension(reader.FileName) != ".z")
|
||||
{
|
||||
Logger.Verbose($"Expected file extension .z, found {Path.GetExtension(reader.FileName)} instead, aborting...");
|
||||
return reader;
|
||||
}
|
||||
using (reader)
|
||||
@@ -269,46 +300,42 @@ namespace AssetStudio
|
||||
data[i] = (byte)(EnsembleStarKey1[k1] ^ ((size ^ EnsembleStarKey3[k3] ^ data[i] ^ EnsembleStarKey2[k2]) + remaining));
|
||||
}
|
||||
|
||||
Logger.Verbose("Decrypted Ensemble Star file successfully !!");
|
||||
return new FileReader(reader.FullPath, new MemoryStream(data));
|
||||
}
|
||||
}
|
||||
|
||||
public static FileReader ParseOPFP(FileReader reader)
|
||||
{
|
||||
var stream = reader.BaseStream;
|
||||
var data = reader.ReadBytes(0x1000);
|
||||
var idx = data.Search("UnityFS");
|
||||
if (idx != -1)
|
||||
{
|
||||
stream = new OffsetStream(stream, idx);
|
||||
}
|
||||
|
||||
return new FileReader(reader.FullPath, stream);
|
||||
}
|
||||
|
||||
public static FileReader ParseFakeHeader(FileReader reader)
|
||||
{
|
||||
Logger.Verbose($"Attempting to parse file {reader.FileName} with fake header");
|
||||
|
||||
var stream = reader.BaseStream;
|
||||
var data = reader.ReadBytes(0x1000);
|
||||
var idx = data.Search("UnityFS");
|
||||
if (idx != -1)
|
||||
{
|
||||
Logger.Verbose($"Found fake header at offset 0x{idx:X8}");
|
||||
var idx2 = data[(idx + 1)..].Search("UnityFS");
|
||||
if (idx2 != -1)
|
||||
{
|
||||
Logger.Verbose($"Found real header at offset 0x{idx + idx2 + 1:X8}");
|
||||
stream = new OffsetStream(stream, idx + idx2 + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Verbose("Real header was not found, assuming fake header is the real one");
|
||||
stream = new OffsetStream(stream, idx);
|
||||
}
|
||||
}
|
||||
|
||||
Logger.Verbose("Parsed fake header file successfully !!");
|
||||
return new FileReader(reader.FullPath, stream);
|
||||
}
|
||||
|
||||
public static FileReader DecryptFantasyOfWind(FileReader reader)
|
||||
{
|
||||
Logger.Verbose($"Attempting to decrypt file {reader.FileName} with Fantasy of Wind encryption");
|
||||
|
||||
byte[] encryptKeyName = Encoding.UTF8.GetBytes("28856");
|
||||
const int MinLength = 0xC8;
|
||||
const int KeyLength = 8;
|
||||
@@ -319,6 +346,7 @@ namespace AssetStudio
|
||||
var signature = reader.ReadStringToNull(HeadLength);
|
||||
if (string.Compare(signature, "K9999") > 0 || reader.Length <= MinLength)
|
||||
{
|
||||
Logger.Verbose($"Signature version {signature} is higher than K9999 or stream length {reader.Length} is less than minimum length {MinLength}, aborting...");
|
||||
reader.Position = 0;
|
||||
return reader;
|
||||
}
|
||||
@@ -360,23 +388,13 @@ namespace AssetStudio
|
||||
reader.BaseStream.CopyTo(ms);
|
||||
ms.Position = 0;
|
||||
|
||||
Logger.Verbose("Decrypted Fantasy of Wind file successfully !!");
|
||||
return new FileReader(reader.FullPath, ms);
|
||||
}
|
||||
|
||||
public static FileReader ParseShiningNikki(FileReader reader)
|
||||
{
|
||||
var data = reader.ReadBytes(0x1000);
|
||||
var idx = data.Search("UnityFS");
|
||||
if (idx == -1)
|
||||
{
|
||||
reader.Position = 0;
|
||||
return reader;
|
||||
}
|
||||
var stream = new OffsetStream(reader.BaseStream, idx);
|
||||
return new FileReader(reader.FullPath, stream);
|
||||
}
|
||||
public static FileReader ParseHelixWaltz2(FileReader reader)
|
||||
{
|
||||
Logger.Verbose($"Attempting to decrypt file {reader.FileName} with Helix Waltz 2 encryption");
|
||||
|
||||
var originalHeader = new byte[] { 0x55, 0x6E, 0x69, 0x74, 0x79, 0x46, 0x53, 0x00, 0x00, 0x00, 0x00, 0x07, 0x35, 0x2E, 0x78, 0x2E };
|
||||
|
||||
var signature = reader.ReadStringToNull();
|
||||
@@ -384,6 +402,7 @@ namespace AssetStudio
|
||||
|
||||
if (signature != "SzxFS")
|
||||
{
|
||||
Logger.Verbose($"Expected signature SzxFS, found {signature} instead, aborting...");
|
||||
reader.Position = 0;
|
||||
return reader;
|
||||
}
|
||||
@@ -415,6 +434,7 @@ namespace AssetStudio
|
||||
data[i] = key[idx];
|
||||
}
|
||||
|
||||
Logger.Verbose("Decrypted Helix Waltz 2 file successfully !!");
|
||||
MemoryStream ms = new();
|
||||
ms.Write(originalHeader);
|
||||
ms.Write(data);
|
||||
@@ -424,6 +444,8 @@ namespace AssetStudio
|
||||
}
|
||||
public static FileReader DecryptAnchorPanic(FileReader reader)
|
||||
{
|
||||
Logger.Verbose($"Attempting to decrypt file {reader.FileName} with Anchor Panic encryption");
|
||||
|
||||
const int BlockSize = 0x800;
|
||||
|
||||
var data = reader.ReadBytes(0x1000);
|
||||
@@ -432,25 +454,31 @@ namespace AssetStudio
|
||||
var idx = data.Search("UnityFS");
|
||||
if (idx != -1)
|
||||
{
|
||||
Logger.Verbose("Found UnityFS signature, file might not be encrypted");
|
||||
return ParseFakeHeader(reader);
|
||||
}
|
||||
|
||||
var key = GetKey(Path.GetFileNameWithoutExtension(reader.FileName));
|
||||
Logger.Verbose($"Calculated key is {key}");
|
||||
|
||||
var chunkIndex = 0;
|
||||
MemoryStream ms = new();
|
||||
while (reader.Remaining > 0)
|
||||
{
|
||||
var chunkSize = Math.Min((int)reader.Remaining, BlockSize);
|
||||
Logger.Verbose($"Chunk {chunkIndex} size is {chunkSize}");
|
||||
var chunk = reader.ReadBytes(chunkSize);
|
||||
if (IsEncrypt((int)reader.Length, chunkIndex++))
|
||||
{
|
||||
Logger.Verbose($"Chunk {chunkIndex} is encrypted, decrypting...");
|
||||
RC4(chunk, key);
|
||||
}
|
||||
|
||||
ms.Write(chunk);
|
||||
}
|
||||
|
||||
Logger.Verbose("Decrypted Anchor Panic file successfully !!");
|
||||
ms.Position = 0;
|
||||
|
||||
return new FileReader(reader.FullPath, ms);
|
||||
|
||||
bool IsEncrypt(int fileSize, int chunkIndex)
|
||||
@@ -533,11 +561,14 @@ namespace AssetStudio
|
||||
|
||||
public static FileReader DecryptDreamscapeAlbireo(FileReader reader)
|
||||
{
|
||||
Logger.Verbose($"Attempting to decrypt file {reader.FileName} with Dreamscape Albireo encryption");
|
||||
|
||||
var key = new byte[] { 0x1E, 0x1E, 0x01, 0x01, 0xFC };
|
||||
|
||||
var signature = reader.ReadStringToNull(4);
|
||||
if (signature != "MJJ")
|
||||
{
|
||||
Logger.Verbose($"Expected signature MJJ, found {signature} instead, aborting...");
|
||||
reader.Position = 0;
|
||||
return reader;
|
||||
}
|
||||
@@ -559,6 +590,8 @@ namespace AssetStudio
|
||||
var sizeLow = (u5 >> 24 | (u2 << 8)) ^ u1;
|
||||
var size = (long)(sizeHigh << 32 | sizeLow);
|
||||
|
||||
Logger.Verbose($"Decrypted File info: Flag 0x{flag:X8} | Compressed blockInfo size 0x{compressedBlocksInfoSize:X8} | Decompressed blockInfo size 0x{uncompressedBlocksInfoSize:X8} | Bundle size 0x{size:X8}");
|
||||
|
||||
var blocksInfo = reader.ReadBytes((int)compressedBlocksInfoSize);
|
||||
for(int i = 0; i < blocksInfo.Length; i++)
|
||||
{
|
||||
@@ -587,6 +620,7 @@ namespace AssetStudio
|
||||
reader.BaseStream.CopyTo(ms);
|
||||
ms.Position = 0;
|
||||
|
||||
Logger.Verbose("Decrypted Dreamscape Albireo file successfully !!");
|
||||
return new FileReader(reader.FullPath, ms);
|
||||
|
||||
static uint Scrample(uint value) => (value >> 5) & 0xFFE000 | (value >> 29) | (value << 14) & 0xFF000000 | (8 * value) & 0x1FF8;
|
||||
@@ -594,6 +628,8 @@ namespace AssetStudio
|
||||
|
||||
public static FileReader DecryptImaginaryFest(FileReader reader)
|
||||
{
|
||||
Logger.Verbose($"Attempting to decrypt file {reader.FileName} with Imaginary Fest encryption");
|
||||
|
||||
const string dataRoot = "data";
|
||||
var key = new byte[] { 0xBD, 0x65, 0xF2, 0x4F, 0xBE, 0xD1, 0x36, 0xD4, 0x95, 0xFE, 0x64, 0x94, 0xCB, 0xD3, 0x7E, 0x91, 0x57, 0xB7, 0x94, 0xB7, 0x9F, 0x70, 0xB2, 0xA9, 0xA0, 0xD5, 0x4E, 0x36, 0xC6, 0x40, 0xE0, 0x2F, 0x4E, 0x6E, 0x76, 0x6D, 0xCD, 0xAE, 0xEA, 0x05, 0x13, 0x6B, 0xA7, 0x84, 0xFF, 0xED, 0x90, 0x91, 0x15, 0x7E, 0xF1, 0xF8, 0xA5, 0x9C, 0xB6, 0xDE, 0xF9, 0x56, 0x57, 0x18, 0xBF, 0x94, 0x63, 0x6F, 0x1B, 0xE2, 0x92, 0xD2, 0x7E, 0x25, 0x95, 0x23, 0x24, 0xCB, 0x93, 0xD3, 0x36, 0xD9, 0x18, 0x11, 0xF5, 0x50, 0x18, 0xE4, 0x22, 0x28, 0xD8, 0xE2, 0x1A, 0x57, 0x1E, 0x04, 0x88, 0xA5, 0x84, 0xC0, 0x6C, 0x3B, 0x46, 0x62, 0xCE, 0x85, 0x10, 0x2E, 0xA0, 0xDC, 0xD3, 0x09, 0xB2, 0xB6, 0xA4, 0x8D, 0xAF, 0x74, 0x36, 0xF7, 0x9A, 0x3F, 0x98, 0xDA, 0x62, 0x57, 0x71, 0x75, 0x92, 0x05, 0xA3, 0xB2, 0x7C, 0xCA, 0xFB, 0x1E, 0xBE, 0xC9, 0x24, 0xC1, 0xD2, 0xB9, 0xDE, 0xE4, 0x7E, 0xF3, 0x0F, 0xB4, 0xFB, 0xA2, 0xC1, 0xC2, 0x14, 0x5C, 0x78, 0x13, 0x74, 0x41, 0x8D, 0x79, 0xF4, 0x3C, 0x49, 0x92, 0x98, 0xF2, 0xCD, 0x8C, 0x09, 0xA6, 0x40, 0x34, 0x51, 0x1C, 0x11, 0x2B, 0xE0, 0x6B, 0x42, 0x9C, 0x86, 0x41, 0x06, 0xF6, 0xD2, 0x87, 0xF1, 0x10, 0x26, 0x89, 0xC2, 0x7B, 0x2A, 0x5D, 0x1C, 0xDA, 0x92, 0xC8, 0x93, 0x59, 0xF9, 0x60, 0xD0, 0xB5, 0x1E, 0xD5, 0x75, 0x56, 0xA0, 0x05, 0x83, 0x90, 0xAC, 0x72, 0xC8, 0x10, 0x09, 0xED, 0x1A, 0x46, 0xD9, 0x39, 0x6B, 0x9E, 0x19, 0x5E, 0x51, 0x44, 0x09, 0x0D, 0x74, 0xAB, 0xA8, 0xF9, 0x32, 0x43, 0xBC, 0xD2, 0xED, 0x7B, 0x6C, 0x75, 0x32, 0x24, 0x14, 0x43, 0x5D, 0x98, 0xB2, 0xFC, 0xFB, 0xF5, 0x9A, 0x19, 0x03, 0xB0, 0xB7, 0xAC, 0xAE, 0x8B };
|
||||
|
||||
@@ -601,12 +637,14 @@ namespace AssetStudio
|
||||
var signature = Encoding.UTF8.GetString(signatureBytes[..7]);
|
||||
if (signature == "UnityFS")
|
||||
{
|
||||
Logger.Verbose("Found UnityFS signature, file might not be encrypted");
|
||||
reader.Position = 0;
|
||||
return reader;
|
||||
}
|
||||
|
||||
if (signatureBytes[7] != 0)
|
||||
{
|
||||
Logger.Verbose($"File might be encrypted with a byte xorkey 0x{signatureBytes[7]:X8}, attemping to decrypting...");
|
||||
var xorKey = signatureBytes[7];
|
||||
for (int i = 0; i < signatureBytes.Length; i++)
|
||||
{
|
||||
@@ -615,12 +653,14 @@ namespace AssetStudio
|
||||
signature = Encoding.UTF8.GetString(signatureBytes[..7]);
|
||||
if (signature == "UnityFS")
|
||||
{
|
||||
Logger.Verbose("Found UnityFS signature, key is valid, decrypting the rest of the stream");
|
||||
var remaining = reader.ReadBytes((int)reader.Remaining);
|
||||
for (int i = 0; i < remaining.Length; i++)
|
||||
{
|
||||
remaining[i] ^= xorKey;
|
||||
}
|
||||
|
||||
Logger.Verbose("Decrypted Imaginary Fest file successfully !!");
|
||||
var stream = new MemoryStream();
|
||||
stream.Write(signatureBytes);
|
||||
stream.Write(remaining);
|
||||
@@ -635,25 +675,33 @@ namespace AssetStudio
|
||||
var startIdx = Array.FindIndex(paths, x => x == dataRoot);
|
||||
if (startIdx != -1 && startIdx != paths.Length - 1)
|
||||
{
|
||||
Logger.Verbose("File is in the data folder !!");
|
||||
var path = string.Join(Path.AltDirectorySeparatorChar, paths[(startIdx+1)..]);
|
||||
var offset = GetLoadAssetBundleOffset(path);
|
||||
if (offset > 0 && offset < reader.Length)
|
||||
{
|
||||
Logger.Verbose($"Calculated offset is 0x{offset:X8}, attempting to read signature...");
|
||||
reader.Position = offset;
|
||||
signature = reader.ReadStringToNull(7);
|
||||
if (signature == "UnityFS")
|
||||
{
|
||||
Logger.Verbose($"Found UnityFS signature, file starts at 0x{offset:X8} !!");
|
||||
Logger.Verbose("Parsed Imaginary Fest file successfully !!");
|
||||
reader.Position = offset;
|
||||
return new FileReader(reader.FullPath, new MemoryStream(reader.ReadBytes((int)reader.Remaining)));
|
||||
}
|
||||
}
|
||||
Logger.Verbose($"Invalid offset, attempting to generate key...");
|
||||
reader.Position = 0;
|
||||
var data = reader.ReadBytes((int)reader.Remaining);
|
||||
var key_value = GetHashCode(path);
|
||||
Logger.Verbose($"Generated key is 0x{key_value:X8}, decrypting...");
|
||||
Decrypt(data, key_value);
|
||||
Logger.Verbose("Decrypted Imaginary Fest file successfully !!");
|
||||
return new FileReader(reader.FullPath, new MemoryStream(data));
|
||||
}
|
||||
|
||||
Logger.Verbose("File doesn't match any of the encryption types");
|
||||
reader.Position = 0;
|
||||
return reader;
|
||||
|
||||
|
||||
@@ -9,5 +9,13 @@ namespace AssetStudio
|
||||
{
|
||||
public int localSerializedFileIndex;
|
||||
public long localIdentifierInFile;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append($"localSerializedFileIndex: {localSerializedFileIndex} | ");
|
||||
sb.Append($"localIdentifierInFile: {localIdentifierInFile}");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
@@ -7,21 +8,79 @@ namespace AssetStudio
|
||||
{
|
||||
public static class Logger
|
||||
{
|
||||
public static ILogger Default = new DummyLogger();
|
||||
public static bool Silent = false;
|
||||
private static bool _fileLogging = true;
|
||||
|
||||
public static void Verbose(string message) => Default.Log(LoggerEvent.Verbose, message, Silent);
|
||||
public static void Debug(string message) => Default.Log(LoggerEvent.Debug, message, Silent);
|
||||
public static void Info(string message) => Default.Log(LoggerEvent.Info, message, Silent);
|
||||
public static void Warning(string message) => Default.Log(LoggerEvent.Warning, message, Silent);
|
||||
public static void Error(string message) => Default.Log(LoggerEvent.Error, message, Silent);
|
||||
public static ILogger Default = new DummyLogger();
|
||||
public static ILogger File;
|
||||
public static bool Silent = false;
|
||||
public static bool LogVerbose = false;
|
||||
|
||||
public static bool FileLogging
|
||||
{
|
||||
get => _fileLogging;
|
||||
set
|
||||
{
|
||||
_fileLogging = value;
|
||||
if (_fileLogging)
|
||||
{
|
||||
File = new FileLogger();
|
||||
}
|
||||
else
|
||||
{
|
||||
((FileLogger)File)?.Writer?.Dispose();
|
||||
File = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Verbose(string message)
|
||||
{
|
||||
if (LogVerbose)
|
||||
{
|
||||
try
|
||||
{
|
||||
var callerMethod = new StackTrace().GetFrame(1).GetMethod();
|
||||
var callerMethodClass = callerMethod.ReflectedType.Name;
|
||||
if (!string.IsNullOrEmpty(callerMethodClass))
|
||||
{
|
||||
message = $"[{callerMethodClass}] {message}";
|
||||
}
|
||||
}
|
||||
catch (Exception) { }
|
||||
if (FileLogging) File.Log(LoggerEvent.Verbose, message);
|
||||
Default.Log(LoggerEvent.Verbose, message, Silent);
|
||||
}
|
||||
}
|
||||
public static void Debug(string message)
|
||||
{
|
||||
if (FileLogging) File.Log(LoggerEvent.Debug, message);
|
||||
Default.Log(LoggerEvent.Debug, message, Silent);
|
||||
}
|
||||
public static void Info(string message)
|
||||
{
|
||||
if (FileLogging) File.Log(LoggerEvent.Info, message);
|
||||
Default.Log(LoggerEvent.Info, message, Silent);
|
||||
}
|
||||
public static void Warning(string message)
|
||||
{
|
||||
if (FileLogging) File.Log(LoggerEvent.Warning, message);
|
||||
Default.Log(LoggerEvent.Warning, message, Silent);
|
||||
}
|
||||
public static void Error(string message)
|
||||
{
|
||||
if (FileLogging) File.Log(LoggerEvent.Error, message);
|
||||
Default.Log(LoggerEvent.Error, message, Silent);
|
||||
}
|
||||
|
||||
public static void Error(string message, Exception e)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine(message);
|
||||
sb.AppendLine(e.ToString());
|
||||
Default.Log(LoggerEvent.Error, sb.ToString(), Silent);
|
||||
|
||||
message = sb.ToString();
|
||||
if (FileLogging) File.Log(LoggerEvent.Error, message);
|
||||
Default.Log(LoggerEvent.Error, message, Silent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ namespace AssetStudio
|
||||
return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z) && W.Equals(other.W);
|
||||
}
|
||||
|
||||
public static Quaternion Zero => new Quaternion(0, 0, 0, 1);
|
||||
public static float Dot(Quaternion a, Quaternion b)
|
||||
{
|
||||
return a.X * b.X + a.Y * b.Y + a.Z * b.Z + a.W * b.W;
|
||||
|
||||
@@ -15,6 +15,8 @@ namespace AssetStudio
|
||||
public long Offset;
|
||||
public Mhy0 mhy0;
|
||||
|
||||
public long TotalSize => 8 + m_Header.compressedBlocksInfoSize + m_BlocksInfo.Sum(x => x.compressedSize);
|
||||
|
||||
public Mhy0File(FileReader reader, string path, Mhy0 mhy0)
|
||||
{
|
||||
this.mhy0 = mhy0;
|
||||
@@ -22,6 +24,10 @@ namespace AssetStudio
|
||||
reader.Endian = EndianType.LittleEndian;
|
||||
|
||||
var signature = reader.ReadStringToNull(4);
|
||||
Logger.Verbose($"Parsed signature {signature}");
|
||||
if (signature != "mhy0")
|
||||
throw new Exception("not a mhy0");
|
||||
|
||||
m_Header = new BundleFile.Header
|
||||
{
|
||||
version = 6,
|
||||
@@ -30,6 +36,7 @@ namespace AssetStudio
|
||||
compressedBlocksInfoSize = reader.ReadUInt32(),
|
||||
flags = (ArchiveFlags)0x43
|
||||
};
|
||||
Logger.Verbose($"Header: {m_Header}");
|
||||
ReadBlocksInfoAndDirectory(reader);
|
||||
using var blocksStream = CreateBlocksStream(path);
|
||||
ReadBlocks(reader, blocksStream);
|
||||
@@ -41,9 +48,11 @@ namespace AssetStudio
|
||||
var blocksInfo = reader.ReadBytes((int)m_Header.compressedBlocksInfoSize);
|
||||
DescrambleHeader(blocksInfo);
|
||||
|
||||
Logger.Verbose($"Descrambled blocksInfo signature {Convert.ToHexString(blocksInfo, 0 , 4)}");
|
||||
using var blocksInfoStream = new MemoryStream(blocksInfo, 0x20, (int)m_Header.compressedBlocksInfoSize - 0x20);
|
||||
using var blocksInfoReader = new EndianBinaryReader(blocksInfoStream);
|
||||
m_Header.uncompressedBlocksInfoSize = blocksInfoReader.ReadMhy0UInt();
|
||||
Logger.Verbose($"uncompressed blocksInfo size: 0x{m_Header.uncompressedBlocksInfoSize:X8}");
|
||||
var compressedBlocksInfo = blocksInfoReader.ReadBytes((int)blocksInfoReader.Remaining);
|
||||
var uncompressedBlocksInfo = new byte[(int)m_Header.uncompressedBlocksInfoSize];
|
||||
var numWrite = LZ4Codec.Decode(compressedBlocksInfo, uncompressedBlocksInfo);
|
||||
@@ -52,10 +61,12 @@ namespace AssetStudio
|
||||
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {m_Header.uncompressedBlocksInfoSize} bytes");
|
||||
}
|
||||
|
||||
Logger.Verbose($"Writing block and directory to blocks stream...");
|
||||
using var blocksInfoUncompressedStream = new MemoryStream(uncompressedBlocksInfo);
|
||||
using var blocksInfoUncompressedReader = new EndianBinaryReader(blocksInfoUncompressedStream);
|
||||
var nodesCount = blocksInfoUncompressedReader.ReadMhy0Int();
|
||||
m_DirectoryInfo = new BundleFile.Node[nodesCount];
|
||||
Logger.Verbose($"Directory count: {nodesCount}");
|
||||
for (int i = 0; i < nodesCount; i++)
|
||||
{
|
||||
m_DirectoryInfo[i] = new BundleFile.Node
|
||||
@@ -65,10 +76,13 @@ namespace AssetStudio
|
||||
offset = blocksInfoUncompressedReader.ReadMhy0Int(),
|
||||
size = blocksInfoUncompressedReader.ReadMhy0UInt()
|
||||
};
|
||||
|
||||
Logger.Verbose($"Directory {i} Info: {m_DirectoryInfo[i]}");
|
||||
}
|
||||
|
||||
var blocksInfoCount = blocksInfoUncompressedReader.ReadMhy0Int();
|
||||
m_BlocksInfo = new BundleFile.StorageBlock[blocksInfoCount];
|
||||
Logger.Verbose($"Blocks count: {blocksInfoCount}");
|
||||
for (int i = 0; i < blocksInfoCount; i++)
|
||||
{
|
||||
m_BlocksInfo[i] = new BundleFile.StorageBlock
|
||||
@@ -77,6 +91,8 @@ namespace AssetStudio
|
||||
uncompressedSize = blocksInfoUncompressedReader.ReadMhy0UInt(),
|
||||
flags = (StorageBlockFlags)0x43
|
||||
};
|
||||
|
||||
Logger.Verbose($"Block {i} Info: {m_BlocksInfo[i]}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +100,7 @@ namespace AssetStudio
|
||||
{
|
||||
Stream blocksStream;
|
||||
var uncompressedSizeSum = (int)m_BlocksInfo.Sum(x => x.uncompressedSize);
|
||||
Logger.Verbose($"Total size of decompressed blocks: 0x{uncompressedSizeSum:X8}");
|
||||
if (uncompressedSizeSum >= int.MaxValue)
|
||||
blocksStream = new FileStream(path + ".temp", FileMode.Create, FileAccess.ReadWrite, FileShare.None, 4096, FileOptions.DeleteOnClose);
|
||||
else
|
||||
@@ -110,6 +127,7 @@ namespace AssetStudio
|
||||
var uncompressedBytesSpan = uncompressedBytes.AsSpan(0, uncompressedSize);
|
||||
DescrambleEntry(compressedBytesSpan);
|
||||
|
||||
Logger.Verbose($"Descrambled block signature {Convert.ToHexString(compressedBytes, 0, 4)}");
|
||||
var numWrite = LZ4Codec.Decode(compressedBytesSpan[0xC..compressedSize], uncompressedBytesSpan);
|
||||
if (numWrite != uncompressedSize)
|
||||
{
|
||||
@@ -124,6 +142,8 @@ namespace AssetStudio
|
||||
|
||||
private void ReadFiles(Stream blocksStream, string path)
|
||||
{
|
||||
Logger.Verbose($"Writing files from blocks stream...");
|
||||
|
||||
fileList = new StreamFile[m_DirectoryInfo.Length];
|
||||
for (int i = 0; i < m_DirectoryInfo.Length; i++)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace AssetStudio
|
||||
using System.Text;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
public class ObjectInfo
|
||||
{
|
||||
@@ -11,5 +13,18 @@
|
||||
|
||||
public long m_PathID;
|
||||
public SerializedType serializedType;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append($"byteStart: 0x{byteStart:X8} | ");
|
||||
sb.Append($"byteSize: 0x{byteSize:X8} | ");
|
||||
sb.Append($"typeID: {typeID} | ");
|
||||
sb.Append($"classID: {classID} | ");
|
||||
sb.Append($"isDestroyed: {isDestroyed} | ");
|
||||
sb.Append($"stripped: {stripped} | ");
|
||||
sb.Append($"PathID: {m_PathID}");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,10 +39,13 @@ namespace AssetStudio
|
||||
serializedType = objectInfo.serializedType;
|
||||
platform = assetsFile.m_TargetPlatform;
|
||||
m_Version = assetsFile.header.m_Version;
|
||||
|
||||
Logger.Verbose($"Initialized reader for {type} object with {m_PathID} in file {assetsFile.fileName} !!");
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Logger.Verbose($"Resetting reader position to object offset 0x{byteStart:X8}...");
|
||||
Position = byteStart;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,14 +60,7 @@ namespace AssetStudio
|
||||
_baseStream.Seek(target, SeekOrigin.Begin);
|
||||
return Position;
|
||||
}
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (offset > _baseStream.Length || Position + count > _baseStream.Length)
|
||||
{
|
||||
throw new IOException("Unable to read beyond stream bound");
|
||||
}
|
||||
return _baseStream.Read(buffer, offset, count);
|
||||
}
|
||||
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();
|
||||
|
||||
@@ -67,16 +67,21 @@ namespace AssetStudio
|
||||
header.m_FileSize = reader.ReadInt64();
|
||||
header.m_DataOffset = reader.ReadInt64();
|
||||
reader.ReadInt64(); // unknown
|
||||
|
||||
}
|
||||
|
||||
Logger.Verbose($"File {fileName} Info: {header}");
|
||||
|
||||
// ReadMetadata
|
||||
if (m_FileEndianess == 0)
|
||||
{
|
||||
reader.Endian = EndianType.LittleEndian;
|
||||
Logger.Verbose($"Endianness {reader.Endian}");
|
||||
}
|
||||
if (header.m_Version >= SerializedFileFormatVersion.Unknown_7)
|
||||
{
|
||||
unityVersion = reader.ReadStringToNull();
|
||||
Logger.Verbose($"Unity version {unityVersion}");
|
||||
SetVersion(unityVersion);
|
||||
}
|
||||
if (header.m_Version >= SerializedFileFormatVersion.Unknown_8)
|
||||
@@ -84,12 +89,15 @@ namespace AssetStudio
|
||||
m_TargetPlatform = (BuildTarget)reader.ReadInt32();
|
||||
if (!Enum.IsDefined(typeof(BuildTarget), m_TargetPlatform))
|
||||
{
|
||||
Logger.Verbose($"Parsed target format {m_TargetPlatform} doesn't match any of supported formats, defaulting to {BuildTarget.UnknownPlatform}");
|
||||
m_TargetPlatform = BuildTarget.UnknownPlatform;
|
||||
}
|
||||
else if (game.Type.IsMhyGroup())
|
||||
{
|
||||
Logger.Verbose($"Selected game {game.Name} is a mhy game, forcing target format {BuildTarget.StandaloneWindows64}");
|
||||
m_TargetPlatform = BuildTarget.StandaloneWindows64;
|
||||
}
|
||||
Logger.Verbose($"Target format {m_TargetPlatform}");
|
||||
}
|
||||
if (header.m_Version >= SerializedFileFormatVersion.HasTypeTreeHashes)
|
||||
{
|
||||
@@ -99,6 +107,7 @@ namespace AssetStudio
|
||||
// Read Types
|
||||
int typeCount = reader.ReadInt32();
|
||||
m_Types = new List<SerializedType>(typeCount);
|
||||
Logger.Verbose($"Found {typeCount} serialized types");
|
||||
for (int i = 0; i < typeCount; i++)
|
||||
{
|
||||
m_Types.Add(ReadSerializedType(false));
|
||||
@@ -114,6 +123,7 @@ namespace AssetStudio
|
||||
m_Objects = new List<ObjectInfo>(objectCount);
|
||||
Objects = new List<Object>(objectCount);
|
||||
ObjectsDic = new Dictionary<long, Object>(objectCount);
|
||||
Logger.Verbose($"Found {objectCount} objects");
|
||||
for (int i = 0; i < objectCount; i++)
|
||||
{
|
||||
var objectInfo = new ObjectInfo();
|
||||
@@ -164,12 +174,14 @@ namespace AssetStudio
|
||||
{
|
||||
objectInfo.stripped = reader.ReadByte();
|
||||
}
|
||||
Logger.Verbose($"Object Info: {objectInfo}");
|
||||
m_Objects.Add(objectInfo);
|
||||
}
|
||||
|
||||
if (header.m_Version >= SerializedFileFormatVersion.HasScriptTypeIndex)
|
||||
{
|
||||
int scriptCount = reader.ReadInt32();
|
||||
Logger.Verbose($"Found {scriptCount} scripts");
|
||||
m_ScriptTypes = new List<LocalSerializedObjectIdentifier>(scriptCount);
|
||||
for (int i = 0; i < scriptCount; i++)
|
||||
{
|
||||
@@ -184,12 +196,14 @@ namespace AssetStudio
|
||||
reader.AlignStream();
|
||||
m_ScriptType.localIdentifierInFile = reader.ReadInt64();
|
||||
}
|
||||
Logger.Verbose($"Script Info: {m_ScriptType}");
|
||||
m_ScriptTypes.Add(m_ScriptType);
|
||||
}
|
||||
}
|
||||
|
||||
int externalsCount = reader.ReadInt32();
|
||||
m_Externals = new List<FileIdentifier>(externalsCount);
|
||||
Logger.Verbose($"Found {externalsCount} externals");
|
||||
for (int i = 0; i < externalsCount; i++)
|
||||
{
|
||||
var m_External = new FileIdentifier();
|
||||
@@ -204,6 +218,7 @@ namespace AssetStudio
|
||||
}
|
||||
m_External.pathName = reader.ReadStringToNull();
|
||||
m_External.fileName = Path.GetFileName(m_External.pathName);
|
||||
Logger.Verbose($"External Info: {m_External}");
|
||||
m_Externals.Add(m_External);
|
||||
}
|
||||
|
||||
@@ -211,6 +226,7 @@ namespace AssetStudio
|
||||
{
|
||||
int refTypesCount = reader.ReadInt32();
|
||||
m_RefTypes = new List<SerializedType>(refTypesCount);
|
||||
Logger.Verbose($"Found {refTypesCount} reference types");
|
||||
for (int i = 0; i < refTypesCount; i++)
|
||||
{
|
||||
m_RefTypes.Add(ReadSerializedType(true));
|
||||
@@ -239,12 +255,14 @@ namespace AssetStudio
|
||||
|
||||
private SerializedType ReadSerializedType(bool isRefType)
|
||||
{
|
||||
Logger.Verbose($"Attempting to parse serialized" + (isRefType ? " reference" : " ") + "type");
|
||||
var type = new SerializedType();
|
||||
|
||||
type.classID = reader.ReadInt32();
|
||||
|
||||
if (game.Type.IsGIGroup() && BitConverter.ToBoolean(header.m_Reserved))
|
||||
{
|
||||
Logger.Verbose($"Encoded class ID {type.classID}, decoding...");
|
||||
type.classID = DecodeClassID(type.classID);
|
||||
}
|
||||
|
||||
@@ -273,6 +291,7 @@ namespace AssetStudio
|
||||
|
||||
if (m_EnableTypeTree)
|
||||
{
|
||||
Logger.Verbose($"File has type tree enabled !!");
|
||||
type.m_Type = new TypeTree();
|
||||
type.m_Type.m_Nodes = new List<TypeTreeNode>();
|
||||
if (header.m_Version >= SerializedFileFormatVersion.Unknown_12 || header.m_Version == SerializedFileFormatVersion.Unknown_10)
|
||||
@@ -298,11 +317,13 @@ namespace AssetStudio
|
||||
}
|
||||
}
|
||||
|
||||
Logger.Verbose($"Serialized type info: {type}");
|
||||
return type;
|
||||
}
|
||||
|
||||
private void ReadTypeTree(TypeTree m_Type, int level = 0)
|
||||
{
|
||||
Logger.Verbose($"Attempting to parse type tree...");
|
||||
var typeTreeNode = new TypeTreeNode();
|
||||
m_Type.m_Nodes.Add(typeTreeNode);
|
||||
typeTreeNode.m_Level = level;
|
||||
@@ -329,12 +350,16 @@ namespace AssetStudio
|
||||
{
|
||||
ReadTypeTree(m_Type, level + 1);
|
||||
}
|
||||
|
||||
Logger.Verbose($"Type Tree Info: {m_Type}");
|
||||
}
|
||||
|
||||
private void TypeTreeBlobRead(TypeTree m_Type)
|
||||
{
|
||||
Logger.Verbose($"Attempting to parse blob type tree...");
|
||||
int numberOfNodes = reader.ReadInt32();
|
||||
int stringBufferSize = reader.ReadInt32();
|
||||
Logger.Verbose($"Found {numberOfNodes} nodes and {stringBufferSize} strings");
|
||||
for (int i = 0; i < numberOfNodes; i++)
|
||||
{
|
||||
var typeTreeNode = new TypeTreeNode();
|
||||
@@ -364,6 +389,8 @@ namespace AssetStudio
|
||||
}
|
||||
}
|
||||
|
||||
Logger.Verbose($"Type Tree Info: {m_Type}");
|
||||
|
||||
string ReadString(EndianBinaryReader stringBufferReader, uint value)
|
||||
{
|
||||
var isOffset = (value & 0x80000000) == 0;
|
||||
@@ -383,6 +410,7 @@ namespace AssetStudio
|
||||
|
||||
public void AddObject(Object obj)
|
||||
{
|
||||
Logger.Verbose($"Caching object with {obj.m_PathID} in file {fileName}...");
|
||||
Objects.Add(obj);
|
||||
ObjectsDic.Add(obj.m_PathID, obj);
|
||||
}
|
||||
|
||||
@@ -13,5 +13,16 @@ namespace AssetStudio
|
||||
public long m_DataOffset;
|
||||
public byte m_Endianess;
|
||||
public byte[] m_Reserved;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append($"MetadataSize: 0x{m_MetadataSize:X8} | ");
|
||||
sb.Append($"FileSize: 0x{m_FileSize:X8} | ");
|
||||
sb.Append($"Version: {m_Version} | ");
|
||||
sb.Append($"DataOffset: 0x{m_DataOffset:X8} | ");
|
||||
sb.Append($"Endianness: {(EndianType)m_Endianess}");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,5 +19,16 @@ namespace AssetStudio
|
||||
public string m_AsmName;
|
||||
|
||||
public bool Match(string hash) => Convert.ToHexString(m_OldTypeHash) == hash;
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append($"classID: {classID} | ");
|
||||
sb.Append($"IsStrippedType: {m_IsStrippedType} | ");
|
||||
sb.Append($"ScriptTypeIndex: {m_ScriptTypeIndex} | ");
|
||||
sb.Append($"KlassName: {m_KlassName} | ");
|
||||
sb.Append($"NameSpace: {m_NameSpace} | ");
|
||||
sb.Append($"AsmName: {m_AsmName}");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,6 +186,7 @@ namespace AssetStudio
|
||||
{
|
||||
var m_Node = m_Nodes[i];
|
||||
var varTypeStr = m_Node.m_Type;
|
||||
Logger.Verbose($"Reading {m_Node.m_Name} of type {varTypeStr}");
|
||||
object value;
|
||||
var align = (m_Node.m_MetaFlag & 0x4000) != 0;
|
||||
switch (varTypeStr)
|
||||
|
||||
@@ -28,5 +28,19 @@ namespace AssetStudio
|
||||
m_Level = level;
|
||||
m_MetaFlag = align ? 0x4000 : 0;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append($"Type: {m_Type} | ");
|
||||
sb.Append($"Name: {m_Name} | ");
|
||||
sb.Append($"ByteSize: 0x{m_ByteSize:X8} | ");
|
||||
sb.Append($"Index: {m_Index} | ");
|
||||
sb.Append($"TypeFlags: {m_TypeFlags} | ");
|
||||
sb.Append($"Version: {m_Version} | ");
|
||||
sb.Append($"TypeStrOffset: 0x{m_TypeStrOffset:X8} | ");
|
||||
sb.Append($"NameStrOffset: 0x{m_NameStrOffset:X8}");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,15 @@ namespace AssetStudio
|
||||
public int dataOffset;
|
||||
public int dataLength;
|
||||
public string path;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append($"dataOffset: 0x{dataOffset:X8} | ");
|
||||
sb.Append($"dataOffset: 0x{dataLength:X8} | ");
|
||||
sb.Append($"path: {path}");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public WebFile(EndianBinaryReader reader)
|
||||
@@ -21,15 +30,19 @@ namespace AssetStudio
|
||||
var signature = reader.ReadStringToNull();
|
||||
var headLength = reader.ReadInt32();
|
||||
var dataList = new List<WebData>();
|
||||
Logger.Verbose($"Header size: 0x{headLength:X8}");
|
||||
while (reader.BaseStream.Position < headLength)
|
||||
{
|
||||
var data = new WebData();
|
||||
data.dataOffset = reader.ReadInt32();
|
||||
data.dataLength = reader.ReadInt32();
|
||||
var pathLength = reader.ReadInt32();
|
||||
Logger.Verbose($"Path length: {pathLength}");
|
||||
data.path = Encoding.UTF8.GetString(reader.ReadBytes(pathLength));
|
||||
Logger.Verbose($"Web data Info: {data}");
|
||||
dataList.Add(data);
|
||||
}
|
||||
Logger.Verbose("Writing files to streams...");
|
||||
fileList = new StreamFile[dataList.Count];
|
||||
for (int i = 0; i < dataList.Count; i++)
|
||||
{
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net6.0-windows;net7.0-windows</TargetFrameworks>
|
||||
<ApplicationIcon>Resources\as.ico</ApplicationIcon>
|
||||
<Version>0.90.00</Version>
|
||||
<AssemblyVersion>0.90.00</AssemblyVersion>
|
||||
<FileVersion>0.90.00</FileVersion>
|
||||
<Version>0.90.10</Version>
|
||||
<AssemblyVersion>0.90.10</AssemblyVersion>
|
||||
<FileVersion>0.90.10</FileVersion>
|
||||
<Copyright>Copyright © Razmoth 2022; Copyright © Perfare 2018-2022</Copyright>
|
||||
<BaseOutputPath>..\AssetStudioGUI\bin</BaseOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Version>0.90.00</Version>
|
||||
<AssemblyVersion>0.90.00</AssemblyVersion>
|
||||
<FileVersion>0.90.00</FileVersion>
|
||||
<Version>0.90.10</Version>
|
||||
<AssemblyVersion>0.90.10</AssemblyVersion>
|
||||
<FileVersion>0.90.10</FileVersion>
|
||||
<Copyright>Copyright © Perfare 2018-2022; Copyright © hozuki 2020</Copyright>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -27,9 +27,10 @@ namespace AssetStudio.FbxInterop
|
||||
[DllImport(FbxDll.DllName)]
|
||||
private static extern IntPtr AsFbxGetSceneRootNode(IntPtr context);
|
||||
|
||||
private static IntPtr AsFbxExportSingleFrame(IntPtr context, IntPtr parentNode, string framePath, string frameName, in Vector3 localPosition, in Vector3 localRotation, in Vector3 localScale)
|
||||
private static IntPtr AsFbxExportSingleFrame(IntPtr context, IntPtr parentNode, string framePath, string frameName, in Vector3 localPosition, in Quaternion localRotation, in Vector3 localScale)
|
||||
{
|
||||
return AsFbxExportSingleFrame(context, parentNode, framePath, frameName, localPosition.X, localPosition.Y, localPosition.Z, localRotation.X, localRotation.Y, localRotation.Z, localScale.X, localScale.Y, localScale.Z);
|
||||
var localRotationEuler = Fbx.QuaternionToEuler(localRotation);
|
||||
return AsFbxExportSingleFrame(context, parentNode, framePath, frameName, localPosition.X, localPosition.Y, localPosition.Z, localRotationEuler.X, localRotationEuler.Y, localRotationEuler.Z, localScale.X, localScale.Y, localScale.Z);
|
||||
}
|
||||
|
||||
[DllImport(FbxDll.DllName)]
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace AssetStudio.FbxInterop
|
||||
|
||||
public FbxExporterContext()
|
||||
{
|
||||
Fbx.QuaternionToEuler(Quaternion.Zero); // workaround to init dll
|
||||
_pContext = AsFbxCreateContext();
|
||||
_frameToNode = new Dictionary<ImportedFrame, IntPtr>();
|
||||
_createdMaterials = new List<KeyValuePair<string, IntPtr>>();
|
||||
@@ -554,7 +555,7 @@ namespace AssetStudio.FbxInterop
|
||||
|
||||
foreach (var rotation in track.Rotations)
|
||||
{
|
||||
var value = rotation.value;
|
||||
var value = Fbx.QuaternionToEuler(rotation.value);
|
||||
AsFbxAnimAddRotationKey(pAnimContext, rotation.time, value.X, value.Y, value.Z);
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +115,15 @@
|
||||
<setting name="selectedCABMapName" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="disableAnimationClip" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
<setting name="enableFileLogging" serializeAs="String">
|
||||
<value>True</value>
|
||||
</setting>
|
||||
<setting name="enableVerbose" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
</AssetStudioGUI.Properties.Settings>
|
||||
</userSettings>
|
||||
</configuration>
|
||||
@@ -5,9 +5,9 @@
|
||||
<TargetFrameworks>net6.0-windows;net7.0-windows</TargetFrameworks>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<ApplicationIcon>Resources\as.ico</ApplicationIcon>
|
||||
<Version>0.90.00</Version>
|
||||
<AssemblyVersion>0.90.00</AssemblyVersion>
|
||||
<FileVersion>0.90.00</FileVersion>
|
||||
<Version>0.90.10</Version>
|
||||
<AssemblyVersion>0.90.10</AssemblyVersion>
|
||||
<FileVersion>0.90.10</FileVersion>
|
||||
<Copyright>Copyright © Razmoth 2022; Copyright © Perfare 2018-2022</Copyright>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
@@ -69,7 +69,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="OpenTK" Version="5.0.0-pre.8" />
|
||||
<PackageReference Include="OpenTK" Version="4.8.0" />
|
||||
<Reference Include="OpenTK.WinForms">
|
||||
<HintPath>Libraries\OpenTK.WinForms.dll</HintPath>
|
||||
</Reference>
|
||||
|
||||
68
AssetStudioGUI/AssetStudioGUIForm.Designer.cs
generated
68
AssetStudioGUI/AssetStudioGUIForm.Designer.cs
generated
@@ -55,6 +55,8 @@ namespace AssetStudioGUI
|
||||
toolStripSeparator12 = new System.Windows.Forms.ToolStripSeparator();
|
||||
toolStripMenuItem14 = new System.Windows.Forms.ToolStripMenuItem();
|
||||
specifyUnityVersion = new System.Windows.Forms.ToolStripTextBox();
|
||||
specifyUnityCNKey = new System.Windows.Forms.ToolStripMenuItem();
|
||||
toolStripSeparator13 = new System.Windows.Forms.ToolStripSeparator();
|
||||
toolStripMenuItem18 = new System.Windows.Forms.ToolStripMenuItem();
|
||||
specifyGame = new System.Windows.Forms.ToolStripComboBox();
|
||||
toolStripMenuItem19 = new System.Windows.Forms.ToolStripMenuItem();
|
||||
@@ -101,6 +103,8 @@ namespace AssetStudioGUI
|
||||
exportClassStructuresMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
enableConsole = new System.Windows.Forms.ToolStripMenuItem();
|
||||
clearConsoleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
enableFileLogging = new System.Windows.Forms.ToolStripMenuItem();
|
||||
enableVerbose = new System.Windows.Forms.ToolStripMenuItem();
|
||||
miscToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
MapNameComboBox = new System.Windows.Forms.ToolStripComboBox();
|
||||
buildMapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
@@ -167,8 +171,6 @@ namespace AssetStudioGUI
|
||||
exportAnimatorwithselectedAnimationClipMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
goToSceneHierarchyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
showOriginalFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
specifyUnityCNKey = new System.Windows.Forms.ToolStripMenuItem();
|
||||
toolStripSeparator13 = new System.Windows.Forms.ToolStripSeparator();
|
||||
menuStrip1.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)splitContainer1).BeginInit();
|
||||
splitContainer1.Panel1.SuspendLayout();
|
||||
@@ -360,6 +362,18 @@ namespace AssetStudioGUI
|
||||
specifyUnityVersion.Name = "specifyUnityVersion";
|
||||
specifyUnityVersion.Size = new System.Drawing.Size(100, 23);
|
||||
//
|
||||
// specifyUnityCNKey
|
||||
//
|
||||
specifyUnityCNKey.Name = "specifyUnityCNKey";
|
||||
specifyUnityCNKey.Size = new System.Drawing.Size(225, 22);
|
||||
specifyUnityCNKey.Text = "Specify UnityCN Key";
|
||||
specifyUnityCNKey.Click += specifyUnityCNKey_Click;
|
||||
//
|
||||
// toolStripSeparator13
|
||||
//
|
||||
toolStripSeparator13.Name = "toolStripSeparator13";
|
||||
toolStripSeparator13.Size = new System.Drawing.Size(222, 6);
|
||||
//
|
||||
// toolStripMenuItem18
|
||||
//
|
||||
toolStripMenuItem18.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { specifyGame });
|
||||
@@ -642,7 +656,7 @@ namespace AssetStudioGUI
|
||||
//
|
||||
// debugMenuItem
|
||||
//
|
||||
debugMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { toolStripMenuItem15, exportClassStructuresMenuItem, enableConsole, clearConsoleToolStripMenuItem });
|
||||
debugMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { toolStripMenuItem15, exportClassStructuresMenuItem, enableConsole, clearConsoleToolStripMenuItem, enableFileLogging, enableVerbose });
|
||||
debugMenuItem.Name = "debugMenuItem";
|
||||
debugMenuItem.Size = new System.Drawing.Size(54, 20);
|
||||
debugMenuItem.Text = "Debug";
|
||||
@@ -681,6 +695,24 @@ namespace AssetStudioGUI
|
||||
clearConsoleToolStripMenuItem.Text = "Clear Console";
|
||||
clearConsoleToolStripMenuItem.Click += clearConsoleToolStripMenuItem_Click;
|
||||
//
|
||||
// enableFileLogging
|
||||
//
|
||||
enableFileLogging.Checked = true;
|
||||
enableFileLogging.CheckOnClick = true;
|
||||
enableFileLogging.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||
enableFileLogging.Name = "enableFileLogging";
|
||||
enableFileLogging.Size = new System.Drawing.Size(191, 22);
|
||||
enableFileLogging.Text = "Enable File Logging";
|
||||
enableFileLogging.CheckedChanged += enableFileLogging_CheckedChanged;
|
||||
//
|
||||
// enableVerbose
|
||||
//
|
||||
enableVerbose.CheckOnClick = true;
|
||||
enableVerbose.Name = "enableVerbose";
|
||||
enableVerbose.Size = new System.Drawing.Size(191, 22);
|
||||
enableVerbose.Text = "Enable Verbose";
|
||||
enableVerbose.CheckedChanged += enableVerbose_Click;
|
||||
//
|
||||
// miscToolStripMenuItem
|
||||
//
|
||||
miscToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { MapNameComboBox, buildMapToolStripMenuItem, buildBothToolStripMenuItem, clearMapToolStripMenuItem, toolStripSeparator7, assetMapNameTextBox, buildAssetMapToolStripMenuItem, assetMapTypeComboBox, toolStripSeparator8, loadAIToolStripMenuItem, assetBrowserToolStripMenuItem });
|
||||
@@ -843,16 +875,14 @@ namespace AssetStudioGUI
|
||||
// treeSearch
|
||||
//
|
||||
treeSearch.Dock = System.Windows.Forms.DockStyle.Top;
|
||||
treeSearch.ForeColor = System.Drawing.SystemColors.GrayText;
|
||||
treeSearch.ForeColor = System.Drawing.SystemColors.WindowText;
|
||||
treeSearch.Location = new System.Drawing.Point(0, 0);
|
||||
treeSearch.Name = "treeSearch";
|
||||
treeSearch.Size = new System.Drawing.Size(472, 23);
|
||||
treeSearch.TabIndex = 0;
|
||||
treeSearch.Text = " Search ";
|
||||
treeSearch.PlaceholderText = "Search (with Ctrl to check result, with Shift for all)";
|
||||
treeSearch.TextChanged += treeSearch_TextChanged;
|
||||
treeSearch.Enter += treeSearch_Enter;
|
||||
treeSearch.KeyDown += treeSearch_KeyDown;
|
||||
treeSearch.Leave += treeSearch_Leave;
|
||||
//
|
||||
// tabPage2
|
||||
//
|
||||
@@ -910,15 +940,13 @@ namespace AssetStudioGUI
|
||||
// listSearch
|
||||
//
|
||||
listSearch.Dock = System.Windows.Forms.DockStyle.Top;
|
||||
listSearch.ForeColor = System.Drawing.SystemColors.GrayText;
|
||||
listSearch.ForeColor = System.Drawing.SystemColors.WindowText;
|
||||
listSearch.Location = new System.Drawing.Point(0, 0);
|
||||
listSearch.Name = "listSearch";
|
||||
listSearch.Size = new System.Drawing.Size(472, 23);
|
||||
listSearch.TabIndex = 0;
|
||||
listSearch.Text = " Filter ";
|
||||
listSearch.TextChanged += ListSearchTextChanged;
|
||||
listSearch.Enter += listSearch_Enter;
|
||||
listSearch.Leave += listSearch_Leave;
|
||||
listSearch.PlaceholderText = "Search";
|
||||
listSearch.KeyPress += listSearch_KeyPress;
|
||||
//
|
||||
// tabPage3
|
||||
//
|
||||
@@ -1166,7 +1194,7 @@ namespace AssetStudioGUI
|
||||
// glControl
|
||||
//
|
||||
glControl.API = OpenTK.Windowing.Common.ContextAPI.OpenGL;
|
||||
glControl.APIVersion = new Version(4, 6, 0, 0);
|
||||
glControl.APIVersion = new Version(4, 5, 0, 0);
|
||||
glControl.BackColor = System.Drawing.SystemColors.ControlDarkDark;
|
||||
glControl.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
glControl.Flags = OpenTK.Windowing.Common.ContextFlags.Default;
|
||||
@@ -1308,18 +1336,6 @@ namespace AssetStudioGUI
|
||||
showOriginalFileToolStripMenuItem.Visible = false;
|
||||
showOriginalFileToolStripMenuItem.Click += showOriginalFileToolStripMenuItem_Click;
|
||||
//
|
||||
// specifyUnityCNKey
|
||||
//
|
||||
specifyUnityCNKey.Name = "specifyUnityCNKey";
|
||||
specifyUnityCNKey.Size = new System.Drawing.Size(225, 22);
|
||||
specifyUnityCNKey.Text = "Specify UnityCN Key";
|
||||
specifyUnityCNKey.Click += specifyUnityCNKey_Click;
|
||||
//
|
||||
// toolStripSeparator13
|
||||
//
|
||||
toolStripSeparator13.Name = "toolStripSeparator13";
|
||||
toolStripSeparator13.Size = new System.Drawing.Size(222, 6);
|
||||
//
|
||||
// AssetStudioGUIForm
|
||||
//
|
||||
AllowDrop = true;
|
||||
@@ -1506,6 +1522,8 @@ namespace AssetStudioGUI
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripSeparator12;
|
||||
private System.Windows.Forms.ToolStripMenuItem specifyUnityCNKey;
|
||||
private System.Windows.Forms.ToolStripSeparator toolStripSeparator13;
|
||||
private System.Windows.Forms.ToolStripMenuItem enableFileLogging;
|
||||
private System.Windows.Forms.ToolStripMenuItem enableVerbose;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ using Matrix4 = OpenTK.Mathematics.Matrix4;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK.Mathematics;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace AssetStudioGUI
|
||||
{
|
||||
@@ -50,14 +51,14 @@ namespace AssetStudioGUI
|
||||
private bool glControlLoaded;
|
||||
private int mdx, mdy;
|
||||
private bool lmdown, rmdown;
|
||||
private ProgramHandle pgmID, pgmColorID, pgmBlackID;
|
||||
private int pgmID, pgmColorID, pgmBlackID;
|
||||
private int attributeVertexPosition;
|
||||
private int attributeNormalDirection;
|
||||
private int attributeVertexColor;
|
||||
private int uniformModelMatrix;
|
||||
private int uniformViewMatrix;
|
||||
private int uniformProjMatrix;
|
||||
private VertexArrayHandle vao;
|
||||
private int vao;
|
||||
private Vector3[] vertexData;
|
||||
private Vector3[] normalData;
|
||||
private Vector3[] normal2Data;
|
||||
@@ -75,10 +76,6 @@ namespace AssetStudioGUI
|
||||
private int sortColumn = -1;
|
||||
private bool reverseSort;
|
||||
|
||||
//asset list filter
|
||||
private System.Timers.Timer delayTimer;
|
||||
private bool enableFiltering;
|
||||
|
||||
//tree search
|
||||
private int nextGObject;
|
||||
private List<TreeNode> treeSrcResults = new List<TreeNode>();
|
||||
@@ -93,8 +90,6 @@ namespace AssetStudioGUI
|
||||
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
|
||||
InitializeComponent();
|
||||
Text = $"Studio v{Application.ProductVersion}";
|
||||
delayTimer = new System.Timers.Timer(800);
|
||||
delayTimer.Elapsed += new ElapsedEventHandler(delayTimer_Elapsed);
|
||||
InitializeExportOptions();
|
||||
InitializeProgressBar();
|
||||
InitializeLogger();
|
||||
@@ -105,6 +100,8 @@ namespace AssetStudioGUI
|
||||
private void InitializeExportOptions()
|
||||
{
|
||||
enableConsole.Checked = Properties.Settings.Default.enableConsole;
|
||||
enableFileLogging.Checked = Properties.Settings.Default.enableFileLogging;
|
||||
enableVerbose.Checked = Properties.Settings.Default.enableVerbose;
|
||||
displayAll.Checked = Properties.Settings.Default.displayAll;
|
||||
displayInfo.Checked = Properties.Settings.Default.displayInfo;
|
||||
enablePreview.Checked = Properties.Settings.Default.enablePreview;
|
||||
@@ -119,10 +116,13 @@ namespace AssetStudioGUI
|
||||
AssetsHelper.Minimal = Properties.Settings.Default.minimalAssetMap;
|
||||
Renderer.Parsable = !Properties.Settings.Default.disableRenderer;
|
||||
Shader.Parsable = !Properties.Settings.Default.disableShader;
|
||||
AnimationClip.Parsable = !Properties.Settings.Default.disableAnimationClip;
|
||||
}
|
||||
|
||||
private void InitializeLogger()
|
||||
{
|
||||
Logger.LogVerbose = enableVerbose.Checked;
|
||||
Logger.FileLogging = enableFileLogging.Checked;
|
||||
logger = new GUILogger(StatusStripUpdate);
|
||||
ConsoleHelper.AllocConsole();
|
||||
ConsoleHelper.SetConsoleTitle("Debug Console");
|
||||
@@ -559,24 +559,6 @@ namespace AssetStudioGUI
|
||||
}
|
||||
}
|
||||
|
||||
private void treeSearch_Enter(object sender, EventArgs e)
|
||||
{
|
||||
if (treeSearch.Text == " Search ")
|
||||
{
|
||||
treeSearch.Text = "";
|
||||
treeSearch.ForeColor = SystemColors.WindowText;
|
||||
}
|
||||
}
|
||||
|
||||
private void treeSearch_Leave(object sender, EventArgs e)
|
||||
{
|
||||
if (treeSearch.Text == "")
|
||||
{
|
||||
treeSearch.Text = " Search ";
|
||||
treeSearch.ForeColor = SystemColors.GrayText;
|
||||
}
|
||||
}
|
||||
|
||||
private void treeSearch_TextChanged(object sender, EventArgs e)
|
||||
{
|
||||
treeSrcResults.Clear();
|
||||
@@ -585,38 +567,52 @@ namespace AssetStudioGUI
|
||||
|
||||
private void treeSearch_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.KeyCode == Keys.Enter)
|
||||
if (e.KeyCode == Keys.Enter && !string.IsNullOrEmpty(treeSearch.Text))
|
||||
{
|
||||
if (treeSrcResults.Count == 0)
|
||||
{
|
||||
var regex = new Regex(treeSearch.Text, RegexOptions.IgnoreCase);
|
||||
foreach (TreeNode node in sceneTreeView.Nodes)
|
||||
{
|
||||
TreeNodeSearch(node);
|
||||
TreeNodeSearch(regex, node);
|
||||
}
|
||||
}
|
||||
if (treeSrcResults.Count > 0)
|
||||
{
|
||||
if (e.Shift)
|
||||
{
|
||||
foreach(var node in treeSrcResults)
|
||||
{
|
||||
node.EnsureVisible();
|
||||
node.Checked = e.Control;
|
||||
}
|
||||
sceneTreeView.SelectedNode = treeSrcResults[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nextGObject >= treeSrcResults.Count)
|
||||
{
|
||||
nextGObject = 0;
|
||||
}
|
||||
treeSrcResults[nextGObject].EnsureVisible();
|
||||
treeSrcResults[nextGObject].Checked = e.Control;
|
||||
sceneTreeView.SelectedNode = treeSrcResults[nextGObject];
|
||||
nextGObject++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TreeNodeSearch(TreeNode treeNode)
|
||||
private void TreeNodeSearch(Regex regex, TreeNode treeNode)
|
||||
{
|
||||
if (treeNode.Text.IndexOf(treeSearch.Text, StringComparison.OrdinalIgnoreCase) >= 0)
|
||||
if (regex.IsMatch(treeNode.Text))
|
||||
{
|
||||
treeSrcResults.Add(treeNode);
|
||||
}
|
||||
|
||||
foreach (TreeNode node in treeNode.Nodes)
|
||||
{
|
||||
TreeNodeSearch(node);
|
||||
TreeNodeSearch(regex, node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -628,47 +624,13 @@ namespace AssetStudioGUI
|
||||
}
|
||||
}
|
||||
|
||||
private void listSearch_Enter(object sender, EventArgs e)
|
||||
private void listSearch_KeyPress(object sender, KeyPressEventArgs e)
|
||||
{
|
||||
if (listSearch.Text == " Filter ")
|
||||
if (e.KeyChar == (char)Keys.Enter)
|
||||
{
|
||||
listSearch.Text = "";
|
||||
listSearch.ForeColor = SystemColors.WindowText;
|
||||
enableFiltering = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void listSearch_Leave(object sender, EventArgs e)
|
||||
{
|
||||
if (listSearch.Text == "")
|
||||
{
|
||||
enableFiltering = false;
|
||||
listSearch.Text = " Filter ";
|
||||
listSearch.ForeColor = SystemColors.GrayText;
|
||||
}
|
||||
}
|
||||
|
||||
private void ListSearchTextChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (enableFiltering)
|
||||
{
|
||||
if (delayTimer.Enabled)
|
||||
{
|
||||
delayTimer.Stop();
|
||||
delayTimer.Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
delayTimer.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void delayTimer_Elapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
delayTimer.Stop();
|
||||
Invoke(new Action(FilterAssetList));
|
||||
}
|
||||
}
|
||||
|
||||
private void assetListView_ColumnClick(object sender, ColumnClickEventArgs e)
|
||||
{
|
||||
@@ -1429,8 +1391,7 @@ namespace AssetStudioGUI
|
||||
lastSelectedItem = null;
|
||||
sortColumn = -1;
|
||||
reverseSort = false;
|
||||
enableFiltering = false;
|
||||
listSearch.Text = " Filter ";
|
||||
listSearch.Text = string.Empty;
|
||||
|
||||
var count = filterTypeToolStripMenuItem.DropDownItems.Count;
|
||||
for (var i = 1; i < count; i++)
|
||||
@@ -1788,12 +1749,13 @@ namespace AssetStudioGUI
|
||||
}
|
||||
}
|
||||
}
|
||||
if (listSearch.Text != " Filter ")
|
||||
if (!string.IsNullOrEmpty(listSearch.Text))
|
||||
{
|
||||
var regex = new Regex(listSearch.Text, RegexOptions.IgnoreCase);
|
||||
visibleAssets = visibleAssets.FindAll(
|
||||
x => x.Text.IndexOf(listSearch.Text, StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||
x.SubItems[1].Text.IndexOf(listSearch.Text, StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||
x.SubItems[3].Text.IndexOf(listSearch.Text, StringComparison.OrdinalIgnoreCase) >= 0);
|
||||
x => regex.IsMatch(x.Text) ||
|
||||
regex.IsMatch(x.SubItems[1].Text) ||
|
||||
regex.IsMatch(x.SubItems[3].Text));
|
||||
}
|
||||
assetListView.VirtualListSize = visibleAssets.Count;
|
||||
assetListView.EndUpdate();
|
||||
@@ -2238,6 +2200,22 @@ namespace AssetStudioGUI
|
||||
}
|
||||
}
|
||||
|
||||
private void enableFileLogging_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
Properties.Settings.Default.enableFileLogging = enableFileLogging.Checked;
|
||||
Properties.Settings.Default.Save();
|
||||
|
||||
Logger.FileLogging = enableFileLogging.Checked;
|
||||
}
|
||||
|
||||
private void enableVerbose_Click(object sender, EventArgs e)
|
||||
{
|
||||
Properties.Settings.Default.enableVerbose = enableVerbose.Checked;
|
||||
Properties.Settings.Default.Save();
|
||||
|
||||
Logger.LogVerbose = enableVerbose.Checked;
|
||||
}
|
||||
|
||||
private void abortStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
Logger.Info("Aborting....");
|
||||
@@ -2607,10 +2585,10 @@ namespace AssetStudioGUI
|
||||
private void InitOpenTK()
|
||||
{
|
||||
ChangeGLSize(glControl.Size);
|
||||
GL.ClearColor(Color4.Cadetblue);
|
||||
GL.ClearColor(System.Drawing.Color.CadetBlue);
|
||||
pgmID = GL.CreateProgram();
|
||||
LoadShader("vs", ShaderType.VertexShader, pgmID, out ShaderHandle vsID);
|
||||
LoadShader("fs", ShaderType.FragmentShader, pgmID, out ShaderHandle fsID);
|
||||
LoadShader("vs", ShaderType.VertexShader, pgmID, out int vsID);
|
||||
LoadShader("fs", ShaderType.FragmentShader, pgmID, out int fsID);
|
||||
GL.LinkProgram(pgmID);
|
||||
|
||||
pgmColorID = GL.CreateProgram();
|
||||
@@ -2631,7 +2609,7 @@ namespace AssetStudioGUI
|
||||
uniformProjMatrix = GL.GetUniformLocation(pgmID, "projMatrix");
|
||||
}
|
||||
|
||||
private static void LoadShader(string filename, ShaderType type, ProgramHandle program, out ShaderHandle address)
|
||||
private static void LoadShader(string filename, ShaderType type, int program, out int address)
|
||||
{
|
||||
address = GL.CreateShader(type);
|
||||
var str = (string)Properties.Resources.ResourceManager.GetObject(filename);
|
||||
@@ -2641,47 +2619,50 @@ namespace AssetStudioGUI
|
||||
GL.DeleteShader(address);
|
||||
}
|
||||
|
||||
private static void CreateVBO(out BufferHandle vboAddress, Vector3[] data, int address)
|
||||
private static void CreateVBO(out int vboAddress, Vector3[] data, int address)
|
||||
{
|
||||
GL.CreateBuffer(out vboAddress);
|
||||
GL.BindBuffer(BufferTargetARB.ArrayBuffer, vboAddress);
|
||||
GL.BufferData(BufferTargetARB.ArrayBuffer,
|
||||
GL.GenBuffers(1, out vboAddress);
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, vboAddress);
|
||||
GL.BufferData(BufferTarget.ArrayBuffer,
|
||||
(IntPtr)(data.Length * Vector3.SizeInBytes),
|
||||
data,
|
||||
BufferUsageARB.StaticDraw);
|
||||
GL.VertexAttribPointer((uint)address, 3, VertexAttribPointerType.Float, false, 0, 0);
|
||||
GL.EnableVertexAttribArray((uint)address);
|
||||
BufferUsageHint.StaticDraw);
|
||||
GL.VertexAttribPointer(address, 3, VertexAttribPointerType.Float, false, 0, 0);
|
||||
GL.EnableVertexAttribArray(address);
|
||||
}
|
||||
|
||||
private static void CreateVBO(out BufferHandle vboAddress, Vector4[] data, int address)
|
||||
private static void CreateVBO(out int vboAddress, Vector4[] data, int address)
|
||||
{
|
||||
GL.CreateBuffer(out vboAddress);
|
||||
GL.BindBuffer(BufferTargetARB.ArrayBuffer, vboAddress);
|
||||
GL.BufferData(BufferTargetARB.ArrayBuffer,
|
||||
GL.GenBuffers(1, out vboAddress);
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, vboAddress);
|
||||
GL.BufferData(BufferTarget.ArrayBuffer,
|
||||
(IntPtr)(data.Length * Vector4.SizeInBytes),
|
||||
data,
|
||||
BufferUsageARB.StaticDraw);
|
||||
GL.VertexAttribPointer((uint)address, 4, VertexAttribPointerType.Float, false, 0, 0);
|
||||
GL.EnableVertexAttribArray((uint)address);
|
||||
BufferUsageHint.StaticDraw);
|
||||
GL.VertexAttribPointer(address, 4, VertexAttribPointerType.Float, false, 0, 0);
|
||||
GL.EnableVertexAttribArray(address);
|
||||
}
|
||||
|
||||
private static void CreateVBO(out BufferHandle vboAddress, Matrix4 data, int address)
|
||||
private static void CreateVBO(out int vboAddress, Matrix4 data, int address)
|
||||
{
|
||||
GL.CreateBuffer(out vboAddress);
|
||||
GL.UniformMatrix4f(address, false, in data);
|
||||
GL.GenBuffers(1, out vboAddress);
|
||||
GL.UniformMatrix4(address, false, ref data);
|
||||
}
|
||||
|
||||
private static void CreateEBO(out BufferHandle address, int[] data)
|
||||
private static void CreateEBO(out int address, int[] data)
|
||||
{
|
||||
GL.CreateBuffer(out address);
|
||||
GL.BindBuffer(BufferTargetARB.ElementArrayBuffer, address);
|
||||
GL.BufferData(BufferTargetARB.ElementArrayBuffer,
|
||||
GL.GenBuffers(1, out address);
|
||||
GL.BindBuffer(BufferTarget.ElementArrayBuffer, address);
|
||||
GL.BufferData(BufferTarget.ElementArrayBuffer,
|
||||
(IntPtr)(data.Length * sizeof(int)),
|
||||
data,
|
||||
BufferUsageARB.StaticDraw);
|
||||
BufferUsageHint.StaticDraw);
|
||||
}
|
||||
|
||||
private void CreateVAO()
|
||||
{
|
||||
GL.DeleteVertexArray(vao);
|
||||
GL.CreateVertexArray(out vao);
|
||||
GL.GenVertexArrays(1, out vao);
|
||||
GL.BindVertexArray(vao);
|
||||
CreateVBO(out var vboPositions, vertexData, attributeVertexPosition);
|
||||
if (normalMode == 0)
|
||||
@@ -2698,8 +2679,8 @@ namespace AssetStudioGUI
|
||||
CreateVBO(out var vboViewMatrix, viewMatrixData, uniformViewMatrix);
|
||||
CreateVBO(out var vboProjMatrix, projMatrixData, uniformProjMatrix);
|
||||
CreateEBO(out var eboElements, indiceData);
|
||||
GL.BindBuffer(BufferTargetARB.ArrayBuffer, BufferHandle.Zero);
|
||||
GL.BindVertexArray(VertexArrayHandle.Zero);
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
|
||||
GL.BindVertexArray(0);
|
||||
}
|
||||
|
||||
private void ChangeGLSize(Size size)
|
||||
@@ -2734,10 +2715,10 @@ namespace AssetStudioGUI
|
||||
if (wireFrameMode == 0 || wireFrameMode == 2)
|
||||
{
|
||||
GL.UseProgram(shadeMode == 0 ? pgmID : pgmColorID);
|
||||
GL.UniformMatrix4f(uniformModelMatrix, false, in modelMatrixData);
|
||||
GL.UniformMatrix4f(uniformViewMatrix, false, in viewMatrixData);
|
||||
GL.UniformMatrix4f(uniformProjMatrix, false, in projMatrixData);
|
||||
GL.PolygonMode(TriangleFace.FrontAndBack, PolygonMode.Fill);
|
||||
GL.UniformMatrix4(uniformModelMatrix, false, ref modelMatrixData);
|
||||
GL.UniformMatrix4(uniformViewMatrix, false, ref viewMatrixData);
|
||||
GL.UniformMatrix4(uniformProjMatrix, false, ref projMatrixData);
|
||||
GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);
|
||||
GL.DrawElements(PrimitiveType.Triangles, indiceData.Length, DrawElementsType.UnsignedInt, 0);
|
||||
}
|
||||
//Wireframe
|
||||
@@ -2746,14 +2727,14 @@ namespace AssetStudioGUI
|
||||
GL.Enable(EnableCap.PolygonOffsetLine);
|
||||
GL.PolygonOffset(-1, -1);
|
||||
GL.UseProgram(pgmBlackID);
|
||||
GL.UniformMatrix4f(uniformModelMatrix, false, in modelMatrixData);
|
||||
GL.UniformMatrix4f(uniformViewMatrix, false, in viewMatrixData);
|
||||
GL.UniformMatrix4f(uniformProjMatrix, false, in projMatrixData);
|
||||
GL.PolygonMode(TriangleFace.FrontAndBack, PolygonMode.Line);
|
||||
GL.UniformMatrix4(uniformModelMatrix, false, ref modelMatrixData);
|
||||
GL.UniformMatrix4(uniformViewMatrix, false, ref viewMatrixData);
|
||||
GL.UniformMatrix4(uniformProjMatrix, false, ref projMatrixData);
|
||||
GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);
|
||||
GL.DrawElements(PrimitiveType.Triangles, indiceData.Length, DrawElementsType.UnsignedInt, 0);
|
||||
GL.Disable(EnableCap.PolygonOffsetLine);
|
||||
}
|
||||
GL.BindVertexArray(VertexArrayHandle.Zero);
|
||||
GL.BindVertexArray(0);
|
||||
GL.Flush();
|
||||
glControl.SwapBuffers();
|
||||
}
|
||||
|
||||
@@ -120,6 +120,9 @@
|
||||
<metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>312, 17</value>
|
||||
</metadata>
|
||||
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>432, 17</value>
|
||||
</metadata>
|
||||
<data name="fontPreviewBox.Text" xml:space="preserve">
|
||||
<value>abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWYZ
|
||||
1234567890.:,;'\"(!?)+-*/=
|
||||
@@ -138,9 +141,6 @@ The quick brown fox jumps over the lazy dog. 1234567890
|
||||
|
||||
The quick brown fox jumps over the lazy dog. 1234567890</value>
|
||||
</data>
|
||||
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>432, 17</value>
|
||||
</metadata>
|
||||
<metadata name="timer.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>553, 17</value>
|
||||
</metadata>
|
||||
|
||||
65
AssetStudioGUI/ExportOptions.Designer.cs
generated
65
AssetStudioGUI/ExportOptions.Designer.cs
generated
@@ -35,6 +35,8 @@ namespace AssetStudioGUI
|
||||
OKbutton = new System.Windows.Forms.Button();
|
||||
Cancel = new System.Windows.Forms.Button();
|
||||
groupBox1 = new System.Windows.Forms.GroupBox();
|
||||
assetGroupOptions = new System.Windows.Forms.ComboBox();
|
||||
label7 = new System.Windows.Forms.Label();
|
||||
openAfterExport = new System.Windows.Forms.CheckBox();
|
||||
restoreExtensionName = new System.Windows.Forms.CheckBox();
|
||||
assetGroupOptions = new System.Windows.Forms.ComboBox();
|
||||
@@ -71,11 +73,10 @@ namespace AssetStudioGUI
|
||||
key = new System.Windows.Forms.NumericUpDown();
|
||||
keyToolTip = new System.Windows.Forms.ToolTip(components);
|
||||
groupBox4 = new System.Windows.Forms.GroupBox();
|
||||
disableAnimationClip = new System.Windows.Forms.CheckBox();
|
||||
minimalAssetMap = new System.Windows.Forms.CheckBox();
|
||||
disableShader = new System.Windows.Forms.CheckBox();
|
||||
disableRenderer = new System.Windows.Forms.CheckBox();
|
||||
resolveToolTip = new System.Windows.Forms.ToolTip(components);
|
||||
skipToolTip = new System.Windows.Forms.ToolTip(components);
|
||||
groupBox1.SuspendLayout();
|
||||
panel1.SuspendLayout();
|
||||
groupBox2.SuspendLayout();
|
||||
@@ -112,6 +113,8 @@ namespace AssetStudioGUI
|
||||
// groupBox1
|
||||
//
|
||||
groupBox1.AutoSize = true;
|
||||
groupBox1.Controls.Add(assetGroupOptions);
|
||||
groupBox1.Controls.Add(label7);
|
||||
groupBox1.Controls.Add(openAfterExport);
|
||||
groupBox1.Controls.Add(restoreExtensionName);
|
||||
groupBox1.Controls.Add(assetGroupOptions);
|
||||
@@ -123,17 +126,38 @@ namespace AssetStudioGUI
|
||||
groupBox1.Margin = new System.Windows.Forms.Padding(4);
|
||||
groupBox1.Name = "groupBox1";
|
||||
groupBox1.Padding = new System.Windows.Forms.Padding(4);
|
||||
groupBox1.Size = new System.Drawing.Size(271, 243);
|
||||
groupBox1.Size = new System.Drawing.Size(271, 273);
|
||||
groupBox1.TabIndex = 9;
|
||||
groupBox1.TabStop = false;
|
||||
groupBox1.Text = "Export";
|
||||
//
|
||||
// assetGroupOptions
|
||||
//
|
||||
assetGroupOptions.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
assetGroupOptions.FormattingEnabled = true;
|
||||
assetGroupOptions.Items.AddRange(new object[] { "type name", "container path", "source file name", "do not group" });
|
||||
assetGroupOptions.Location = new System.Drawing.Point(7, 83);
|
||||
assetGroupOptions.Margin = new System.Windows.Forms.Padding(4);
|
||||
assetGroupOptions.Name = "assetGroupOptions";
|
||||
assetGroupOptions.Size = new System.Drawing.Size(173, 23);
|
||||
assetGroupOptions.TabIndex = 12;
|
||||
//
|
||||
// label7
|
||||
//
|
||||
label7.AutoSize = true;
|
||||
label7.Location = new System.Drawing.Point(7, 64);
|
||||
label7.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
label7.Name = "label7";
|
||||
label7.Size = new System.Drawing.Size(140, 15);
|
||||
label7.TabIndex = 11;
|
||||
label7.Text = "Group exported assets by";
|
||||
//
|
||||
// openAfterExport
|
||||
//
|
||||
openAfterExport.AutoSize = true;
|
||||
openAfterExport.Checked = true;
|
||||
openAfterExport.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||
openAfterExport.Location = new System.Drawing.Point(7, 200);
|
||||
openAfterExport.Location = new System.Drawing.Point(8, 230);
|
||||
openAfterExport.Margin = new System.Windows.Forms.Padding(4);
|
||||
openAfterExport.Name = "openAfterExport";
|
||||
openAfterExport.Size = new System.Drawing.Size(153, 19);
|
||||
@@ -146,7 +170,7 @@ namespace AssetStudioGUI
|
||||
restoreExtensionName.AutoSize = true;
|
||||
restoreExtensionName.Checked = true;
|
||||
restoreExtensionName.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||
restoreExtensionName.Location = new System.Drawing.Point(7, 72);
|
||||
restoreExtensionName.Location = new System.Drawing.Point(7, 109);
|
||||
restoreExtensionName.Margin = new System.Windows.Forms.Padding(4);
|
||||
restoreExtensionName.Name = "restoreExtensionName";
|
||||
restoreExtensionName.Size = new System.Drawing.Size(204, 19);
|
||||
@@ -171,7 +195,7 @@ namespace AssetStudioGUI
|
||||
label6.Location = new System.Drawing.Point(7, 21);
|
||||
label6.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
label6.Name = "label6";
|
||||
label6.Size = new System.Drawing.Size(140, 15);
|
||||
label6.Size = new System.Drawing.Size(80, 15);
|
||||
label6.TabIndex = 7;
|
||||
label6.Text = "Group exported assets by";
|
||||
//
|
||||
@@ -180,7 +204,7 @@ namespace AssetStudioGUI
|
||||
convertAudio.AutoSize = true;
|
||||
convertAudio.Checked = true;
|
||||
convertAudio.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||
convertAudio.Location = new System.Drawing.Point(7, 172);
|
||||
convertAudio.Location = new System.Drawing.Point(7, 204);
|
||||
convertAudio.Margin = new System.Windows.Forms.Padding(4);
|
||||
convertAudio.Name = "convertAudio";
|
||||
convertAudio.Size = new System.Drawing.Size(200, 19);
|
||||
@@ -194,7 +218,7 @@ namespace AssetStudioGUI
|
||||
panel1.Controls.Add(tojpg);
|
||||
panel1.Controls.Add(topng);
|
||||
panel1.Controls.Add(tobmp);
|
||||
panel1.Location = new System.Drawing.Point(23, 128);
|
||||
panel1.Location = new System.Drawing.Point(23, 164);
|
||||
panel1.Margin = new System.Windows.Forms.Padding(4);
|
||||
panel1.Name = "panel1";
|
||||
panel1.Size = new System.Drawing.Size(236, 38);
|
||||
@@ -251,7 +275,7 @@ namespace AssetStudioGUI
|
||||
converttexture.AutoSize = true;
|
||||
converttexture.Checked = true;
|
||||
converttexture.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||
converttexture.Location = new System.Drawing.Point(7, 100);
|
||||
converttexture.Location = new System.Drawing.Point(7, 136);
|
||||
converttexture.Margin = new System.Windows.Forms.Padding(4);
|
||||
converttexture.Name = "converttexture";
|
||||
converttexture.Size = new System.Drawing.Size(123, 19);
|
||||
@@ -536,20 +560,32 @@ namespace AssetStudioGUI
|
||||
// groupBox4
|
||||
//
|
||||
groupBox4.AutoSize = true;
|
||||
groupBox4.Controls.Add(disableAnimationClip);
|
||||
groupBox4.Controls.Add(minimalAssetMap);
|
||||
groupBox4.Controls.Add(disableShader);
|
||||
groupBox4.Controls.Add(disableRenderer);
|
||||
groupBox4.Controls.Add(key);
|
||||
groupBox4.Controls.Add(encrypted);
|
||||
groupBox4.Location = new System.Drawing.Point(13, 258);
|
||||
groupBox4.Location = new System.Drawing.Point(13, 287);
|
||||
groupBox4.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
groupBox4.Name = "groupBox4";
|
||||
groupBox4.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
groupBox4.Size = new System.Drawing.Size(272, 187);
|
||||
groupBox4.Size = new System.Drawing.Size(272, 146);
|
||||
groupBox4.TabIndex = 13;
|
||||
groupBox4.TabStop = false;
|
||||
groupBox4.Text = "Options";
|
||||
//
|
||||
// disableAnimationClip
|
||||
//
|
||||
disableAnimationClip.AutoSize = true;
|
||||
disableAnimationClip.Location = new System.Drawing.Point(119, 72);
|
||||
disableAnimationClip.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
disableAnimationClip.Name = "disableAnimationClip";
|
||||
disableAnimationClip.Size = new System.Drawing.Size(144, 19);
|
||||
disableAnimationClip.TabIndex = 18;
|
||||
disableAnimationClip.Text = "Disable AnimationClip";
|
||||
disableAnimationClip.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// minimalAssetMap
|
||||
//
|
||||
minimalAssetMap.AutoSize = true;
|
||||
@@ -569,7 +605,6 @@ namespace AssetStudioGUI
|
||||
disableShader.Size = new System.Drawing.Size(103, 19);
|
||||
disableShader.TabIndex = 16;
|
||||
disableShader.Text = "Disable Shader";
|
||||
skipToolTip.SetToolTip(disableShader, "Skips the container recovery step.\nImproves loading when dealing with a large number of files.");
|
||||
disableShader.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// disableRenderer
|
||||
@@ -581,7 +616,6 @@ namespace AssetStudioGUI
|
||||
disableRenderer.Size = new System.Drawing.Size(114, 19);
|
||||
disableRenderer.TabIndex = 15;
|
||||
disableRenderer.Text = "Disable Renderer";
|
||||
skipToolTip.SetToolTip(disableRenderer, "Skips the container recovery step.\nImproves loading when dealing with a large number of files.");
|
||||
disableRenderer.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// ExportOptions
|
||||
@@ -661,10 +695,11 @@ namespace AssetStudioGUI
|
||||
private System.Windows.Forms.ToolTip keyToolTip;
|
||||
private System.Windows.Forms.CheckBox exportUV0UV1;
|
||||
private System.Windows.Forms.GroupBox groupBox4;
|
||||
private System.Windows.Forms.ToolTip resolveToolTip;
|
||||
private System.Windows.Forms.ToolTip skipToolTip;
|
||||
private System.Windows.Forms.CheckBox disableShader;
|
||||
private System.Windows.Forms.CheckBox disableRenderer;
|
||||
private System.Windows.Forms.CheckBox minimalAssetMap;
|
||||
private System.Windows.Forms.ComboBox assetGroupOptions;
|
||||
private System.Windows.Forms.Label label7;
|
||||
private System.Windows.Forms.CheckBox disableAnimationClip;
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,7 @@ namespace AssetStudioGUI
|
||||
key.Value = Properties.Settings.Default.key;
|
||||
disableRenderer.Checked = Properties.Settings.Default.disableRenderer;
|
||||
disableShader.Checked = Properties.Settings.Default.disableShader;
|
||||
disableAnimationClip.Checked = Properties.Settings.Default.disableAnimationClip;
|
||||
minimalAssetMap.Checked = Properties.Settings.Default.minimalAssetMap;
|
||||
}
|
||||
|
||||
@@ -80,6 +81,7 @@ namespace AssetStudioGUI
|
||||
Properties.Settings.Default.key = (byte)key.Value;
|
||||
Properties.Settings.Default.disableRenderer = disableRenderer.Checked;
|
||||
Properties.Settings.Default.disableShader = disableShader.Checked;
|
||||
Properties.Settings.Default.disableAnimationClip = disableAnimationClip.Checked;
|
||||
Properties.Settings.Default.minimalAssetMap = minimalAssetMap.Checked;
|
||||
Properties.Settings.Default.Save();
|
||||
MiHoYoBinData.Key = (byte)key.Value;
|
||||
@@ -87,6 +89,7 @@ namespace AssetStudioGUI
|
||||
AssetsHelper.Minimal = Properties.Settings.Default.minimalAssetMap;
|
||||
Renderer.Parsable = !Properties.Settings.Default.disableRenderer;
|
||||
Shader.Parsable = !Properties.Settings.Default.disableShader;
|
||||
AnimationClip.Parsable = !Properties.Settings.Default.disableAnimationClip;
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
|
||||
@@ -120,24 +120,9 @@
|
||||
<metadata name="exportUvsTooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<metadata name="exportUvsTooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<metadata name="keyToolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>162, 17</value>
|
||||
</metadata>
|
||||
<metadata name="keyToolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>162, 17</value>
|
||||
</metadata>
|
||||
<metadata name="skipToolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>404, 17</value>
|
||||
</metadata>
|
||||
<metadata name="resolveToolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>273, 17</value>
|
||||
</metadata>
|
||||
<metadata name="skipToolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>404, 17</value>
|
||||
</metadata>
|
||||
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>57</value>
|
||||
</metadata>
|
||||
|
||||
36
AssetStudioGUI/Properties/Settings.Designer.cs
generated
36
AssetStudioGUI/Properties/Settings.Designer.cs
generated
@@ -465,5 +465,41 @@ namespace AssetStudioGUI.Properties {
|
||||
this["selectedCABMapName"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
public bool disableAnimationClip {
|
||||
get {
|
||||
return ((bool)(this["disableAnimationClip"]));
|
||||
}
|
||||
set {
|
||||
this["disableAnimationClip"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("True")]
|
||||
public bool enableFileLogging {
|
||||
get {
|
||||
return ((bool)(this["enableFileLogging"]));
|
||||
}
|
||||
set {
|
||||
this["enableFileLogging"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
public bool enableVerbose {
|
||||
get {
|
||||
return ((bool)(this["enableVerbose"]));
|
||||
}
|
||||
set {
|
||||
this["enableVerbose"] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,5 +113,14 @@
|
||||
<Setting Name="selectedCABMapName" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
<Setting Name="disableAnimationClip" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="enableFileLogging" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">True</Value>
|
||||
</Setting>
|
||||
<Setting Name="enableVerbose" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
</Settings>
|
||||
</SettingsFile>
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
|
||||
<Version>0.90.00</Version>
|
||||
<AssemblyVersion>0.90.00</AssemblyVersion>
|
||||
<FileVersion>0.90.00</FileVersion>
|
||||
<Version>0.90.10</Version>
|
||||
<AssemblyVersion>0.90.10</AssemblyVersion>
|
||||
<FileVersion>0.90.10</FileVersion>
|
||||
<Copyright>Copyright © Perfare 2018-2022</Copyright>
|
||||
<DebugType>embedded</DebugType>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
@@ -13,7 +13,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Kyaru.Texture2DDecoder" Version="0.17.0" />
|
||||
<PackageReference Include="Kyaru.Texture2DDecoder.Windows" Version="0.1.0" />
|
||||
<PackageReference Include="Mono.Cecil" Version="0.11.4" />
|
||||
<PackageReference Include="Mono.Cecil" Version="0.11.5" />
|
||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta15" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -244,7 +244,7 @@ namespace AssetStudio
|
||||
private static void SetFrame(ImportedFrame frame, Vector3 t, Quaternion q, Vector3 s)
|
||||
{
|
||||
frame.LocalPosition = new Vector3(-t.X, t.Y, t.Z);
|
||||
frame.LocalRotation = Fbx.QuaternionToEuler(new Quaternion(q.X, -q.Y, -q.Z, q.W));
|
||||
frame.LocalRotation = new Quaternion(q.X, -q.Y, -q.Z, q.W);
|
||||
frame.LocalScale = s;
|
||||
}
|
||||
|
||||
@@ -818,8 +818,8 @@ namespace AssetStudio
|
||||
for (int i = 0; i < numKeys; i++)
|
||||
{
|
||||
var quat = quats[i];
|
||||
var value = Fbx.QuaternionToEuler(new Quaternion(quat.X, -quat.Y, -quat.Z, quat.W));
|
||||
track.Rotations.Add(new ImportedKeyframe<Vector3>(times[i], value));
|
||||
var value = new Quaternion(quat.X, -quat.Y, -quat.Z, quat.W);
|
||||
track.Rotations.Add(new ImportedKeyframe<Quaternion>(times[i], value));
|
||||
}
|
||||
}
|
||||
foreach (var m_RotationCurve in animationClip.m_RotationCurves)
|
||||
@@ -827,8 +827,8 @@ namespace AssetStudio
|
||||
var track = iAnim.FindTrack(FixBonePath(animationClip, m_RotationCurve.path));
|
||||
foreach (var m_Curve in m_RotationCurve.curve.m_Curve)
|
||||
{
|
||||
var value = Fbx.QuaternionToEuler(new Quaternion(m_Curve.value.X, -m_Curve.value.Y, -m_Curve.value.Z, m_Curve.value.W));
|
||||
track.Rotations.Add(new ImportedKeyframe<Vector3>(m_Curve.time, value));
|
||||
var value = new Quaternion(m_Curve.value.X, -m_Curve.value.Y, -m_Curve.value.Z, m_Curve.value.W);
|
||||
track.Rotations.Add(new ImportedKeyframe<Quaternion>(m_Curve.time, value));
|
||||
}
|
||||
}
|
||||
foreach (var m_PositionCurve in animationClip.m_PositionCurves)
|
||||
@@ -854,7 +854,8 @@ namespace AssetStudio
|
||||
var track = iAnim.FindTrack(FixBonePath(animationClip, m_EulerCurve.path));
|
||||
foreach (var m_Curve in m_EulerCurve.curve.m_Curve)
|
||||
{
|
||||
track.Rotations.Add(new ImportedKeyframe<Vector3>(m_Curve.time, new Vector3(m_Curve.value.X, -m_Curve.value.Y, -m_Curve.value.Z)));
|
||||
var value = Fbx.EulerToQuaternion(new Vector3(m_Curve.value.X, -m_Curve.value.Y, -m_Curve.value.Z));
|
||||
track.Rotations.Add(new ImportedKeyframe<Quaternion>(m_Curve.time, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1009,14 +1010,13 @@ namespace AssetStudio
|
||||
)));
|
||||
break;
|
||||
case 2:
|
||||
var value = Fbx.QuaternionToEuler(new Quaternion
|
||||
track.Rotations.Add(new ImportedKeyframe<Quaternion>(time, new Quaternion
|
||||
(
|
||||
data[curveIndex++ + offset],
|
||||
-data[curveIndex++ + offset],
|
||||
-data[curveIndex++ + offset],
|
||||
data[curveIndex++ + offset]
|
||||
));
|
||||
track.Rotations.Add(new ImportedKeyframe<Vector3>(time, value));
|
||||
)));
|
||||
break;
|
||||
case 3:
|
||||
track.Scalings.Add(new ImportedKeyframe<Vector3>(time, new Vector3
|
||||
@@ -1027,12 +1027,13 @@ namespace AssetStudio
|
||||
)));
|
||||
break;
|
||||
case 4:
|
||||
track.Rotations.Add(new ImportedKeyframe<Vector3>(time, new Vector3
|
||||
var value = Fbx.EulerToQuaternion(new Vector3
|
||||
(
|
||||
data[curveIndex++ + offset],
|
||||
-data[curveIndex++ + offset],
|
||||
-data[curveIndex++ + offset]
|
||||
)));
|
||||
));
|
||||
track.Rotations.Add(new ImportedKeyframe<Quaternion>(time, value));
|
||||
break;
|
||||
default:
|
||||
curveIndex++;
|
||||
|
||||
Reference in New Issue
Block a user