Files
2025-05-13 02:59:41 +03:00

267 lines
8.4 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace FireTools {
namespace Native
{
public struct Header {
public struct PageDesc {
public uint offset;
public uint size;
public uint index;
};
public struct SomeRecordTableDesc {
public uint unk3;
public uint someRecordsOffset;
public uint someRecordsCount;
};
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BaseInfo {
public ushort magic;
public ushort someType;
public uint totalHeaderSize;
public uint loginTableFilePageId;
public uint loginTableFileOffset;
public uint pagesCount;
public uint pageTableOffset;
public uint someConstantValue; // always == 8
public uint someRecordTableDescOffset; // always == 8
public uint someAttachedDataSize; //bottom of pak file (after last page)
public uint vramDescTableFilePageId;
public uint vramDescTableFileOffset;
}
public BaseInfo baseInfo;
public PageDesc[] pageTable;
public SomeRecordTableDesc someRecordTableDesc;
public byte[] someZeroData;
public struct SomeRecord {
public ushort w0;
public ushort w1;
public uint v1;
};
public SomeRecord[] someRecords;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct FileHeader {
public uint fileNameOffset;
public uint alwaysZeroValue;
public uint fileClassNameOffset;
public uint u3;
public uint fileSize;
public uint dummy0; //0x12345
public uint dummy1; //0x12345
public uint dummy2; //0x12345
}
}
public class Page
{
public PakFileInput pakFile;
public uint id;
public class File
{
public PakFileInput Pak => page.pakFile;
public Page page;
public uint id;
public Native.FileHeader header;
public uint Size => header.fileSize;
public uint LocalOffset => page.fileTable[id].fileOffset;
public uint GlobalOffset => LocalOffset + page.Offset;
public uint LocalDataOffset => (uint)(page.fileTable[id].fileOffset + Marshal.SizeOf<Native.FileHeader>());
public uint GlobalDataOffset => LocalDataOffset + page.Offset;
public string name;
public string className;
}
public int FilesCount => baseInfo.filesTableHeader.filesCount;
public uint Offset => pakFile.header.pageTable[id].offset;
public uint Size => pakFile.header.pageTable[id].size;
public long TotalFilesSize {
get {
return files.Select(v => (long)v.header.fileSize).Sum();
}
}
public Native.Header.PageDesc desc;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BaseInfo {
public struct FilesTableHeader {
public ushort unk0;
public ushort filesCount;
}
public uint magic0; //0xDEADBEEF
public uint magic1; //0xDEADBEEF
public uint unk0;
public uint fullPageSize;
public FilesTableHeader filesTableHeader;
}
public BaseInfo baseInfo;
public struct FileTableRecord {
public uint fileNameOffset;
public uint alwaysZeroValue;
public uint fileOffset;
public uint dummy;
}
public FileTableRecord[] fileTable;
public File[] files;
}
public class VRamDescFile {
public struct Header {
public uint u0;
public uint u1;
public uint maybeOffset;
public uint u3;
public uint maybeLength;
public ushort h0;
public ushort h1;
public ushort h2;
public ushort h3;
public uint maybeFormat;
public ushort h6;
public ushort h7;
public uint mipCount;
public uint width;
public uint height;
public uint u12;
public uint u13;
public uint u14;
public uint u15;
}
public Header header;
public string fileName;
public Page.File file;
public static VRamDescFile Read(Stream stream, Page.File file) {
var result = new VRamDescFile();
result.file = file;
stream.Seek(file.GlobalDataOffset, SeekOrigin.Begin);
stream.ReadValue(out result.header);
result.fileName = stream.ReadString();
return result;
}
public byte[] ReadRawData(Stream stm)
{
stm.Seek((long)stm.Length - file.Pak.RawDataSize + header.maybeOffset, SeekOrigin.Begin);
return stm.ReadBytes((int)header.maybeLength);
}
}
public class PakFileInput {
public string sourceName;
public long realFullSize;
public Native.Header header;
public Page[] pages;
public uint RawDataSize => header.baseInfo.someAttachedDataSize;
public int TotalFilesCount {
get {
return pages.Select(v => v.files.Length).Sum();
}
}
public PakFileInput(Stream stm, string sourceName) {
this.sourceName = sourceName;
realFullSize = stm.Length;
stm.Seek(0, SeekOrigin.Begin);
ParseHeader(stm);
pages = new Page[header.baseInfo.pagesCount];
for (uint i = 0; i < pages.Length; ++i) {
pages[i] = ParsePage(stm, i);
}
}
private Page ParsePage(Stream stm, uint id) {
Page result = new Page();
result.id = id;
result.pakFile = this;
var pageDesc = header.pageTable[id];
result.desc = pageDesc;
stm.Seek(pageDesc.offset, SeekOrigin.Begin);
stm.ReadValue(out result.baseInfo);
result.fileTable = new Page.FileTableRecord[result.FilesCount];
for (uint i = 0; i < result.FilesCount; ++i) {
stm.ReadValue(out result.fileTable[i]);
}
result.files = new Page.File[result.FilesCount];
for (uint i = 0; i < result.FilesCount; ++i) {
result.files[i] = new Page.File();
result.files[i].id = i;
result.files[i].page = result;
stm.Seek(pageDesc.offset + result.fileTable[i].fileOffset, SeekOrigin.Begin);
stm.ReadValue(out result.files[i].header);
stm.Seek(pageDesc.offset + result.files[i].header.fileNameOffset, SeekOrigin.Begin);
result.files[i].name = stm.ReadString();
stm.Seek(pageDesc.offset + result.files[i].header.fileClassNameOffset, SeekOrigin.Begin);
result.files[i].className = stm.ReadString();
}
return result;
}
private void ParseHeader(Stream stm) {
stm.ReadValue(out header.baseInfo);
header.pageTable = new Native.Header.PageDesc[header.baseInfo.pagesCount];
for (uint i = 0; i < header.baseInfo.pagesCount; ++i) {
stm.ReadValue(out header.pageTable[i]);
}
stm.Seek(header.baseInfo.someRecordTableDescOffset, SeekOrigin.Begin);
stm.ReadValue(out header.someRecordTableDesc);
var someZeroDataSize = (int)(header.someRecordTableDesc.someRecordsOffset - stm.Position);
header.someZeroData = stm.ReadBytes(someZeroDataSize);
if (header.someRecordTableDesc.someRecordsCount != 0) {
header.someRecords = new Native.Header.SomeRecord[header.someRecordTableDesc.someRecordsCount];
for (uint i = 0; i < header.someRecords.Length; ++i) {
stm.ReadValue(out header.someRecords[i]);
}
} else {
header.someRecords = new Native.Header.SomeRecord[0];
}
}
}
}