1
mirror of https://github.com/sampletext32/ParkanPlayground.git synced 2025-12-09 11:11:24 +04:00

parse PAL file

This commit is contained in:
bird_egop
2025-11-23 00:04:05 +03:00
parent f4442897a6
commit c27b77cbfc
8 changed files with 128 additions and 15 deletions

54
PalLib/PalFile.cs Normal file
View File

@@ -0,0 +1,54 @@
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
namespace PalLib;
/// <summary>
/// PAL файл по сути это indexed текстура (1024 байт - 256 цветов lookup + 4 байта "Ipol" и затем 256x256 индексов в lookup)
/// </summary>
public class PalFile
{
public required string FileName { get; set; }
/// <summary>
/// 256 цветов lookup (1024 байт)
/// </summary>
public required byte[] Palette { get; set; }
/// <summary>
/// 256x256 индексов в lookup
/// </summary>
public required byte[] Indices { get; set; }
public void SaveAsPng(string outputPath)
{
const int width = 256;
const int height = 256;
var rgbaBytes = new byte[width * height * 4];
for (int i = 0; i < Indices.Length; i++)
{
var index = Indices[i];
// Palette is 256 colors * 4 bytes (ARGB usually, based on TexmLib)
// TexmLib: r = lookup[i*4+0], g = lookup[i*4+1], b = lookup[i*4+2], a = lookup[i*4+3]
// Assuming same format here.
// since PAL is likely directx related, the format is is likely BGRA
var b = Palette[index * 4 + 0];
var g = Palette[index * 4 + 1];
var r = Palette[index * 4 + 2];
var a = Palette[index * 4 + 3]; // Alpha? Or is it unused/padding? TexmLib sets alpha to 255 manually for indexed.
rgbaBytes[i * 4 + 0] = r;
rgbaBytes[i * 4 + 1] = g;
rgbaBytes[i * 4 + 2] = b;
rgbaBytes[i * 4 + 3] = 255;
}
using var image = Image.LoadPixelData<Rgba32>(rgbaBytes, width, height);
image.SaveAsPng(outputPath);
}
}

7
PalLib/PalLib.csproj Normal file
View File

@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="SixLabors.ImageSharp"/>
</ItemGroup>
</Project>

37
PalLib/PalParser.cs Normal file
View File

@@ -0,0 +1,37 @@
using System.Text;
namespace PalLib;
public class PalParser
{
public static PalFile ReadFromStream(Stream stream, string filename)
{
// Expected size: 1024 (palette) + 4 ("Ipol") + 65536 (indices) = 66564
if (stream.Length != 66564)
{
throw new InvalidDataException($"Invalid PAL file size. Expected 66564 bytes, got {stream.Length}.");
}
var palette = new byte[1024];
stream.ReadExactly(palette, 0, 1024);
var signatureBytes = new byte[4];
stream.ReadExactly(signatureBytes, 0, 4);
var signature = Encoding.ASCII.GetString(signatureBytes);
if (signature != "Ipol")
{
throw new InvalidDataException($"Invalid PAL file signature. Expected 'Ipol', got '{signature}'.");
}
var indices = new byte[65536];
stream.ReadExactly(indices, 0, 65536);
return new PalFile
{
FileName = filename,
Palette = palette,
Indices = indices
};
}
}

View File

@@ -12,6 +12,7 @@
<Project Path="NLUnpacker/NLUnpacker.csproj" />
<Project Path="NResLib/NResLib.csproj" />
<Project Path="NResUI/NResUI.csproj" />
<Project Path="PalLib\PalLib.csproj" Type="Classic C#" />
<Project Path="ParkanPlayground/ParkanPlayground.csproj" />
<Project Path="ScrLib/ScrLib.csproj" />
<Project Path="TexmLib/TexmLib.csproj" />

View File

@@ -7,6 +7,7 @@
<ItemGroup>
<ProjectReference Include="..\MissionTmaLib\MissionTmaLib.csproj" />
<ProjectReference Include="..\NResLib\NResLib.csproj" />
<ProjectReference Include="..\PalLib\PalLib.csproj" />
<ProjectReference Include="..\ScrLib\ScrLib.csproj" />
<ProjectReference Include="..\VarsetLib\VarsetLib.csproj" />
</ItemGroup>

View File

@@ -2,6 +2,7 @@ using System.Buffers.Binary;
using Common;
using MissionTmaLib.Parsing;
using NResLib;
using PalLib;
using ParkanPlayground;
using VarsetLib;
@@ -17,21 +18,27 @@ using VarsetLib;
// converter.Convert("E:\\ParkanUnpacked\\static.rlb\\2_MESH_s_stn_0_01.msh");
// converter.Convert("E:\\ParkanUnpacked\\bases.rlb\\25_MESH_R_H_02.msh");
var fs = new FileStream("E:\\ParkanUnpacked\\sys.lib\\1_Palm_PAL.PAL", FileMode.Open,
FileAccess.Read, FileShare.Read);
foreach (var path in Directory.EnumerateFiles("E:\\ParkanUnpacked\\behpsp.res"))
{
using var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
var palFile = PalParser.ReadFromStream(fs, "1_Palm_PAL.PAL");
var file = BinaryVarsetFileParser.Parse(fs);
palFile.SaveAsPng("pal.png");
_ = 5;
}
{
var fs = new FileStream("E:\\ParkanUnpacked\\behpsp.res\\31_00 00 00 00_prof_generator.var", FileMode.Open,
FileAccess.Read, FileShare.Read);
var file = BinaryVarsetFileParser.Parse(fs);
_ = 5;
}
// foreach (var path in Directory.EnumerateFiles("E:\\ParkanUnpacked\\behpsp.res"))
// {
// using var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
//
// var file = BinaryVarsetFileParser.Parse(fs);
//
// _ = 5;
// }
//
// {
// var fs = new FileStream("E:\\ParkanUnpacked\\behpsp.res\\31_00 00 00 00_prof_generator.var", FileMode.Open,
// FileAccess.Read, FileShare.Read);
//
// var file = BinaryVarsetFileParser.Parse(fs);
//
// _ = 5;
// }

View File

@@ -344,6 +344,9 @@ IComponent ** LoadSomething(undefined4, undefined4, undefined4, undefined4)
- `0x701` - INetworkInterface
- `0x10d` - CreateVertexBufferData
## SuperAI = Clan с точки зрения индексации
Т.е. у каждого клана свой SuperAI
## Опции

View File

@@ -1,3 +1,6 @@
namespace VarsetLib;
/// <summary>
/// Бинарный файл, который можно найти в behpsp.res
/// </summary>
public record BinaryVarsetFile(int Count, List<BinaryVarsetItem> Items);