Template
*
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
/bin
|
||||||
|
/obj
|
||||||
|
/.vs
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
@@ -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
|
||||||
@@ -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>
|
||||||
@@ -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 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
# FireTools
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user