diff --git a/PalLib/PalFile.cs b/PalLib/PalFile.cs
new file mode 100644
index 0000000..082fc9c
--- /dev/null
+++ b/PalLib/PalFile.cs
@@ -0,0 +1,54 @@
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace PalLib;
+
+///
+/// PAL файл по сути это indexed текстура (1024 байт - 256 цветов lookup + 4 байта "Ipol" и затем 256x256 индексов в lookup)
+///
+public class PalFile
+{
+ public required string FileName { get; set; }
+
+ ///
+ /// 256 цветов lookup (1024 байт)
+ ///
+ public required byte[] Palette { get; set; }
+
+ ///
+ /// 256x256 индексов в lookup
+ ///
+ 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(rgbaBytes, width, height);
+ image.SaveAsPng(outputPath);
+ }
+}
diff --git a/PalLib/PalLib.csproj b/PalLib/PalLib.csproj
new file mode 100644
index 0000000..a0f23a0
--- /dev/null
+++ b/PalLib/PalLib.csproj
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/PalLib/PalParser.cs b/PalLib/PalParser.cs
new file mode 100644
index 0000000..efcc815
--- /dev/null
+++ b/PalLib/PalParser.cs
@@ -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
+ };
+ }
+}
diff --git a/ParkanPlayground.slnx b/ParkanPlayground.slnx
index 5383a33..ac5de6f 100644
--- a/ParkanPlayground.slnx
+++ b/ParkanPlayground.slnx
@@ -12,6 +12,7 @@
+
diff --git a/ParkanPlayground/ParkanPlayground.csproj b/ParkanPlayground/ParkanPlayground.csproj
index b749716..ed750c5 100644
--- a/ParkanPlayground/ParkanPlayground.csproj
+++ b/ParkanPlayground/ParkanPlayground.csproj
@@ -7,6 +7,7 @@
+
diff --git a/ParkanPlayground/Program.cs b/ParkanPlayground/Program.cs
index c5c064b..7449e01 100644
--- a/ParkanPlayground/Program.cs
+++ b/ParkanPlayground/Program.cs
@@ -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;
-}
\ No newline at end of file
+// 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;
+// }
\ No newline at end of file
diff --git a/README.md b/README.md
index 44ac408..042e89a 100644
--- a/README.md
+++ b/README.md
@@ -344,6 +344,9 @@ IComponent ** LoadSomething(undefined4, undefined4, undefined4, undefined4)
- `0x701` - INetworkInterface
- `0x10d` - CreateVertexBufferData
+## SuperAI = Clan с точки зрения индексации
+
+Т.е. у каждого клана свой SuperAI
## Опции
diff --git a/VarsetLib/BinaryVarsetFile.cs b/VarsetLib/BinaryVarsetFile.cs
index 490c9bb..5f7f84f 100644
--- a/VarsetLib/BinaryVarsetFile.cs
+++ b/VarsetLib/BinaryVarsetFile.cs
@@ -1,3 +1,6 @@
namespace VarsetLib;
+///
+/// Бинарный файл, который можно найти в behpsp.res
+///
public record BinaryVarsetFile(int Count, List Items);