diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj
index 0970026f..b410cb58 100644
--- a/ArcFormats/ArcFormats.csproj
+++ b/ArcFormats/ArcFormats.csproj
@@ -204,6 +204,7 @@
+
diff --git a/ArcFormats/KiriKiri/ArcTLG.cs b/ArcFormats/KiriKiri/ArcTLG.cs
new file mode 100644
index 00000000..a4d7f568
--- /dev/null
+++ b/ArcFormats/KiriKiri/ArcTLG.cs
@@ -0,0 +1,109 @@
+//! \file ArcTLG.cs
+//! \date Tue Mar 17 2026 10:35:55
+//! \brief KiriKiri TLG image implementation.
+//---------------------------------------------------------------------------
+// TLGqoi multi-layer image decoder
+//
+// C# port by crsky
+//
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.IO;
+
+namespace GameRes.Formats.KiriKiri
+{
+ internal class TlgLayerEntry : Entry
+ {
+ public int Index;
+ }
+
+ [Export(typeof(ArchiveFormat))]
+ public class TlgOpener : ArchiveFormat
+ {
+ public override string Tag { get { return "TLG"; } }
+ public override string Description { get { return "KiriKiri game engine image format"; } }
+ public override uint Signature { get { return 0x71474C54; } } // 'TLGq'
+ public override bool IsHierarchic { get { return false; } }
+ public override bool CanWrite { get { return false; } }
+
+ public TlgOpener ()
+ {
+ Extensions = new string[] { "tlg" };
+ }
+
+ public override ArcFile TryOpen (ArcView file)
+ {
+ if (!file.View.AsciiEqual (0, "TLGqoi") || !file.View.AsciiEqual (7, "raw"))
+ return null;
+ var qhdr = Array.Empty ();
+ var offset = 0x14;
+ while (true)
+ {
+ var entry_signature = file.View.ReadInt32 (offset);
+ var entry_size = file.View.ReadInt32 (offset+4);
+ offset += 8;
+ if (0x52444851 == entry_signature) // 'QHDR'
+ {
+ if (0x30 != entry_size)
+ return null;
+ qhdr = file.View.ReadBytes (offset, (uint)entry_size);
+ if (entry_size != qhdr.Length)
+ return null;
+ offset += entry_size;
+ }
+ else if (0 == entry_signature && 0 == entry_size)
+ break;
+ else
+ return null;
+ }
+ if (0 == qhdr.Length)
+ return null;
+ var layer_count = qhdr.ToInt32 (4);
+ if (layer_count < 1)
+ return null;
+ var block_count = qhdr.ToInt32 (12);
+ if (0 == block_count)
+ return null;
+ var dir = new List (layer_count);
+ for (var i = 0; i < layer_count; i++)
+ {
+ dir.Add (new TlgLayerEntry
+ {
+ Name = string.Format ("{0}#{1:D3}.tlg", Path.GetFileNameWithoutExtension (file.Name), i),
+ Size = (uint)file.MaxOffset,
+ Type = "image",
+ Index = i,
+ });
+ }
+ return new ArcFile (file, this, dir);
+ }
+
+ static readonly ResourceInstance s_TlgFormat = new ResourceInstance ("TLG");
+
+ public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
+ {
+ var layer_entry = entry as TlgLayerEntry;
+ if (null == layer_entry)
+ return base.OpenImage (arc, entry);
+ var input = arc.File.CreateStream ();
+ try
+ {
+ var info = s_TlgFormat.Value.ReadMetaData (input);
+ if (null == info)
+ throw new InvalidFormatException ();
+ if (info is TlgMetaData tlg)
+ {
+ tlg.LayerIndex = layer_entry.Index;
+ }
+ return new ImageFormatDecoder (input, s_TlgFormat.Value, info);
+ }
+ catch
+ {
+ input.Dispose ();
+ throw;
+ }
+ }
+ }
+}
diff --git a/ArcFormats/KiriKiri/ImageTLG.cs b/ArcFormats/KiriKiri/ImageTLG.cs
index a715900f..3d533b70 100644
--- a/ArcFormats/KiriKiri/ImageTLG.cs
+++ b/ArcFormats/KiriKiri/ImageTLG.cs
@@ -25,6 +25,7 @@ namespace GameRes.Formats.KiriKiri
{
public int Version;
public long DataOffset;
+ public int LayerIndex;
}
[Export(typeof(ImageFormat))]
@@ -1520,7 +1521,7 @@ namespace GameRes.Formats.KiriKiri
throw new InvalidFormatException ();
}
if (0 != qhdr.Length)
- return DecodeMultiLayerQOI (src, info.Width, info.Height, qhdr, 0);
+ return DecodeMultiLayerQOI (src, info.Width, info.Height, qhdr, info.LayerIndex);
return DecodeQOI (src, info.Width, info.Height);
}