This commit is contained in:
2025-05-13 02:59:41 +03:00
parent 1260390353
commit 5f03062f42
18 changed files with 1249 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
/bin
/obj
/.vs
+102
View File
@@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace FireTools {
public static class Dds {
public struct DdsMagic
{
public uint fourCC;
public bool IsValid => fourCC == Make().fourCC;
public static DdsMagic Make()
{
return new DdsMagic {fourCC = 0x20534444};
}
}
[Flags]
public enum DDS_PIXELFORMAT_FLAGS : uint {
DDS_FOURCC = 0x00000004, // DDPF_FOURCC
DDS_RGB = 0x00000040, // DDPF_RGB
DDS_RGBA = 0x00000041, // DDPF_RGB | DDPF_ALPHAPIXELS
DDS_LUMINANCE = 0x00020000, // DDPF_LUMINANCE
DDS_LUMINANCEA = 0x00020001, // DDPF_LUMINANCE | DDPF_ALPHAPIXELS
DDS_ALPHAPIXELS = 0x00000001, // DDPF_ALPHAPIXELS
DDS_ALPHA = 0x00000002, // DDPF_ALPHA
DDS_PAL8 = 0x00000020, // DDPF_PALETTEINDEXED8
DDS_PAL8A = 0x00000021, // DDPF_PALETTEINDEXED8 | DDPF_ALPHAPIXELS
DDS_BUMPDUDV = 0x00080000 // DDPF_BUMPDUDV
}
public struct DDS_PIXELFORMAT {
public uint size;
public DDS_PIXELFORMAT_FLAGS flags;
public FourCC fourCC;
public uint RGBBitCount;
public uint RBitMask;
public uint GBitMask;
public uint BBitMask;
public uint ABitMask;
public static DDS_PIXELFORMAT make(DDS_PIXELFORMAT_FLAGS flags,
FourCC fourCC, uint RGBBitCount, uint RBitMask,
uint GBitMask, uint BBitMask, uint ABitMask) {
DDS_PIXELFORMAT result = new DDS_PIXELFORMAT();
result.size = (uint)Marshal.SizeOf<DDS_PIXELFORMAT>();
result.flags = flags;
result.fourCC = fourCC;
result.RGBBitCount = RGBBitCount;
result.RBitMask = RBitMask;
result.GBitMask = GBitMask;
result.BBitMask = BBitMask;
result.ABitMask = ABitMask;
return result;
}
public static DDS_PIXELFORMAT DXT1 => make(DDS_PIXELFORMAT_FLAGS.DDS_FOURCC, FourCC.Make('D', 'X', 'T', '1'), 0, 0, 0, 0, 0);
public static DDS_PIXELFORMAT BC4_UNORM => make(DDS_PIXELFORMAT_FLAGS.DDS_FOURCC, FourCC.Make('B', 'C', '4', 'U'), 0, 0, 0, 0, 0);
}
[Flags]
public enum DdsHeaderFlags : uint
{
DDSD_CAPS = 0x1,
DDSD_HEIGHT = 0x2,
DDSD_WIDTH = 0x4,
DDSD_PITCH = 0x8,
DDSD_PIXELFORMAT = 0x1000,
DDSD_MIPMAPCOUNT = 0x20000,
DDSD_LINEARSIZE = 0x80000,
DDSD_DEPTH = 0x800000
}
public struct DDS_HEADER {
public uint size;
public DdsHeaderFlags flags;
public uint height;
public uint width;
public uint pitchOrLinearSize;
public uint depth; // only if DDS_HEADER_FLAGS_VOLUME is set in flags
public uint mipMapCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)]
public uint[] reserved1;
public DDS_PIXELFORMAT ddspf;
public uint caps;
public uint caps2;
public uint caps3;
public uint caps4;
public uint reserved2;
}
}
}
+16
View File
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
</Project>
+25
View File
@@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30907.101
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FireTools", "FireTools.csproj", "{6B9F9F80-66A0-4FFB-A04F-3825E7FC9AE4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{6B9F9F80-66A0-4FFB-A04F-3825E7FC9AE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6B9F9F80-66A0-4FFB-A04F-3825E7FC9AE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6B9F9F80-66A0-4FFB-A04F-3825E7FC9AE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6B9F9F80-66A0-4FFB-A04F-3825E7FC9AE4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {280C51FA-6E1E-44CC-8D48-808908716837}
EndGlobalSection
EndGlobal
+2
View File
@@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=Phyre/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
+35
View File
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace FireTools {
[StructLayout(LayoutKind.Sequential)]
public struct FourCC {
[MarshalAs(UnmanagedType.U1)]
public char c0;
[MarshalAs(UnmanagedType.U1)]
public char c1;
[MarshalAs(UnmanagedType.U1)]
public char c2;
[MarshalAs(UnmanagedType.U1)]
public char c3;
public uint Value
{
get
{
return (uint)(c0 | c1 << 8 | c2 << 16 | c3 << 24);
}
}
public override string ToString()
{
return new string(new char[]{c0, c1, c2, c3});
}
public static FourCC Make(char ch0, char ch1, char ch2, char ch3) {
return new FourCC { c0 = ch0, c1 = ch1, c2 = ch2, c3 = ch3 };
}
}
}
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Sergey Serb
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+24
View File
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace FireTools {
public static class MemoryUtils {
public static byte[] StructToBytes<T>(T value) where T : struct
{
byte[] result = new byte[Marshal.SizeOf<T>()];
var handle = GCHandle.Alloc(result, GCHandleType.Pinned);
Marshal.StructureToPtr(value, handle.AddrOfPinnedObject(), false);
handle.Free();
return result;
}
public static T BytesToStruct<T>(byte[] data) where T : struct {
var handle = GCHandle.Alloc(data, GCHandleType.Pinned);
var result = Marshal.PtrToStructure<T>( handle.AddrOfPinnedObject());
handle.Free();
return result;
}
}
}
+266
View File
@@ -0,0 +1,266 @@
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];
}
}
}
}
+25
View File
@@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30804.86
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetsProcessing", "AssetsProcessing.csproj", "{9F8773F6-C1A2-48C4-8468-06A099828A9B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9F8773F6-C1A2-48C4-8468-06A099828A9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9F8773F6-C1A2-48C4-8468-06A099828A9B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9F8773F6-C1A2-48C4-8468-06A099828A9B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9F8773F6-C1A2-48C4-8468-06A099828A9B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6F7FE027-76D1-4565-B7D6-4F24B4CEA99C}
EndGlobalSection
EndGlobal
+136
View File
@@ -0,0 +1,136 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
namespace FireTools.Phyre {
public class Archive {
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BaseHeader {
public FourCC magic;
public uint size;
public uint objectsTableSize;
public FourCC platformId;
public uint instanceListCount;
public uint arrayFixupSize;
public uint arrayFixupCount;
public uint pointerFixupSize;
public uint pointerFixupCount;
public uint pointerArrayFixupSize;
public uint pointerArrayFixupCount;
public uint pointersInArraysCount;
public uint userFixupCount;
public uint userFixupDataSize;
public uint totalDataSize;
public uint headerClassInstanceCount;
public uint headerClassChildCount;
public uint physicsEngineID;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct DX11Header {
public BaseHeader baseHeader;
public uint indexBufferSize;
public uint vertexBufferSize;
public uint maxTextureMipBufferSize;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct PInstanceListHeader
{
public uint m_classID; // The ID of the class stored in this instance list.
public uint m_count; // The number of instances of the class stored in this instance list.
public uint m_size; // The size of the data for this instance list in bytes. This should be equal to m_objectsSize + m_arraysSize;
public uint m_objectsSize; // The size of the data for the objects in this instance list in bytes. Also includes the padding to make sure that the array data is correctly aligned.
public uint m_arraysSize; // The size of the non-object array data in this instance list in bytes
public uint m_pointersInArraysCount; // The number of pointers in pointer arrays.
public uint m_arrayFixupCount; // The count of array fixups required.
public uint m_pointerFixupCount; // The count of pointer fixups required.
public uint m_pointerArrayFixupCount; // The count of pointer array fixups required.
};
public static PInstanceListHeader ReadInstanceListHeader(Stream data)
{
return data.ReadStruct<PInstanceListHeader>();
}
public static object ReadHeader(Stream data)
{
var baseHeader = data.ReadStruct<BaseHeader>();
var baseSize = Marshal.SizeOf<BaseHeader>();
if (baseHeader.size < baseSize)
{
throw new Exception("Unexpected header size");
}
if (baseHeader.size == baseSize)
{
return baseHeader;
}
if (baseHeader.platformId.Value == 0x44583131)
{
//dx11 platform
var dx11Size = Marshal.SizeOf<DX11Header>();
var extraBytesCount = dx11Size - baseSize;
var extraBytes = data.ReadBytes(extraBytesCount);
var baseBytes = MemoryUtils.StructToBytes(baseHeader);
List<byte> dx11Bytes = new List<byte>();
dx11Bytes.AddRange(baseBytes);
dx11Bytes.AddRange(extraBytes);
return MemoryUtils.BytesToStruct<DX11Header>(dx11Bytes.ToArray());
}
throw new Exception("Unknown platform");
}
public static ObjectsTable ReadObjectsTable( Stream data)
{
/*var baseArcHeader = archiveHeader is BaseHeader
? (BaseHeader) archiveHeader
: ((DX11Header) archiveHeader).baseHeader;*/
ObjectsTable result = new ObjectsTable();
result.header = data.ReadStruct<ObjectsTable.Header>();
result.typeDescriptors = data.ReadStructArray<ObjectsTable.TypeDescriptor>(result.header.typeCount).ToArray();
result.classDescriptors = data.ReadStructArray<ObjectsTable.ClassDescriptor>(result.header.classCount).ToArray();
result.classMemberDescriptors = data.ReadStructArray<ObjectsTable.ClassMemberDescriptor>(result.header.classMemberCount).ToArray();
result.stringTable = data.ReadBytes((int)result.header.stringTableSize);
var s0 = Marshal.SizeOf<ObjectsTable.Header>();
var s1 = Marshal.SizeOf<ObjectsTable.TypeDescriptor>() * result.header.typeCount;
var s2 = Marshal.SizeOf<ObjectsTable.ClassDescriptor>() * result.header.classCount;
var s3 = Marshal.SizeOf<ObjectsTable.ClassMemberDescriptor>() * result.header.classMemberCount;
var s4 = result.header.stringTableSize;
var paddingSize = result.header.size - (s0 + s1 + s2 + s3 + s4);
if (paddingSize != 0)
{
data.ReadBytes((int)paddingSize);
}
return result;
}
/*SetBackColor(0x0000ff);
Header header;
SetBackColor(0x00ff00);
PackedNamespaceHeader namespaceHeader;
SetBackColor(0xff0000);
PPackedType namespaceTypes[namespaceHeader.m_typeCount];
PPackedClassDescriptor namespaceClassDescriptors[namespaceHeader.m_classCount];
PPackedDataMember namespaceDataMembers[namespaceHeader.m_classDataMemberCount];
CHAR namespaceStringTable[namespaceHeader.m_stringTableSize];*/
//PChar* string table
//PUInt8 * default buffers
}
}
+18
View File
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace FireTools.Phyre {
public class FieldInstance
{
public ObjectsTable.ClassMemberDescriptor descriptor;
public object value;
}
public class ClassInstance
{
public ClassInstance parentInstance;
public ObjectsTable.Class thisClass;
}
}
+180
View File
@@ -0,0 +1,180 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace FireTools.Phyre {
public class ObjectsTable {
public struct Header {
public uint magic;
public uint size;
public uint typeCount;
public uint classCount;
public uint classMemberCount;
public uint stringTableSize;
public uint someBufferCount;
public uint someBufferSize;
}
public struct TypeDescriptor {
public uint nameOffset;
}
public struct ClassDescriptor {
public int baseClassID; // 0 implies not set, positive implies local, negative implies global.
public uint sizeInBytesAndAlignment; // The size of this class in bytes with the alignment shift in the top 4 bits.
public uint nameOffset; // The offset to the name of this class in the string table.
public uint classDataMemberCount; // The number of data members in this class.
public uint offsetFromParent; // The offset of the start of this class from the start of the parent class.
public uint offsetToBase; // The offset of the start of this class from the start of the PBase class.
public uint offsetToBaseInAllocatedBlock; // The offset of the start of the PBase class in a block allocated for this class.
public uint flags; // The flags for this class.
public uint defaultBufferOffset; // 0 implies not saved. Subtract one to get the correct default buffer offset if non-zero.
}
public struct ClassMemberDescriptor {
public uint nameOffset; // The offset to the name of this member in the string table.
public uint typeID; // The type of this member
public uint valueOffset; // The offset to the value of this member in the class.
public uint sizeInBytes; // The size of this member in bytes.
public uint flags; // The flags for this member.
public uint fixedArraySize; // If non zero, this represents the size of a fixed size array.
}
public Header header;
public TypeDescriptor[] typeDescriptors;
public ClassDescriptor[] classDescriptors;
public ClassMemberDescriptor[] classMemberDescriptors;
public byte[] stringTable;
public class ClassMember
{
public Type fieldType;
public string name;
public uint offset;
public uint size;
public uint flags;
public uint fixedArraySize;
public override string ToString()
{
return name;
}
}
public class Type
{
public string name;
public override string ToString()
{
return name;
}
}
public class Class : Type
{
public Class baseClass;
public List<ClassMember> members = new List<ClassMember>();
public override string ToString()
{
var baseStr = baseClass == null ? "" : $" Base: {baseClass.name}";
return $"Name: {name} Members: {members.Count} Local members size: {members.Sum(v=>v.size)} {baseStr}";
}
}
public class ExternalClass : Class {
public ExternalClass()
{
name = "ExternalClass";
}
}
public Type[] Types;
public Class[] Classes;
public void ParseTypes()
{
Types = new Type[typeDescriptors.Length];
for (uint i = 0; i < Types.Length; ++i) {
Types[i] = new Type();
Types[i].name = ReadString((int)typeDescriptors[i].nameOffset);
}
}
public void ParseClasses() {
Classes = new Class[classDescriptors.Length];
for (uint m = 0; m < Classes.Length; ++m)
{
Classes[m] = new Class();
}
int memberId = 0;
for (uint i = 0; i < Classes.Length; ++i)
{
var c = classDescriptors[i];
var cls = Classes[i];
cls.name = ReadString((int) c.nameOffset);
if (c.baseClassID < 0)
{
cls.baseClass = new ExternalClass();
}else if (c.baseClassID > 0)
{
cls.baseClass = Classes[c.baseClassID - 1 ];
}
for (uint m = 0; m < c.classDataMemberCount; ++m)
{
var d = classMemberDescriptors[memberId++];
ClassMember mem = new ClassMember();
mem.size = d.sizeInBytes;
mem.fixedArraySize = d.fixedArraySize;
mem.flags = d.flags;
mem.offset = d.valueOffset;
mem.name = ReadString((int)d.nameOffset);
Console.WriteLine("Member type ID: " + d.typeID);
if (d.typeID >= Types.Length) {
mem.fieldType = Classes[(int)d.typeID - Types.Length - 1];
} else {
mem.fieldType = Types[(int)d.typeID];
}
cls.members.Add(mem);
}
}
}
public void Unpack()
{
ParseTypes();
ParseClasses();
}
public string ReadString(int offset)
{
var handle = GCHandle.Alloc(stringTable, GCHandleType.Pinned);
var result = Marshal.PtrToStringAnsi(handle.AddrOfPinnedObject() + offset);
handle.Free();
return result;
}
}
}
+225
View File
@@ -0,0 +1,225 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Mime;
using System.Runtime.InteropServices;
using System.Text;
using FireTools.Phyre;
namespace FireTools {
class Program {
static void PrintPak(PakFileInput pak)
{
Console.WriteLine("Path: " + pak.sourceName);
Console.WriteLine("Page count: " + pak.pages.Length);
for (int i = 0; i < pak.pages.Length; ++i)
{
var page = pak.pages[i];
Console.WriteLine(" Files count: " + page.FilesCount);
Console.WriteLine(" Total files size: " + page.TotalFilesSize);
for (int j = 0; j < page.files.Length; ++j)
{
var file = page.files[j];
Console.WriteLine($" File size: {file.header.fileSize}\tname: {file.name} \tclass name: { file.className}");
}
}
}
static byte[] UnswizzleTexture(byte[] data, uint width, uint height, uint mipCount)
{
List<byte> result = new List<byte>();
uint offset = 0;
for (uint i = 0; i < mipCount; ++i)
{
var rowPitch = width * 2;
var slicePitch = (width * height) / 2;
if (width < 4 || height < 4)
{
rowPitch = 8;
slicePitch = 8;
}
var mipDataSegment = new ArraySegment<byte>(data, (int) offset, (int) slicePitch);
var mipData = mipDataSegment.ToArray();
var unswizzledMipData = TextureSwizzling.UnSwizzle(mipData, (int)width, (int)height, 8);
result.AddRange(unswizzledMipData);
width /= 2;
height /= 2;
offset += slicePitch;
}
return result.ToArray();
}
static void ExportDdsTexture(Stream stm, VRamDescFile texture)
{
if (texture.header.width == texture.header.height)
{
return;
}
Dds.DDS_HEADER header = new Dds.DDS_HEADER();
header.flags = Dds.DdsHeaderFlags.DDSD_CAPS | Dds.DdsHeaderFlags.DDSD_HEIGHT |
Dds.DdsHeaderFlags.DDSD_WIDTH | Dds.DdsHeaderFlags.DDSD_PIXELFORMAT |
Dds.DdsHeaderFlags.DDSD_MIPMAPCOUNT | Dds.DdsHeaderFlags.DDSD_LINEARSIZE;
var realFilePath = Path.GetDirectoryName(texture.fileName);
var fileName = Path.GetFileName(realFilePath);
string outFileName = Path.Join("D:\\TextureExport",
fileName +
$".{texture.header.width}_{texture.header.height}_unswizzled_page{texture.file.page.id}_file{texture.file.id}_fmt{texture.header.maybeFormat}" +
".dds");
Console.WriteLine("Exporting texture " + outFileName);
var rawData = texture.ReadRawData(stm);
var unswizzledData = UnswizzleTexture(rawData, texture.header.width, texture.header.height, texture.header.mipCount);
if (texture.header.maybeFormat == 80) {
header.ddspf = Dds.DDS_PIXELFORMAT.BC4_UNORM;
} else if (texture.header.maybeFormat == 71 || (texture.header.maybeFormat == 72)) {
header.ddspf = Dds.DDS_PIXELFORMAT.DXT1;
} else if (texture.header.maybeFormat == 35) {
header.ddspf = Dds.DDS_PIXELFORMAT.DXT1;
unswizzledData = rawData;
} else {
header.ddspf = Dds.DDS_PIXELFORMAT.DXT1;
Console.WriteLine($"unknown format {texture.header.maybeFormat} for texture {texture.fileName}");
//return;
}
header.width = texture.header.width;
header.height = texture.header.height;
header.pitchOrLinearSize = (header.width * header.height) / 2;
header.depth = 1;
header.mipMapCount = texture.header.mipCount;
header.reserved1 = new uint[11];
header.caps = 0x1000; //DDSCAPS_TEXTURE
header.size = (uint)Marshal.SizeOf<Dds.DDS_HEADER>();
var ofs = File.OpenWrite(outFileName);
ofs.WriteStruct(Dds.DdsMagic.Make());
ofs.WriteStruct(header);
ofs.Write(unswizzledData);
ofs.Close();
Console.WriteLine("Exporting texture done");
}
static void ExportTextures()
{
var path = @".pak";
var stm = File.OpenRead(path);
var p = new PakFileInput(stm, path);
for (int i = 0; i < p.pages.Length; ++i)
{
var page = p.pages[i];
for(int j = 0; j < page.files.Length; ++j)
{
if (page.files[j].className == "VRAM_DESC")
{
var f = VRamDescFile.Read(stm, page.files[j]);
ExportDdsTexture(stm, f);
Console.WriteLine("Ololo");
}
}
}
}
class ClassPrinter
{
public List<Phyre.ObjectsTable.Class> printedClasses = new List<ObjectsTable.Class>();
public void Print(Phyre.ObjectsTable.Class c)
{
string ident = " ";
if (printedClasses.Contains(c))
{
return;
}
if (c.baseClass != null)
{
Print(c.baseClass);
}
Console.Write($"class {c.name}");
if (c.baseClass != null)
{
Console.Write($" : {c.baseClass.name}");
}
Console.WriteLine($"{{");
Console.WriteLine($"public:");
foreach (var classMember in c.members)
{
Console.WriteLine($"{ident}{classMember.fieldType.name} {classMember.name}; //is class: {classMember.fieldType is ObjectsTable.Class}");
}
Console.WriteLine($"}};");
Console.WriteLine($"");
printedClasses.Add(c);
}
}
static void Main(string[] args)
{
var stm = File.OpenRead(@"D:\tex.dds.phyre");
var header = Phyre.Archive.ReadHeader(stm);
var objectsTable = Phyre.Archive.ReadObjectsTable(stm);
objectsTable.Unpack();
ClassPrinter printer = new ClassPrinter();
foreach (var c in objectsTable.Classes)
{
printer.Print(c);
}
var instanceListHeaders = new List<Archive.PInstanceListHeader>();
var instances = ((Archive.DX11Header) header).baseHeader.instanceListCount;
for (uint i = 0; i < instances; ++i)
{
var h = Phyre.Archive.ReadInstanceListHeader(stm);
instanceListHeaders.Add(h);
Console.WriteLine($"Objects count: {h.m_count} Type: {objectsTable.Classes[h.m_classID - 1]} {h.m_objectsSize}");
}
var mems = objectsTable.classDescriptors.Sum(v => v.classDataMemberCount);
Console.WriteLine();
}
}
}
+1
View File
@@ -0,0 +1 @@
# FireTools
+35
View File
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace FireTools {
public static class SpaceFillingCurve {
public static int Morton2D(int t, int sx, int sy) {
int num1;
int num2 = num1 = 1;
int num3 = t;
int num4 = sx;
int num5 = sy;
int num6 = 0;
int num7 = 0;
while (num4 > 1 || num5 > 1) {
if (num4 > 1) {
num6 += num2 * (num3 & 1);
num3 >>= 1;
num2 *= 2;
num4 >>= 1;
}
if (num5 > 1) {
num7 += num1 * (num3 & 1);
num3 >>= 1;
num1 *= 2;
num5 >>= 1;
}
}
return num7 * sx + num6;
}
}
}
+85
View File
@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
namespace FireTools {
public static class StreamExtensions {
public static byte[] ReadBytes(this Stream stm, int size) {
byte[] bytes = new byte[size];
if (stm.Read(bytes, 0, size) != size) {
throw new Exception("Failed to read stream");
}
return bytes;
}
public static void WriteStruct<T>(this Stream stm, T value) where T : struct
{
var bytes = MemoryUtils.StructToBytes(value);
stm.Write(bytes, 0, bytes.Length);
}
public static T ReadStruct<T>(this Stream stm) where T : struct {
byte[] bytes = new byte[Marshal.SizeOf<T>()];
if (stm.Read(bytes, 0, Marshal.SizeOf<T>()) != bytes.Length) {
throw new Exception("Failed to read stream");
}
var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
var result = Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());
handle.Free();
return result;
}
public static List<T> ReadStructArray<T>(this Stream stm, uint count) where T : struct {
List<T> result = new List<T>((int)count);
for (int i = 0; i < count; ++i)
{
result.Add(stm.ReadStruct<T>());
}
return result;
}
public static uint ReadUInt(this Stream stm) {
return ReadStruct<uint>(stm);
}
public static int ReadInt(this Stream stm) {
return ReadStruct<int>(stm);
}
public static ushort ReadUShort(this Stream stm) {
return ReadStruct<ushort>(stm);
}
public static short ReadShort(this Stream stm) {
return ReadStruct<short>(stm);
}
public static string ReadString(this Stream stm) {
var result = new List<byte>();
while (true) {
int c = stm.ReadByte();
if (c < 0) {
throw new Exception("End of file");
}
if (c == 0) {
break;
}
result.Add((byte)(c & 0xff));
}
return Encoding.ASCII.GetString(result.ToArray());
}
public static void ReadValue(this Stream stm, out string result) {
result = ReadString(stm);
}
public static void ReadValue<T>(this Stream stm, out T result) where T : struct {
result = ReadStruct<T>(stm);
}
}
}
+50
View File
@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace FireTools {
public static class TextureSwizzling {
public static byte[] Swizzle(byte[] data, int width, int height, int blockSize) {
return DoSwizzle(data, width, height, blockSize, false);
}
public static byte[] UnSwizzle(byte[] data, int width, int height, int blockSize) {
return DoSwizzle(data, width, height, blockSize, true);
}
private static byte[] DoSwizzle(byte[] data, int width, int height, int blockSize, bool unswizzle) {
var processed = new byte[data.Length];
var heightTexels = height / 4;
var heightTexelsAligned = (heightTexels + 7) / 8;
int widthTexels = width / 4;
var widthTexelsAligned = (widthTexels + 7) / 8;
var dataIndex = 0;
for (int y = 0; y < heightTexelsAligned; ++y) {
for (int x = 0; x < widthTexelsAligned; ++x) {
for (int t = 0; t < 64; ++t) {
int pixelIndex = SpaceFillingCurve.Morton2D(t, 8, 8);
int num8 = pixelIndex / 8;
int num9 = pixelIndex % 8;
var yOffset = (y * 8) + num8;
var xOffset = (x * 8) + num9;
if (xOffset < widthTexels && yOffset < heightTexels) {
var destPixelIndex = yOffset * widthTexels + xOffset;
int destIndex = blockSize * destPixelIndex;
if (unswizzle)
Array.Copy(data, dataIndex, processed, destIndex, blockSize);
else
Array.Copy(data, destIndex, processed, dataIndex, blockSize);
}
dataIndex += blockSize;
}
}
}
return processed;
}
}
}