mirror of
https://github.com/sampletext32/ParkanPlayground.git
synced 2025-05-18 19:31:17 +03:00
texm viewer
This commit is contained in:
parent
f9f89d9703
commit
08c8d07d91
10
MeshUnpacker/MeshUnpacker.csproj
Normal file
10
MeshUnpacker/MeshUnpacker.csproj
Normal file
@ -0,0 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
14
MeshUnpacker/Program.cs
Normal file
14
MeshUnpacker/Program.cs
Normal file
@ -0,0 +1,14 @@
|
||||
// See https://aka.ms/new-console-template for more information
|
||||
|
||||
Console.WriteLine("Hello, World!");
|
||||
|
||||
using var fs = new FileStream("C:\\ParkanUnpacked\\11_fr_e_brige.msh\\0_fr_e_brige.bin", FileMode.Open);
|
||||
|
||||
byte[] buffer = new byte[38];
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
fs.ReadExactly(buffer);
|
||||
|
||||
Console.WriteLine(string.Join(" ", buffer.Select(x => x.ToString("X2"))));
|
||||
}
|
@ -47,7 +47,8 @@ public class App
|
||||
serviceCollection.AddSingleton(type);
|
||||
}
|
||||
|
||||
serviceCollection.AddSingleton(new ExplorerViewModel());
|
||||
serviceCollection.AddSingleton(new NResExplorerViewModel());
|
||||
serviceCollection.AddSingleton(new TexmExplorerViewModel());
|
||||
|
||||
var serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
|
||||
|
@ -4,17 +4,20 @@ using NativeFileDialogSharp;
|
||||
using NResLib;
|
||||
using NResUI.Abstractions;
|
||||
using NResUI.Models;
|
||||
using TexmLib;
|
||||
|
||||
namespace NResUI.ImGuiUI
|
||||
{
|
||||
public class MainMenuBar : IImGuiPanel
|
||||
{
|
||||
private readonly ExplorerViewModel _explorerViewModel;
|
||||
private readonly NResExplorerViewModel _nResExplorerViewModel;
|
||||
private readonly TexmExplorerViewModel _texmExplorerViewModel;
|
||||
|
||||
private readonly MessageBoxModalPanel _messageBox;
|
||||
public MainMenuBar(ExplorerViewModel explorerViewModel, MessageBoxModalPanel messageBox)
|
||||
public MainMenuBar(NResExplorerViewModel nResExplorerViewModel, TexmExplorerViewModel texmExplorerViewModel, MessageBoxModalPanel messageBox)
|
||||
{
|
||||
_explorerViewModel = explorerViewModel;
|
||||
_nResExplorerViewModel = nResExplorerViewModel;
|
||||
_texmExplorerViewModel = texmExplorerViewModel;
|
||||
_messageBox = messageBox;
|
||||
}
|
||||
|
||||
@ -34,11 +37,29 @@ namespace NResUI.ImGuiUI
|
||||
|
||||
var parseResult = NResParser.ReadFile(path);
|
||||
|
||||
_explorerViewModel.SetParseResult(parseResult, path);
|
||||
_nResExplorerViewModel.SetParseResult(parseResult, path);
|
||||
Console.WriteLine("Read NRES");
|
||||
}
|
||||
}
|
||||
|
||||
if (_explorerViewModel.HasFile)
|
||||
if (ImGui.MenuItem("Open TEXM"))
|
||||
{
|
||||
var result = Dialog.FileOpen();
|
||||
|
||||
if (result.IsOk)
|
||||
{
|
||||
var path = result.Path;
|
||||
|
||||
using var fs = new FileStream(path, FileMode.Open);
|
||||
|
||||
var parseResult = TexmParser.ReadFromStream(fs, path);
|
||||
|
||||
_texmExplorerViewModel.SetParseResult(parseResult, path);
|
||||
Console.WriteLine("Read TEXM");
|
||||
}
|
||||
}
|
||||
|
||||
if (_nResExplorerViewModel.HasFile)
|
||||
{
|
||||
if (ImGui.MenuItem("Экспортировать"))
|
||||
{
|
||||
@ -48,32 +69,13 @@ namespace NResUI.ImGuiUI
|
||||
{
|
||||
var path = result.Path;
|
||||
|
||||
NResExporter.Export(_explorerViewModel.Archive!, path, _explorerViewModel.Path!);
|
||||
NResExporter.Export(_nResExplorerViewModel.Archive!, path, _nResExplorerViewModel.Path!);
|
||||
|
||||
_messageBox.Show("Успешно экспортировано");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui.BeginMenu("Open Recent"))
|
||||
{
|
||||
ImGui.EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui.MenuItem("Exit"))
|
||||
{
|
||||
App.Instance.Exit();
|
||||
}
|
||||
|
||||
ImGui.EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui.BeginMenu("Windows"))
|
||||
{
|
||||
if (ImGui.MenuItem("Settings"))
|
||||
{
|
||||
}
|
||||
|
||||
ImGui.EndMenu();
|
||||
}
|
||||
|
||||
|
@ -4,18 +4,18 @@ using NResUI.Models;
|
||||
|
||||
namespace NResUI.ImGuiUI;
|
||||
|
||||
public class ExplorerPanel : IImGuiPanel
|
||||
public class NResExplorerPanel : IImGuiPanel
|
||||
{
|
||||
private readonly ExplorerViewModel _viewModel;
|
||||
private readonly NResExplorerViewModel _viewModel;
|
||||
|
||||
public ExplorerPanel(ExplorerViewModel viewModel)
|
||||
public NResExplorerPanel(NResExplorerViewModel viewModel)
|
||||
{
|
||||
_viewModel = viewModel;
|
||||
}
|
||||
|
||||
public void OnImGuiRender()
|
||||
{
|
||||
if (ImGui.Begin("Explorer"))
|
||||
if (ImGui.Begin("NRes Explorer"))
|
||||
{
|
||||
if (!_viewModel.HasFile)
|
||||
{
|
73
NResUI/ImGuiUI/TexmExplorer.cs
Normal file
73
NResUI/ImGuiUI/TexmExplorer.cs
Normal file
@ -0,0 +1,73 @@
|
||||
using ImGuiNET;
|
||||
using NResUI.Abstractions;
|
||||
using NResUI.Models;
|
||||
|
||||
namespace NResUI.ImGuiUI;
|
||||
|
||||
public class TexmExplorer : IImGuiPanel
|
||||
{
|
||||
private readonly TexmExplorerViewModel _viewModel;
|
||||
|
||||
public TexmExplorer(TexmExplorerViewModel viewModel)
|
||||
{
|
||||
_viewModel = viewModel;
|
||||
}
|
||||
|
||||
public void OnImGuiRender()
|
||||
{
|
||||
if (ImGui.Begin("TEXM Explorer"))
|
||||
{
|
||||
if (!_viewModel.HasFile)
|
||||
{
|
||||
ImGui.Text("No TEXM opened");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_viewModel.TexmFile is not null)
|
||||
{
|
||||
ImGui.Text("File: ");
|
||||
ImGui.SameLine();
|
||||
ImGui.Text(_viewModel.Path);
|
||||
|
||||
ImGui.Text("Header: ");
|
||||
ImGui.SameLine();
|
||||
ImGui.Text(_viewModel.TexmFile.Header.TexmAscii);
|
||||
|
||||
ImGui.Text("Width: ");
|
||||
ImGui.SameLine();
|
||||
ImGui.Text(_viewModel.TexmFile.Header.Width.ToString());
|
||||
|
||||
ImGui.Text("Height: ");
|
||||
ImGui.SameLine();
|
||||
ImGui.Text(_viewModel.TexmFile.Header.Height.ToString());
|
||||
|
||||
ImGui.Text("MipMap Count: ");
|
||||
ImGui.SameLine();
|
||||
ImGui.Text(_viewModel.TexmFile.Header.MipmapCount.ToString());
|
||||
|
||||
ImGui.Text("Stride: ");
|
||||
ImGui.SameLine();
|
||||
ImGui.Text(_viewModel.TexmFile.Header.Stride.ToString());
|
||||
|
||||
ImGui.Text("Magic1: ");
|
||||
ImGui.SameLine();
|
||||
ImGui.Text(_viewModel.TexmFile.Header.Magic1.ToString());
|
||||
|
||||
ImGui.Text("Magic2: ");
|
||||
ImGui.SameLine();
|
||||
ImGui.Text(_viewModel.TexmFile.Header.Magic2.ToString());
|
||||
|
||||
ImGui.Text("Format: ");
|
||||
ImGui.SameLine();
|
||||
ImGui.Text(_viewModel.TexmFile.Header.Format.ToString());
|
||||
|
||||
ImGui.Text("IsIndexed: ");
|
||||
ImGui.SameLine();
|
||||
ImGui.Text(_viewModel.TexmFile.IsIndexed.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.End();
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace NResUI.Models;
|
||||
|
||||
public class ExplorerViewModel
|
||||
public class NResExplorerViewModel
|
||||
{
|
||||
public bool HasFile { get; set; }
|
||||
public string? Error { get; set; }
|
27
NResUI/Models/TexmExplorerViewModel.cs
Normal file
27
NResUI/Models/TexmExplorerViewModel.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using TexmLib;
|
||||
|
||||
namespace NResUI.Models;
|
||||
|
||||
public class TexmExplorerViewModel
|
||||
{
|
||||
public bool HasFile { get; set; }
|
||||
public string? Error { get; set; }
|
||||
|
||||
public TexmFile? TexmFile { get; set; }
|
||||
|
||||
public string? Path { get; set; }
|
||||
|
||||
public void SetParseResult(TexmParseResult result, string path)
|
||||
{
|
||||
Error = result.Error;
|
||||
|
||||
if (result.TexmFile != null)
|
||||
{
|
||||
HasFile = true;
|
||||
}
|
||||
|
||||
TexmFile = result.TexmFile;
|
||||
Path = path;
|
||||
}
|
||||
|
||||
}
|
@ -20,6 +20,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NResLib\NResLib.csproj" />
|
||||
<ProjectReference Include="..\TexmLib\TexmLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -10,6 +10,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NResUI", "NResUI\NResUI.csp
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NResLib", "NResLib\NResLib.csproj", "{9429AEAE-80A6-4EE7-AB66-9161CC4C3A3D}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MeshUnpacker", "MeshUnpacker\MeshUnpacker.csproj", "{F1465FFE-0D66-4A3C-90D7-153A14E226E6}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TexmLib", "TexmLib\TexmLib.csproj", "{40097CB1-B4B8-4D3E-A874-7D46F5C81DB3}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -36,5 +40,13 @@ Global
|
||||
{9429AEAE-80A6-4EE7-AB66-9161CC4C3A3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9429AEAE-80A6-4EE7-AB66-9161CC4C3A3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9429AEAE-80A6-4EE7-AB66-9161CC4C3A3D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F1465FFE-0D66-4A3C-90D7-153A14E226E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F1465FFE-0D66-4A3C-90D7-153A14E226E6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F1465FFE-0D66-4A3C-90D7-153A14E226E6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F1465FFE-0D66-4A3C-90D7-153A14E226E6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{40097CB1-B4B8-4D3E-A874-7D46F5C81DB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{40097CB1-B4B8-4D3E-A874-7D46F5C81DB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{40097CB1-B4B8-4D3E-A874-7D46F5C81DB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{40097CB1-B4B8-4D3E-A874-7D46F5C81DB3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace TextureDecoder;
|
||||
namespace TexmLib;
|
||||
|
||||
public static class Extensions
|
||||
{
|
3
TexmLib/NResParseResult.cs
Normal file
3
TexmLib/NResParseResult.cs
Normal file
@ -0,0 +1,3 @@
|
||||
namespace TexmLib;
|
||||
|
||||
public record TexmParseResult(TexmFile? TexmFile = null, string? Error = null);
|
@ -3,7 +3,7 @@ using System.Text;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
|
||||
namespace TextureDecoder;
|
||||
namespace TexmLib;
|
||||
|
||||
/// <summary>
|
||||
/// Заголовок TEXM файла (может быть .0 файл)
|
||||
@ -37,7 +37,7 @@ public record PageHeader(string Page, int Count, List<PageItem> Items);
|
||||
|
||||
public record PageItem(short X, short Width, short Y, short Height);
|
||||
|
||||
public class TextureFile
|
||||
public class TexmFile
|
||||
{
|
||||
/// <summary>
|
||||
/// Исходное имя файла текстуры TEXM
|
||||
@ -70,195 +70,6 @@ public class TextureFile
|
||||
/// </summary>
|
||||
public byte[] LookupColors { get; set; }
|
||||
|
||||
|
||||
private TextureFile()
|
||||
{
|
||||
}
|
||||
|
||||
public static TextureFile ReadFromStream(Stream stream, string file)
|
||||
{
|
||||
Span<byte> headerBytes = stackalloc byte[32];
|
||||
stream.ReadExactly(headerBytes);
|
||||
|
||||
var texmHeader = headerBytes[0..4];
|
||||
|
||||
var widthBytes = headerBytes[4..8];
|
||||
var heightBytes = headerBytes[8..12];
|
||||
var mipmapCountBytes = headerBytes[12..16];
|
||||
var strideBytes = headerBytes[16..20];
|
||||
var magic1Bytes = headerBytes[20..24];
|
||||
var magic2Bytes = headerBytes[24..28];
|
||||
var formatBytes = headerBytes[28..32];
|
||||
|
||||
var texmAscii = Encoding.ASCII.GetString(texmHeader);
|
||||
var width = BinaryPrimitives.ReadInt32LittleEndian(widthBytes);
|
||||
var height = BinaryPrimitives.ReadInt32LittleEndian(heightBytes);
|
||||
var mipmapCount = BinaryPrimitives.ReadInt32LittleEndian(mipmapCountBytes);
|
||||
var stride = BinaryPrimitives.ReadInt32LittleEndian(strideBytes);
|
||||
var magic1 = BinaryPrimitives.ReadInt32LittleEndian(magic1Bytes);
|
||||
var magic2 = BinaryPrimitives.ReadInt32LittleEndian(magic2Bytes);
|
||||
var format = BinaryPrimitives.ReadInt32LittleEndian(formatBytes);
|
||||
|
||||
var textureFile = new TextureFile()
|
||||
{
|
||||
FileName = file
|
||||
};
|
||||
|
||||
var header = new TexmHeader(
|
||||
texmAscii,
|
||||
width,
|
||||
height,
|
||||
mipmapCount,
|
||||
stride,
|
||||
magic1,
|
||||
magic2,
|
||||
format
|
||||
);
|
||||
|
||||
textureFile.Header = header;
|
||||
|
||||
if (format == 0)
|
||||
{
|
||||
// если формат 0, то текстура использует lookup таблицу в первых 1024 байтах (256 разных цветов в формате ARGB 888)
|
||||
|
||||
var lookupColors = new byte[1024];
|
||||
stream.ReadExactly(lookupColors, 0, lookupColors.Length);
|
||||
|
||||
textureFile.LookupColors = lookupColors;
|
||||
|
||||
var mipmapBytesList = ReadMipmapsAsIndexes(
|
||||
stream,
|
||||
mipmapCount,
|
||||
width,
|
||||
height
|
||||
);
|
||||
|
||||
textureFile.MipmapBytes = mipmapBytesList;
|
||||
textureFile.IsIndexed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var mipmapBytesList = ReadMipmaps(
|
||||
stream,
|
||||
format.AsStride(),
|
||||
mipmapCount,
|
||||
width,
|
||||
height
|
||||
);
|
||||
|
||||
textureFile.MipmapBytes = mipmapBytesList;
|
||||
}
|
||||
|
||||
if (stream.Position < stream.Length)
|
||||
{
|
||||
// has PAGE data
|
||||
var pageHeader = ReadPage(stream);
|
||||
|
||||
textureFile.Pages = pageHeader;
|
||||
}
|
||||
|
||||
return textureFile;
|
||||
}
|
||||
|
||||
private static PageHeader ReadPage(Stream stream)
|
||||
{
|
||||
Span<byte> pageBytes = stackalloc byte[4];
|
||||
|
||||
stream.ReadExactly(pageBytes);
|
||||
|
||||
var pageHeaderAscii = Encoding.ASCII.GetString(pageBytes);
|
||||
|
||||
stream.ReadExactly(pageBytes);
|
||||
|
||||
var pageCount = BinaryPrimitives.ReadInt32LittleEndian(pageBytes);
|
||||
|
||||
List<PageItem> pageItems = [];
|
||||
|
||||
Span<byte> itemBytes = stackalloc byte[2];
|
||||
for (int i = 0; i < pageCount; i++)
|
||||
{
|
||||
stream.ReadExactly(itemBytes);
|
||||
var x = BinaryPrimitives.ReadInt16LittleEndian(itemBytes);
|
||||
stream.ReadExactly(itemBytes);
|
||||
var pageWidth = BinaryPrimitives.ReadInt16LittleEndian(itemBytes);
|
||||
stream.ReadExactly(itemBytes);
|
||||
var y = BinaryPrimitives.ReadInt16LittleEndian(itemBytes);
|
||||
stream.ReadExactly(itemBytes);
|
||||
var pageHeight = BinaryPrimitives.ReadInt16LittleEndian(itemBytes);
|
||||
|
||||
pageItems.Add(
|
||||
new PageItem(
|
||||
x,
|
||||
pageWidth,
|
||||
y,
|
||||
pageHeight
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
var pageHeader = new PageHeader(pageHeaderAscii, pageCount, pageItems);
|
||||
return pageHeader;
|
||||
}
|
||||
|
||||
private static List<byte[]> ReadMipmaps(Stream stream, int stride, int mipmapCount, int topWidth, int topHeight)
|
||||
{
|
||||
if (stride == 0)
|
||||
{
|
||||
stride = 16;
|
||||
}
|
||||
|
||||
List<int> mipmapByteLengths = [];
|
||||
|
||||
for (int i = 0; i < mipmapCount; i++)
|
||||
{
|
||||
var mipWidth = topWidth / (int) Math.Pow(2, i);
|
||||
var mipHeight = topHeight / (int) Math.Pow(2, i);
|
||||
|
||||
var imageByteLength = mipWidth * mipHeight * (stride / 8);
|
||||
mipmapByteLengths.Add(imageByteLength);
|
||||
}
|
||||
|
||||
List<byte[]> mipmapBytesList = [];
|
||||
|
||||
foreach (var mipmapByteLength in mipmapByteLengths)
|
||||
{
|
||||
var mipmapBuffer = new byte[mipmapByteLength];
|
||||
|
||||
stream.ReadExactly(mipmapBuffer, 0, mipmapByteLength);
|
||||
|
||||
mipmapBytesList.Add(mipmapBuffer);
|
||||
}
|
||||
|
||||
return mipmapBytesList;
|
||||
}
|
||||
|
||||
private static List<byte[]> ReadMipmapsAsIndexes(Stream stream, int mipmapCount, int topWidth, int topHeight)
|
||||
{
|
||||
List<int> mipmapByteLengths = [];
|
||||
|
||||
for (int i = 0; i < mipmapCount; i++)
|
||||
{
|
||||
var mipWidth = topWidth / (int) Math.Pow(2, i);
|
||||
var mipHeight = topHeight / (int) Math.Pow(2, i);
|
||||
|
||||
var imageByteLength = mipWidth * mipHeight;
|
||||
mipmapByteLengths.Add(imageByteLength);
|
||||
}
|
||||
|
||||
List<byte[]> mipmapBytesList = [];
|
||||
|
||||
foreach (var mipmapByteLength in mipmapByteLengths)
|
||||
{
|
||||
var mipmapBuffer = new byte[mipmapByteLength];
|
||||
|
||||
stream.ReadExactly(mipmapBuffer, 0, mipmapByteLength);
|
||||
|
||||
mipmapBytesList.Add(mipmapBuffer);
|
||||
}
|
||||
|
||||
return mipmapBytesList;
|
||||
}
|
||||
|
||||
public async Task WriteToFolder(string folder)
|
||||
{
|
||||
if (Directory.Exists(folder))
|
13
TexmLib/TexmLib.csproj
Normal file
13
TexmLib/TexmLib.csproj
Normal file
@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
201
TexmLib/TexmParser.cs
Normal file
201
TexmLib/TexmParser.cs
Normal file
@ -0,0 +1,201 @@
|
||||
using System.Buffers.Binary;
|
||||
using System.Text;
|
||||
|
||||
namespace TexmLib;
|
||||
|
||||
public class TexmParser
|
||||
{
|
||||
public static TexmParseResult ReadFromStream(Stream stream, string file)
|
||||
{
|
||||
if (stream.Length < 32)
|
||||
{
|
||||
return new TexmParseResult(null, "Файл короче 32 байт - точно не текстура");
|
||||
}
|
||||
|
||||
Span<byte> headerBytes = stackalloc byte[32];
|
||||
stream.ReadExactly(headerBytes);
|
||||
|
||||
var texmHeader = headerBytes[0..4];
|
||||
|
||||
var widthBytes = headerBytes[4..8];
|
||||
var heightBytes = headerBytes[8..12];
|
||||
var mipmapCountBytes = headerBytes[12..16];
|
||||
var strideBytes = headerBytes[16..20];
|
||||
var magic1Bytes = headerBytes[20..24];
|
||||
var magic2Bytes = headerBytes[24..28];
|
||||
var formatBytes = headerBytes[28..32];
|
||||
|
||||
var texmAscii = Encoding.ASCII.GetString(texmHeader).Trim('\0');
|
||||
var width = BinaryPrimitives.ReadInt32LittleEndian(widthBytes);
|
||||
var height = BinaryPrimitives.ReadInt32LittleEndian(heightBytes);
|
||||
var mipmapCount = BinaryPrimitives.ReadInt32LittleEndian(mipmapCountBytes);
|
||||
var stride = BinaryPrimitives.ReadInt32LittleEndian(strideBytes);
|
||||
var magic1 = BinaryPrimitives.ReadInt32LittleEndian(magic1Bytes);
|
||||
var magic2 = BinaryPrimitives.ReadInt32LittleEndian(magic2Bytes);
|
||||
var format = BinaryPrimitives.ReadInt32LittleEndian(formatBytes);
|
||||
|
||||
if (texmAscii != "Texm")
|
||||
{
|
||||
return new TexmParseResult(null, "Файл не начинается с Texm");
|
||||
}
|
||||
|
||||
var textureFile = new TexmFile()
|
||||
{
|
||||
FileName = file
|
||||
};
|
||||
|
||||
var header = new TexmHeader(
|
||||
texmAscii,
|
||||
width,
|
||||
height,
|
||||
mipmapCount,
|
||||
stride,
|
||||
magic1,
|
||||
magic2,
|
||||
format
|
||||
);
|
||||
|
||||
textureFile.Header = header;
|
||||
|
||||
if (format == 0)
|
||||
{
|
||||
// если формат 0, то текстура использует lookup таблицу в первых 1024 байтах (256 разных цветов в формате ARGB 888)
|
||||
|
||||
var lookupColors = new byte[1024];
|
||||
stream.ReadExactly(lookupColors, 0, lookupColors.Length);
|
||||
|
||||
textureFile.LookupColors = lookupColors;
|
||||
|
||||
var mipmapBytesList = ReadMipmapsAsIndexes(
|
||||
stream,
|
||||
mipmapCount,
|
||||
width,
|
||||
height
|
||||
);
|
||||
|
||||
textureFile.MipmapBytes = mipmapBytesList;
|
||||
textureFile.IsIndexed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var mipmapBytesList = ReadMipmaps(
|
||||
stream,
|
||||
format.AsStride(),
|
||||
mipmapCount,
|
||||
width,
|
||||
height
|
||||
);
|
||||
|
||||
textureFile.MipmapBytes = mipmapBytesList;
|
||||
}
|
||||
|
||||
if (stream.Position < stream.Length)
|
||||
{
|
||||
// has PAGE data
|
||||
var pageHeader = ReadPage(stream);
|
||||
|
||||
textureFile.Pages = pageHeader;
|
||||
}
|
||||
|
||||
return new TexmParseResult(textureFile);
|
||||
}
|
||||
|
||||
private static PageHeader ReadPage(Stream stream)
|
||||
{
|
||||
Span<byte> pageBytes = stackalloc byte[4];
|
||||
|
||||
stream.ReadExactly(pageBytes);
|
||||
|
||||
var pageHeaderAscii = Encoding.ASCII.GetString(pageBytes);
|
||||
|
||||
stream.ReadExactly(pageBytes);
|
||||
|
||||
var pageCount = BinaryPrimitives.ReadInt32LittleEndian(pageBytes);
|
||||
|
||||
List<PageItem> pageItems = [];
|
||||
|
||||
Span<byte> itemBytes = stackalloc byte[2];
|
||||
for (int i = 0; i < pageCount; i++)
|
||||
{
|
||||
stream.ReadExactly(itemBytes);
|
||||
var x = BinaryPrimitives.ReadInt16LittleEndian(itemBytes);
|
||||
stream.ReadExactly(itemBytes);
|
||||
var pageWidth = BinaryPrimitives.ReadInt16LittleEndian(itemBytes);
|
||||
stream.ReadExactly(itemBytes);
|
||||
var y = BinaryPrimitives.ReadInt16LittleEndian(itemBytes);
|
||||
stream.ReadExactly(itemBytes);
|
||||
var pageHeight = BinaryPrimitives.ReadInt16LittleEndian(itemBytes);
|
||||
|
||||
pageItems.Add(
|
||||
new PageItem(
|
||||
x,
|
||||
pageWidth,
|
||||
y,
|
||||
pageHeight
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
var pageHeader = new PageHeader(pageHeaderAscii, pageCount, pageItems);
|
||||
return pageHeader;
|
||||
}
|
||||
|
||||
private static List<byte[]> ReadMipmaps(Stream stream, int stride, int mipmapCount, int topWidth, int topHeight)
|
||||
{
|
||||
if (stride == 0)
|
||||
{
|
||||
stride = 16;
|
||||
}
|
||||
|
||||
List<int> mipmapByteLengths = [];
|
||||
|
||||
for (int i = 0; i < mipmapCount; i++)
|
||||
{
|
||||
var mipWidth = topWidth / (int) Math.Pow(2, i);
|
||||
var mipHeight = topHeight / (int) Math.Pow(2, i);
|
||||
|
||||
var imageByteLength = mipWidth * mipHeight * (stride / 8);
|
||||
mipmapByteLengths.Add(imageByteLength);
|
||||
}
|
||||
|
||||
List<byte[]> mipmapBytesList = [];
|
||||
|
||||
foreach (var mipmapByteLength in mipmapByteLengths)
|
||||
{
|
||||
var mipmapBuffer = new byte[mipmapByteLength];
|
||||
|
||||
stream.ReadExactly(mipmapBuffer, 0, mipmapByteLength);
|
||||
|
||||
mipmapBytesList.Add(mipmapBuffer);
|
||||
}
|
||||
|
||||
return mipmapBytesList;
|
||||
}
|
||||
|
||||
private static List<byte[]> ReadMipmapsAsIndexes(Stream stream, int mipmapCount, int topWidth, int topHeight)
|
||||
{
|
||||
List<int> mipmapByteLengths = [];
|
||||
|
||||
for (int i = 0; i < mipmapCount; i++)
|
||||
{
|
||||
var mipWidth = topWidth / (int) Math.Pow(2, i);
|
||||
var mipHeight = topHeight / (int) Math.Pow(2, i);
|
||||
|
||||
var imageByteLength = mipWidth * mipHeight;
|
||||
mipmapByteLengths.Add(imageByteLength);
|
||||
}
|
||||
|
||||
List<byte[]> mipmapBytesList = [];
|
||||
|
||||
foreach (var mipmapByteLength in mipmapByteLengths)
|
||||
{
|
||||
var mipmapBuffer = new byte[mipmapByteLength];
|
||||
|
||||
stream.ReadExactly(mipmapBuffer, 0, mipmapByteLength);
|
||||
|
||||
mipmapBytesList.Add(mipmapBuffer);
|
||||
}
|
||||
|
||||
return mipmapBytesList;
|
||||
}
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
using System.Buffers.Binary;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using TextureDecoder;
|
||||
using TexmLib;
|
||||
|
||||
var folder = "C:\\Projects\\CSharp\\ParkanPlayground\\ParkanPlayground\\bin\\Debug\\net8.0\\ui.lib";
|
||||
|
||||
var files = Directory.EnumerateFiles(folder);
|
||||
|
||||
List<TextureFile> textureFiles = [];
|
||||
List<TexmFile> textureFiles = [];
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
@ -15,9 +15,9 @@ foreach (var file in files)
|
||||
{
|
||||
var fs = new FileStream(file, FileMode.Open);
|
||||
|
||||
var textureFile = TextureFile.ReadFromStream(fs, file);
|
||||
var parseResult = TexmParser.ReadFromStream(fs, file);
|
||||
|
||||
textureFiles.Add(textureFile);
|
||||
textureFiles.Add(parseResult.TexmFile);
|
||||
|
||||
Console.WriteLine($"Successfully read: {file}");
|
||||
}
|
||||
|
@ -11,4 +11,12 @@
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\TexmLib\TexmLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\TexmLib\TexmLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
Loading…
x
Reference in New Issue
Block a user