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