0
mirror of https://github.com/sampletext32/ParkanPlayground.git synced 2025-05-18 19:31:17 +03:00
ParkanPlayground/TexmLib/TexmParser.cs
2024-11-18 23:48:42 +03:00

201 lines
6.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}
}