Switch-Toolbox/File_Format_Library/FileFormats/Archives/APAK.cs
KillzXGaming f090f7e757 A bunch of bug fixes and some features.
Add apak rebuilding.
Add support for joints for .mod.
Add support for part property panes attached to the heiarchy tree.
Fix BFLYT TXT1 text encoding for wii u to use big endian unicode.
Fix a bug with textures opening in quick access menu and bntx for gfpak.
Fix tex2 files being used for the bfres material editor which caused a crash.
Fix WTA support for decompressed files.
Auto search file names for WTA files if possible.
Fix g1t decoding images at the wrong position depeding on the header size being incorrect.
2020-01-29 16:08:29 -05:00

239 lines
8.1 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Toolbox;
using System.Windows.Forms;
using Toolbox.Library;
using Toolbox.Library.IO;
using Toolbox.Library.Forms;
using System.Linq;
namespace FirstPlugin
{
public class APAK : IFileFormat, IArchiveFile
{
public FileType FileType { get; set; } = FileType.Archive;
public bool CanSave { get; set; }
public string[] Description { get; set; } = new string[] { "APAK" };
public string[] Extension { get; set; } = new string[] { "*.apak" };
public string FileName { get; set; }
public string FilePath { get; set; }
public IFileInfo IFileInfo { get; set; }
public bool Identify(System.IO.Stream stream)
{
using (var reader = new Toolbox.Library.IO.FileReader(stream, true))
{
return reader.CheckSignature(4, "APAK");
}
}
public Type[] Types
{
get
{
List<Type> types = new List<Type>();
return types.ToArray();
}
}
public List<APAKFileInfo> files = new List<APAKFileInfo>();
public IEnumerable<ArchiveFileInfo> Files => files;
public void ClearFiles() { files.Clear(); }
public bool CanAddFiles { get; set; } = true;
public bool CanRenameFiles { get; set; } = true;
public bool CanReplaceFiles { get; set; } = true;
public bool CanDeleteFiles { get; set; } = true;
public Header ApakHeader;
public void Load(System.IO.Stream stream)
{
CanSave = true;
using (var reader = new FileReader(stream)) {
ApakHeader = new APAK.Header(reader, files);
}
}
public void Save(System.IO.Stream stream) {
using (var writer = new FileWriter(stream))
{
ApakHeader.Write(writer, files);
}
}
public class Header
{
public ushort Version { get; set; } = 5;
public bool IsBigEndian { get; set; }
public uint Unknown1 { get; set; } = 5381; //Always 5381?
public uint Unknown2 { get; set; }
public Header(FileReader reader, List<APAKFileInfo> files)
{
reader.SetByteOrder(true);
reader.ReadSignature(4, "APAK");
reader.ReadUInt16();
Version = reader.ReadUInt16();
IsBigEndian = Version == 5;
if (!IsBigEndian) {
Version = 5;
reader.SetByteOrder(false);
}
uint FileCount = reader.ReadUInt32();
Unknown1 = reader.ReadUInt32();
uint FileInfoSize = reader.ReadUInt32();
uint DataTotalSize = reader.ReadUInt32();
for (int i = 0; i < FileCount; i++)
files.Add(new APAKFileInfo(reader));
}
public void Write(FileWriter writer, List<APAKFileInfo> files)
{
writer.SetByteOrder(IsBigEndian);
writer.WriteSignature("APAK");
writer.Write((ushort)0);
writer.Write(Version);
writer.Write(files.Count);
writer.Write(Unknown1);
writer.Write(files.Count * 64);
writer.Write(uint.MaxValue);
long fileInfoPos = writer.Position;
for (int i = 0; i < files.Count; i++) {
writer.Write(files[i].Hash);
writer.Write(uint.MaxValue);
writer.Write(files[i].FileData.Length);
writer.Write(files[i].FileData.Length);
writer.Write(files[i].Alignment);
writer.Write(files[i].Unknown1);
writer.Write(files[i].Unknown2);
writer.Write(files[i].Unknown3);
writer.WriteString(files[i].FileName, 0x20);
}
//The data gets ordered by largest alignment size to lowest
//Then by the file name
var filesSorted = files.OrderByDescending(x => x.Alignment)
.ThenBy(x => x.FileName)
.ToList();
writer.Align((int)files.Max(x => x.Alignment));
long pos = writer.Position;
for (int i = 0; i < files.Count; i++)
{
var file = filesSorted[i];
int index = files.IndexOf(file);
long dataPos = writer.Position;
writer.WriteUint32Offset((fileInfoPos + 4) + index * 64);
writer.Write(file.FileData);
writer.AlignBytes((int)file.Alignment);
long dataEndPos = writer.Position;
using (writer.TemporarySeek((fileInfoPos + 12) + index * 64, SeekOrigin.Begin)) {
writer.Write((uint)(dataEndPos - dataPos));
}
}
long endPos = writer.Position;
uint dataSize = (uint)(endPos - pos);
using (writer.TemporarySeek(0x14, SeekOrigin.Begin)) {
writer.Write(dataSize);
}
//Idk what this format is doing but there's tons of padding at the end and idk why :(
//Even combining all the alignments it can still be too small
writer.AlignBytes((int)files.Sum(x => x.Alignment));
}
}
public class APAKFileInfo : ArchiveFileInfo
{
public uint Hash { get; set; } //Unsure about this. Can't find any matches?
public uint Alignment { get; set; } = 64;
//These are all 0 for files i've seen.
public uint Unknown1 { get; set; }
public uint Unknown2 { get; set; }
public uint Unknown3 { get; set; }
public APAKFileInfo(string fileName)
{
FileName = fileName;
string ext = Utils.GetExtension(FileName);
if (AlignmentTable.ContainsKey(ext))
Alignment = AlignmentTable[ext];
}
private Dictionary<string, uint> AlignmentTable = new Dictionary<string, uint>()
{
{ ".bfres", 8192 },
{ ".pspk" , 256 },
{ ".stfl" , 64 },
{ ".strc" , 64 },
{ ".layout" , 64 },
{ ".atcol" , 64 },
{ ".stsp" , 64 },
};
internal uint DataOffset;
public APAKFileInfo(FileReader reader)
{
long pos = reader.Position;
Hash = reader.ReadUInt32();
DataOffset = reader.ReadUInt32();
uint fileSize = reader.ReadUInt32();
uint totalFileSize = reader.ReadUInt32(); //File size + aligned data
Alignment = reader.ReadUInt32();
Unknown1 = reader.ReadUInt32();
Unknown2 = reader.ReadUInt32();
Unknown3 = reader.ReadUInt32();
FileName = reader.ReadString(0x20, true);
Console.WriteLine($"{Utils.GetExtension(FileName)} {Alignment}");
long endpos = reader.Position;
reader.Seek(DataOffset, System.IO.SeekOrigin.Begin);
FileData = reader.ReadBytes((int)fileSize);
reader.Seek(endpos, System.IO.SeekOrigin.Begin);
}
}
public void Unload()
{
}
public bool AddFile(ArchiveFileInfo archiveFileInfo) {
files.Add(new APAKFileInfo(archiveFileInfo.FileName)
{
FileData = archiveFileInfo.FileData,
});
return true;
}
public bool DeleteFile(ArchiveFileInfo archiveFileInfo) {
files.Remove((APAKFileInfo)archiveFileInfo);
return true;
}
}
}