thief-mission-viewer/project/code/TMV/TextureLoader.Pcx.cs

124 lines
3.9 KiB
C#
Raw Permalink Normal View History

2024-08-12 17:46:30 +00:00
using System.IO;
using System.Linq;
using System.Text;
2024-08-12 18:02:20 +00:00
using Godot;
2024-08-12 17:46:30 +00:00
namespace KeepersCompound.TMV;
public partial class TextureLoader
{
// TODO: Support more types of PCX just in case
// References:
// - https://www.fileformat.info/format/pcx/egff.htm
// - http://www.fysnet.net/pcxfile.htm
2024-09-17 16:07:07 +00:00
private static Image LoadPcx(string path)
2024-08-12 17:46:30 +00:00
{
2024-08-12 18:02:20 +00:00
static Color[] LoadFamilyPalette(string famPath)
{
var numColors = 256;
var bytesPerColor = 3;
var paletteSize = numColors * bytesPerColor;
var palette = new Color[numColors];
var options = new EnumerationOptions
{
MatchCasing = MatchCasing.CaseInsensitive
};
var paths = Directory.GetFiles(famPath, "full.pcx", options);
if (paths.Length != 0)
{
var palettePath = paths[0];
using MemoryStream stream = new(File.ReadAllBytes(palettePath));
using BinaryReader reader = new(stream, Encoding.UTF8, false);
stream.Seek(-paletteSize, SeekOrigin.End);
for (var i = 0; i < numColors; i++)
{
var r = reader.ReadByte() / 255.0f;
var g = reader.ReadByte() / 255.0f;
var b = reader.ReadByte() / 255.0f;
2024-09-01 12:51:05 +00:00
var a = (i == 0) ? 0.0f : 1.0f;
palette[i] = new Color(r, g, b, a);
2024-08-12 18:02:20 +00:00
}
}
return palette;
}
static System.Numerics.Vector2 ReadVec2(BinaryReader reader)
{
return new(reader.ReadUInt16(), reader.ReadUInt16());
}
2024-08-12 17:46:30 +00:00
using MemoryStream stream = new(File.ReadAllBytes(path));
using BinaryReader reader = new(stream, Encoding.UTF8, false);
// Header
var tag = reader.ReadByte();
var version = reader.ReadByte();
var compression = reader.ReadByte();
var bytesPerPixel = reader.ReadByte();
2024-08-12 18:02:20 +00:00
var min = ReadVec2(reader);
var max = ReadVec2(reader);
var dpi = ReadVec2(reader);
2024-08-12 17:46:30 +00:00
var headerPalette = reader.ReadBytes(48);
reader.ReadByte();
var numPlanes = reader.ReadByte();
var bytesPerRow = reader.ReadUInt16();
var paletteMode = reader.ReadUInt16();
2024-08-12 18:02:20 +00:00
var dpiSrc = ReadVec2(reader);
2024-08-12 17:46:30 +00:00
reader.ReadBytes(54);
// Validation
if (tag != 0x0A)
{
throw new System.Exception("Invalid tag");
}
var validVersions = new int[] { 0, 2, 3, 4, 5 };
if (!validVersions.Contains(version))
{
throw new System.Exception("Unsupported version");
}
if (compression != 1)
{
throw new System.Exception("Unsupported compression mode");
}
if (bytesPerPixel != 8 || numPlanes != 1)
{
throw new System.Exception("Unsupported bpp/plane format");
}
2024-08-12 18:02:20 +00:00
var palette = LoadFamilyPalette(path.GetBaseDir());
2024-08-12 17:46:30 +00:00
2024-08-12 18:02:20 +00:00
// Read run length encoded pixel Data
2024-08-12 17:46:30 +00:00
var width = (int)(max.X - min.X + 1);
var height = (int)(max.Y - min.Y + 1);
2024-08-26 11:40:51 +00:00
var image = Image.CreateEmpty(width, height, false, Image.Format.Rgba8);
2024-08-12 17:46:30 +00:00
for (var y = 0; y < height; y++)
{
var x = 0;
while (x < bytesPerRow)
{
var b = reader.ReadByte();
if ((b & 0xc0) == 0xc0)
{
var runLength = b & 0x3f;
b = reader.ReadByte();
for (var i = 0; i < runLength; i++)
{
image.SetPixel(x, y, palette[b]);
x++;
}
}
else
{
image.SetPixel(x, y, palette[b]);
x++;
}
}
}
2024-09-17 16:07:07 +00:00
return image;
2024-08-12 17:46:30 +00:00
}
}