First pass for TRPAK archive files (#544)

first seen in Pokemon Scarlet/Violet
This commit is contained in:
Teras 2022-11-20 15:38:38 +01:00 committed by GitHub
parent 8fc71d3c32
commit d55f738297
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 292 additions and 1 deletions

View file

@ -0,0 +1,116 @@
// <auto-generated>
// automatically generated by the FlatBuffers compiler, do not modify
// </auto-generated>
using global::System;
using global::FlatBuffers;
namespace FlatBuffers.TRPAK
{
public struct TRPAK : IFlatbufferObject
{
private Table __p;
public ByteBuffer ByteBuffer { get { return __p.bb; } }
public static TRPAK GetRootAsTRPAK(ByteBuffer _bb) { return GetRootAsTRPAK(_bb, new TRPAK()); }
public static TRPAK GetRootAsTRPAK(ByteBuffer _bb, TRPAK obj) { return (obj.__assign(_bb.GetInt(_bb.Position) + _bb.Position, _bb)); }
public void __init(int _i, ByteBuffer _bb) { __p.bb_pos = _i; __p.bb = _bb; }
public TRPAK __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; }
public ulong Hashes(int j) { int o = __p.__offset(4); return o != 0 ? __p.bb.GetUlong(__p.__vector(o) + j * 8) : (ulong)0; }
public int HashesLength { get { int o = __p.__offset(4); return o != 0 ? __p.__vector_len(o) : 0; } }
#if ENABLE_SPAN_T
public Span<byte> GetHashesBytes() { return __p.__vector_as_span(4); }
#else
public ArraySegment<byte>? GetHashesBytes() { return __p.__vector_as_arraysegment(4); }
#endif
public ulong[] GetHashesArray() { return __p.__vector_as_array<ulong>(4); }
public File? Files(int j) { int o = __p.__offset(6); return o != 0 ? (File?)(new File()).__assign(__p.__indirect(__p.__vector(o) + j * 4), __p.bb) : null; }
public int FilesLength { get { int o = __p.__offset(6); return o != 0 ? __p.__vector_len(o) : 0; } }
public static Offset<TRPAK> CreateTRPAK(FlatBufferBuilder builder,
VectorOffset hashesOffset = default(VectorOffset),
VectorOffset filesOffset = default(VectorOffset))
{
builder.StartObject(2);
TRPAK.AddFiles(builder, filesOffset);
TRPAK.AddHashes(builder, hashesOffset);
return TRPAK.EndTRPAK(builder);
}
public static void StartTRPAK(FlatBufferBuilder builder) { builder.StartObject(2); }
public static void AddHashes(FlatBufferBuilder builder, VectorOffset hashesOffset) { builder.AddOffset(0, hashesOffset.Value, 0); }
public static VectorOffset CreateHashesVector(FlatBufferBuilder builder, ulong[] data) { builder.StartVector(8, data.Length, 8); for (int i = data.Length - 1; i >= 0; i--) builder.AddUlong(data[i]); return builder.EndVector(); }
public static VectorOffset CreateHashesVectorBlock(FlatBufferBuilder builder, ulong[] data) { builder.StartVector(8, data.Length, 8); builder.Add(data); return builder.EndVector(); }
public static void StartHashesVector(FlatBufferBuilder builder, int numElems) { builder.StartVector(8, numElems, 8); }
public static void AddFiles(FlatBufferBuilder builder, VectorOffset filesOffset) { builder.AddOffset(1, filesOffset.Value, 0); }
public static VectorOffset CreateFilesVector(FlatBufferBuilder builder, Offset<File>[] data) { builder.StartVector(4, data.Length, 4); for (int i = data.Length - 1; i >= 0; i--) builder.AddOffset(data[i].Value); return builder.EndVector(); }
public static VectorOffset CreateFilesVectorBlock(FlatBufferBuilder builder, Offset<File>[] data) { builder.StartVector(4, data.Length, 4); builder.Add(data); return builder.EndVector(); }
public static void StartFilesVector(FlatBufferBuilder builder, int numElems) { builder.StartVector(4, numElems, 4); }
public static Offset<TRPAK> EndTRPAK(FlatBufferBuilder builder)
{
int o = builder.EndObject();
return new Offset<TRPAK>(o);
}
public static void FinishTRPAKBuffer(FlatBufferBuilder builder, Offset<TRPAK> offset) { builder.Finish(offset.Value); }
public static void FinishSizePrefixedTRPAKBuffer(FlatBufferBuilder builder, Offset<TRPAK> offset) { builder.FinishSizePrefixed(offset.Value); }
};
public struct File : IFlatbufferObject
{
private Table __p;
public ByteBuffer ByteBuffer { get { return __p.bb; } }
public static File GetRootAsFile(ByteBuffer _bb) { return GetRootAsFile(_bb, new File()); }
public static File GetRootAsFile(ByteBuffer _bb, File obj) { return (obj.__assign(_bb.GetInt(_bb.Position) + _bb.Position, _bb)); }
public void __init(int _i, ByteBuffer _bb) { __p.bb_pos = _i; __p.bb = _bb; }
public File __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; }
public byte Unused { get { int o = __p.__offset(4); return o != 0 ? __p.bb.Get(o + __p.bb_pos) : (byte)0; } }
public Compression CompressionType { get { int o = __p.__offset(6); return o != 0 ? (Compression)__p.bb.Get(o + __p.bb_pos) : Compression.NONE; } }
public byte Unk1 { get { int o = __p.__offset(8); return o != 0 ? __p.bb.Get(o + __p.bb_pos) : (byte)0; } }
public ulong DecompressedSize { get { int o = __p.__offset(10); return o != 0 ? __p.bb.GetUlong(o + __p.bb_pos) : (ulong)0; } }
public byte Data(int j) { int o = __p.__offset(12); return o != 0 ? __p.bb.Get(__p.__vector(o) + j * 1) : (byte)0; }
public int DataLength { get { int o = __p.__offset(12); return o != 0 ? __p.__vector_len(o) : 0; } }
#if ENABLE_SPAN_T
public Span<byte> GetDataBytes() { return __p.__vector_as_span(12); }
#else
public ArraySegment<byte>? GetDataBytes() { return __p.__vector_as_arraysegment(12); }
#endif
public byte[] GetDataArray() { return __p.__vector_as_array<byte>(12); }
public static Offset<File> CreateFile(FlatBufferBuilder builder,
byte unused = 0,
Compression compression_type = Compression.NONE,
byte unk1 = 0,
ulong decompressed_size = 0,
VectorOffset dataOffset = default(VectorOffset))
{
builder.StartObject(5);
File.AddDecompressedSize(builder, decompressed_size);
File.AddData(builder, dataOffset);
File.AddUnk1(builder, unk1);
File.AddCompressionType(builder, compression_type);
File.AddUnused(builder, unused);
return File.EndFile(builder);
}
public static void StartFile(FlatBufferBuilder builder) { builder.StartObject(5); }
public static void AddUnused(FlatBufferBuilder builder, byte unused) { builder.AddByte(0, unused, 0); }
public static void AddCompressionType(FlatBufferBuilder builder, Compression compressionType) { builder.AddByte(1, (byte)compressionType, 255); }
public static void AddUnk1(FlatBufferBuilder builder, byte unk1) { builder.AddByte(2, unk1, 0); }
public static void AddDecompressedSize(FlatBufferBuilder builder, ulong decompressedSize) { builder.AddUlong(3, decompressedSize, 0); }
public static void AddData(FlatBufferBuilder builder, VectorOffset dataOffset) { builder.AddOffset(4, dataOffset.Value, 0); }
public static VectorOffset CreateDataVector(FlatBufferBuilder builder, byte[] data) { builder.StartVector(1, data.Length, 1); for (int i = data.Length - 1; i >= 0; i--) builder.AddByte(data[i]); return builder.EndVector(); }
public static VectorOffset CreateDataVectorBlock(FlatBufferBuilder builder, byte[] data) { builder.StartVector(1, data.Length, 1); builder.Add(data); return builder.EndVector(); }
public static void StartDataVector(FlatBufferBuilder builder, int numElems) { builder.StartVector(1, numElems, 1); }
public static Offset<File> EndFile(FlatBufferBuilder builder)
{
int o = builder.EndObject();
return new Offset<File>(o);
}
};
public enum Compression : byte
{
OODLE = 3,
NONE = 255,
};
}

View file

@ -0,0 +1,172 @@
using FirstPlugin.FileFormats.Hashes;
using FlatBuffers.TRPAK;
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using Toolbox.Library;
using Toolbox.Library.IO;
namespace FirstPlugin
{
internal class TRPAK : TreeNodeFile, IArchiveFile, IFileFormat
{
public static bool shownOodleError;
public bool CanAddFiles => false;
public bool CanDeleteFiles => false;
public bool CanRenameFiles => false;
public bool CanReplaceFiles => false;
public bool CanSave { get; set; } = false;
public string[] Description { get; set; } = new string[] { "tr Package" };
public string[] Extension { get; set; } = new string[] { "*.trpak" };
public string FileName { get; set; }
public string FilePath { get; set; }
public IEnumerable<ArchiveFileInfo> Files { get; set; }
public FileType FileType { get; set; } = FileType.Archive;
public IFileInfo IFileInfo { get; set; }
public Type[] Types
{
get
{
List<Type> types = new List<Type>();
return types.ToArray();
}
}
public bool AddFile(ArchiveFileInfo archiveFileInfo)
{
throw new NotImplementedException();
}
public void ClearFiles()
{
throw new NotImplementedException();
}
public bool DeleteFile(ArchiveFileInfo archiveFileInfo)
{
throw new NotImplementedException();
}
public bool Identify(Stream stream)
{
return Utils.GetExtension(FileName) == ".trpak";
}
public void Load(Stream stream)
{
GFPAKHashCache.EnsureHashCache();
Files = new List<ArchiveFileInfo>();
FlatBuffers.TRPAK.TRPAK trpak = FlatBuffers.TRPAK.TRPAK.GetRootAsTRPAK(new FlatBuffers.ByteBuffer(stream.ToBytes()));
if (trpak.FilesLength != trpak.HashesLength)
{
throw new Exception("not the same amount of Hashes and File Entries in Trpak Container");
}
for (int i = 0; i < trpak.FilesLength; i++)
{
FlatBuffers.TRPAK.File? file = trpak.Files(i);
ulong hash = trpak.Hashes(i);
if (file.HasValue)
{
ArchiveFileInfo AFI = new ArchiveFileInfo();
byte[] FileData = file.Value.GetDataArray();
if (file.Value.CompressionType == Compression.OODLE)
{
if (!shownOodleError && !System.IO.File.Exists($"{Runtime.ExecutableDir}\\oo2core_6_win64.dll"))
{
MessageBox.Show("'oo2core_6_win64.dll' not found in the executable folder! User must provide their own copy!");
shownOodleError = true;
}
byte[] FileDatadecompressed = Toolbox.Library.Compression.Oodle.Decompress(FileData, (long)file.Value.DecompressedSize);
FileData = FileDatadecompressed;
}
AFI.FileData = FileData;
AFI.Name = GetName(hash, FileData);
AFI.FileName = AFI.Name;
((List<ArchiveFileInfo>)Files).Add(AFI);
}
}
GFPAKHashCache.WriteCache();
}
public void Save(Stream stream)
{
throw new NotImplementedException();
}
public void Unload()
{
foreach (var file in Files)
{
if (file.FileFormat != null)
file.FileFormat.Unload();
file.FileData = null;
}
((List<ArchiveFileInfo>)Files).Clear();
GC.SuppressFinalize(this);
}
private string FindMatch(byte[] f, string FileName)
{
foreach (IFileFormat fileFormat in FileManager.GetFileFormats())
{
fileFormat.FileName = FileName;
if (fileFormat.Identify(new MemoryStream(f)))
{
return fileFormat.Extension[0].Replace("*", "");
}
}
return "";
}
//For BNTX, BNSH, etc
private string GetBinaryHeaderName(byte[] Data)
{
using (var reader = new FileReader(Data))
{
reader.Seek(0x10, SeekOrigin.Begin);
uint NameOffset = reader.ReadUInt32();
reader.Seek(NameOffset, SeekOrigin.Begin);
return reader.ReadString(Syroot.BinaryData.BinaryStringFormat.ZeroTerminated);
}
}
private string GetName(ulong fileHash, byte[] Data)
{
string fileHashName = GFPAKHashCache.GetHashName(fileHash) ?? "";
string ext = FindMatch(Data, fileHashName);
if (ext == ".bntx" || ext == ".bfres" || ext == ".bnsh" || ext == ".bfsha")
{
string fileName = GetBinaryHeaderName(Data);
//Check for matches for shaders
if (ext == ".bnsh")
{
if (FNV64A1.Calculate($"{fileName}.bnsh_fsh") == fileHash)
fileName = $"{fileName}.bnsh_fsh";
else if (FNV64A1.Calculate($"{fileName}.bnsh_vsh") == fileHash)
fileName = $"{fileName}.bnsh_vsh";
}
else
fileName = $"{fileName}{ext}";
return $"{fileName}";
}
else
{
if (fileHashName != "")
{
return $"{fileHashName}{ext}";
}
else
return $"{fileHash.ToString("X")}{ext}";
}
}
}
}

View file

@ -226,6 +226,8 @@
<Compile Include="FileFormats\Archives\QuickAccess\QuickAccessFile.cs" />
<Compile Include="FileFormats\Archives\QuickAccess\QuickAccessFolder.cs" />
<Compile Include="FileFormats\Archives\RARC\RARC_Parser.cs" />
<Compile Include="FileFormats\Archives\TRPAK\Flatbuffers\TRPAK.cs" />
<Compile Include="FileFormats\Archives\TRPAK\TRPAK.cs" />
<Compile Include="FileFormats\Archives\WTA.cs" />
<Compile Include="FileFormats\Audio\BARS\BarsFile.cs" />
<Compile Include="FileFormats\BCH\BCH.cs" />

View file

@ -460,6 +460,7 @@ namespace FirstPlugin
Formats.Add(typeof(MTXT));
Formats.Add(typeof(NKN));
Formats.Add(typeof(MetroidDreadLibrary.BSMAT));
Formats.Add(typeof(TRPAK));
//Formats.Add(typeof(XLINK_FILE));