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(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceCollection.AddSingleton(new ExplorerViewModel());
|
serviceCollection.AddSingleton(new NResExplorerViewModel());
|
||||||
|
serviceCollection.AddSingleton(new TexmExplorerViewModel());
|
||||||
|
|
||||||
var serviceProvider = serviceCollection.BuildServiceProvider();
|
var serviceProvider = serviceCollection.BuildServiceProvider();
|
||||||
|
|
||||||
|
@ -4,17 +4,20 @@ using NativeFileDialogSharp;
|
|||||||
using NResLib;
|
using NResLib;
|
||||||
using NResUI.Abstractions;
|
using NResUI.Abstractions;
|
||||||
using NResUI.Models;
|
using NResUI.Models;
|
||||||
|
using TexmLib;
|
||||||
|
|
||||||
namespace NResUI.ImGuiUI
|
namespace NResUI.ImGuiUI
|
||||||
{
|
{
|
||||||
public class MainMenuBar : IImGuiPanel
|
public class MainMenuBar : IImGuiPanel
|
||||||
{
|
{
|
||||||
private readonly ExplorerViewModel _explorerViewModel;
|
private readonly NResExplorerViewModel _nResExplorerViewModel;
|
||||||
|
private readonly TexmExplorerViewModel _texmExplorerViewModel;
|
||||||
|
|
||||||
private readonly MessageBoxModalPanel _messageBox;
|
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;
|
_messageBox = messageBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,11 +37,29 @@ namespace NResUI.ImGuiUI
|
|||||||
|
|
||||||
var parseResult = NResParser.ReadFile(path);
|
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("Экспортировать"))
|
if (ImGui.MenuItem("Экспортировать"))
|
||||||
{
|
{
|
||||||
@ -48,32 +69,13 @@ namespace NResUI.ImGuiUI
|
|||||||
{
|
{
|
||||||
var path = result.Path;
|
var path = result.Path;
|
||||||
|
|
||||||
NResExporter.Export(_explorerViewModel.Archive!, path, _explorerViewModel.Path!);
|
NResExporter.Export(_nResExplorerViewModel.Archive!, path, _nResExplorerViewModel.Path!);
|
||||||
|
|
||||||
_messageBox.Show("Успешно экспортировано");
|
_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();
|
ImGui.EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,18 +4,18 @@ using NResUI.Models;
|
|||||||
|
|
||||||
namespace NResUI.ImGuiUI;
|
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;
|
_viewModel = viewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnImGuiRender()
|
public void OnImGuiRender()
|
||||||
{
|
{
|
||||||
if (ImGui.Begin("Explorer"))
|
if (ImGui.Begin("NRes Explorer"))
|
||||||
{
|
{
|
||||||
if (!_viewModel.HasFile)
|
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;
|
namespace NResUI.Models;
|
||||||
|
|
||||||
public class ExplorerViewModel
|
public class NResExplorerViewModel
|
||||||
{
|
{
|
||||||
public bool HasFile { get; set; }
|
public bool HasFile { get; set; }
|
||||||
public string? Error { 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>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\NResLib\NResLib.csproj" />
|
<ProjectReference Include="..\NResLib\NResLib.csproj" />
|
||||||
|
<ProjectReference Include="..\TexmLib\TexmLib.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -10,6 +10,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NResUI", "NResUI\NResUI.csp
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NResLib", "NResLib\NResLib.csproj", "{9429AEAE-80A6-4EE7-AB66-9161CC4C3A3D}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NResLib", "NResLib\NResLib.csproj", "{9429AEAE-80A6-4EE7-AB66-9161CC4C3A3D}"
|
||||||
EndProject
|
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
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
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}.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.ActiveCfg = Release|Any CPU
|
||||||
{9429AEAE-80A6-4EE7-AB66-9161CC4C3A3D}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace TextureDecoder;
|
namespace TexmLib;
|
||||||
|
|
||||||
public static class Extensions
|
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;
|
||||||
using SixLabors.ImageSharp.PixelFormats;
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
|
|
||||||
namespace TextureDecoder;
|
namespace TexmLib;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Заголовок TEXM файла (может быть .0 файл)
|
/// Заголовок 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 record PageItem(short X, short Width, short Y, short Height);
|
||||||
|
|
||||||
public class TextureFile
|
public class TexmFile
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Исходное имя файла текстуры TEXM
|
/// Исходное имя файла текстуры TEXM
|
||||||
@ -70,195 +70,6 @@ public class TextureFile
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public byte[] LookupColors { get; set; }
|
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)
|
public async Task WriteToFolder(string folder)
|
||||||
{
|
{
|
||||||
if (Directory.Exists(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 System.Buffers.Binary;
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
using SixLabors.ImageSharp.PixelFormats;
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
using TextureDecoder;
|
using TexmLib;
|
||||||
|
|
||||||
var folder = "C:\\Projects\\CSharp\\ParkanPlayground\\ParkanPlayground\\bin\\Debug\\net8.0\\ui.lib";
|
var folder = "C:\\Projects\\CSharp\\ParkanPlayground\\ParkanPlayground\\bin\\Debug\\net8.0\\ui.lib";
|
||||||
|
|
||||||
var files = Directory.EnumerateFiles(folder);
|
var files = Directory.EnumerateFiles(folder);
|
||||||
|
|
||||||
List<TextureFile> textureFiles = [];
|
List<TexmFile> textureFiles = [];
|
||||||
|
|
||||||
foreach (var file in files)
|
foreach (var file in files)
|
||||||
{
|
{
|
||||||
@ -15,9 +15,9 @@ foreach (var file in files)
|
|||||||
{
|
{
|
||||||
var fs = new FileStream(file, FileMode.Open);
|
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}");
|
Console.WriteLine($"Successfully read: {file}");
|
||||||
}
|
}
|
||||||
|
@ -11,4 +11,12 @@
|
|||||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\TexmLib\TexmLib.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\TexmLib\TexmLib.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user