diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj
index f74ca076..891d1f2b 100644
--- a/ArcFormats/ArcFormats.csproj
+++ b/ArcFormats/ArcFormats.csproj
@@ -268,6 +268,7 @@
+
diff --git a/ArcFormats/KiriKiri/ImageTLG.cs b/ArcFormats/KiriKiri/ImageTLG.cs
index 0712af3e..47d208c0 100644
--- a/ArcFormats/KiriKiri/ImageTLG.cs
+++ b/ArcFormats/KiriKiri/ImageTLG.cs
@@ -8,6 +8,7 @@
// C# port by morkt
//
+using GameRes.Formats.QoiCodec;
using GameRes.Utility;
using K4os.Compression.LZ4;
using System;
@@ -1220,95 +1221,6 @@ namespace GameRes.Formats.KiriKiri
}
}
- static class QoiCodec
- {
- public const int Index = 0x00;
- public const int Diff = 0x40;
- public const int Luma = 0x80;
- public const int Run = 0xC0;
- public const int Rgb = 0xFE;
- public const int Rgba = 0xFF;
- public const int Mask2 = 0xC0;
- public const int HashTableSize = 64;
- }
-
- class QoiDecodeStream
- {
- readonly IBinaryStream m_input;
- readonly byte[] m_table;
- uint m_pixel;
-
- public QoiDecodeStream (IBinaryStream input)
- {
- m_input = input;
- m_table = new byte [4*QoiCodec.HashTableSize];
- m_pixel = 0xFF000000;
- }
-
- public int Read (out uint output)
- {
- var r = (byte)m_pixel;
- var g = (byte)(m_pixel >> 8);
- var b = (byte)(m_pixel >> 16);
- var a = (byte)(m_pixel >> 24);
- var run = 1;
- var b1 = m_input.ReadByte ();
- if (-1 == b1)
- throw new EndOfStreamException ();
- if (QoiCodec.Rgb == b1)
- {
- var rgb = m_input.ReadInt24 ();
- r = (byte)rgb;
- g = (byte)(rgb >> 8);
- b = (byte)(rgb >> 16);
- }
- else if (QoiCodec.Rgba == b1)
- {
- var rgba = m_input.ReadInt32 ();
- r = (byte)rgba;
- g = (byte)(rgba >> 8);
- b = (byte)(rgba >> 16);
- a = (byte)(rgba >> 24);
- }
- else if (QoiCodec.Index == (b1 & QoiCodec.Mask2))
- {
- var p1 = (b1 & ~QoiCodec.Mask2) * 4;
- r = m_table[p1 ];
- g = m_table[p1+1];
- b = m_table[p1+2];
- a = m_table[p1+3];
- }
- else if (QoiCodec.Diff == (b1 & QoiCodec.Mask2))
- {
- r += (byte)(((b1 >> 4) & 0x03) - 2);
- g += (byte)(((b1 >> 2) & 0x03) - 2);
- b += (byte)((b1 & 0x03) - 2);
- }
- else if (QoiCodec.Luma == (b1 & QoiCodec.Mask2))
- {
- var b2 = m_input.ReadByte ();
- if (-1 == b2)
- throw new EndOfStreamException ();
- var vg = (b1 & 0x3F) - 32;
- r += (byte)(vg - 8 + ((b2 >> 4) & 0x0F));
- g += (byte)vg;
- b += (byte)(vg - 8 + (b2 & 0x0F));
- }
- else if (QoiCodec.Run == (b1 & QoiCodec.Mask2))
- {
- run = (b1 & 0x3F) + 1;
- }
- var p2 = (r*3 + g*5 + b*7 + a*11) % QoiCodec.HashTableSize*4;
- m_table[p2 ] = r;
- m_table[p2+1] = g;
- m_table[p2+2] = b;
- m_table[p2+3] = a;
- m_pixel = (uint)(r | (g << 8) | (b << 16) | (a << 24));
- output = (uint)(b | (g << 8) | (r << 16) | (a << 24));
- return run;
- }
- }
-
class QoiBlockDecoder
{
readonly QoiDecodeStream m_qoi;
diff --git a/ArcFormats/QoiCodec.cs b/ArcFormats/QoiCodec.cs
new file mode 100644
index 00000000..377448bf
--- /dev/null
+++ b/ArcFormats/QoiCodec.cs
@@ -0,0 +1,118 @@
+//! \file QoiCodec.cs
+//! \date Sun Apr 12 2026
+//! \brief Quite OK Image Format
+//
+// Copyright (C) 2016 by morkt
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//
+
+using System.IO;
+
+namespace GameRes.Formats.QoiCodec
+{
+ static class QoiConst
+ {
+ public const int Index = 0x00;
+ public const int Diff = 0x40;
+ public const int Luma = 0x80;
+ public const int Run = 0xC0;
+ public const int Rgb = 0xFE;
+ public const int Rgba = 0xFF;
+ public const int Mask2 = 0xC0;
+ public const int HashTableSize = 64;
+ }
+
+ public class QoiDecodeStream
+ {
+ readonly IBinaryStream m_input;
+ readonly byte[] m_table;
+ uint m_pixel;
+
+ public QoiDecodeStream (IBinaryStream input)
+ {
+ m_input = input;
+ m_table = new byte [4*QoiConst.HashTableSize];
+ m_pixel = 0xFF000000;
+ }
+
+ public int Read (out uint output)
+ {
+ var r = (byte)m_pixel;
+ var g = (byte)(m_pixel >> 8);
+ var b = (byte)(m_pixel >> 16);
+ var a = (byte)(m_pixel >> 24);
+ var run = 1;
+ var b1 = m_input.ReadByte ();
+ if (-1 == b1)
+ throw new EndOfStreamException ();
+ if (QoiConst.Rgb == b1)
+ {
+ var rgb = m_input.ReadInt24 ();
+ r = (byte)rgb;
+ g = (byte)(rgb >> 8);
+ b = (byte)(rgb >> 16);
+ }
+ else if (QoiConst.Rgba == b1)
+ {
+ var rgba = m_input.ReadInt32 ();
+ r = (byte)rgba;
+ g = (byte)(rgba >> 8);
+ b = (byte)(rgba >> 16);
+ a = (byte)(rgba >> 24);
+ }
+ else if (QoiConst.Index == (b1 & QoiConst.Mask2))
+ {
+ var p1 = (b1 & ~QoiConst.Mask2) * 4;
+ r = m_table[p1 ];
+ g = m_table[p1+1];
+ b = m_table[p1+2];
+ a = m_table[p1+3];
+ }
+ else if (QoiConst.Diff == (b1 & QoiConst.Mask2))
+ {
+ r += (byte)(((b1 >> 4) & 0x03) - 2);
+ g += (byte)(((b1 >> 2) & 0x03) - 2);
+ b += (byte)((b1 & 0x03) - 2);
+ }
+ else if (QoiConst.Luma == (b1 & QoiConst.Mask2))
+ {
+ var b2 = m_input.ReadByte ();
+ if (-1 == b2)
+ throw new EndOfStreamException ();
+ var vg = (b1 & 0x3F) - 32;
+ r += (byte)(vg - 8 + ((b2 >> 4) & 0x0F));
+ g += (byte)vg;
+ b += (byte)(vg - 8 + (b2 & 0x0F));
+ }
+ else if (QoiConst.Run == (b1 & QoiConst.Mask2))
+ {
+ run = (b1 & 0x3F) + 1;
+ }
+ var p2 = (r*3 + g*5 + b*7 + a*11) % QoiConst.HashTableSize*4;
+ m_table[p2 ] = r;
+ m_table[p2+1] = g;
+ m_table[p2+2] = b;
+ m_table[p2+3] = a;
+ m_pixel = (uint)(r | (g << 8) | (b << 16) | (a << 24));
+ output = (uint)(b | (g << 8) | (r << 16) | (a << 24));
+ return run;
+ }
+ }
+}
diff --git a/ArcFormats/YuRis/ImageYDG.cs b/ArcFormats/YuRis/ImageYDG.cs
index 8aeae9b8..afa11e81 100644
--- a/ArcFormats/YuRis/ImageYDG.cs
+++ b/ArcFormats/YuRis/ImageYDG.cs
@@ -30,6 +30,7 @@ using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Media;
+using GameRes.Formats.QoiCodec;
using GameRes.Utility;
namespace GameRes.Formats.YuRis
@@ -233,96 +234,5 @@ namespace GameRes.Formats.YuRis
return output;
}
}
-
- static class QoiCodec
- {
- public const int Index = 0x00;
- public const int Diff = 0x40;
- public const int Luma = 0x80;
- public const int Run = 0xC0;
- public const int Rgb = 0xFE;
- public const int Rgba = 0xFF;
- public const int Mask2 = 0xC0;
- public const int HashTableSize = 64;
- }
-
- class QoiDecodeStream
- {
- readonly IBinaryStream m_input;
- readonly byte[] m_table;
- uint m_pixel;
-
- public QoiDecodeStream(IBinaryStream input)
- {
- m_input = input;
- m_table = new byte[4 * QoiCodec.HashTableSize];
- m_pixel = 0xFF000000;
- }
-
- public int Read(out uint output)
- {
- var r = (byte)m_pixel;
- var g = (byte)(m_pixel >> 8);
- var b = (byte)(m_pixel >> 16);
- var a = (byte)(m_pixel >> 24);
- var run = 1;
- var b1 = m_input.ReadByte();
- if (-1 == b1)
- throw new EndOfStreamException();
-
- if (QoiCodec.Rgb == b1)
- {
- var rgb = m_input.ReadInt24();
- r = (byte)rgb;
- g = (byte)(rgb >> 8);
- b = (byte)(rgb >> 16);
- }
- else if (QoiCodec.Rgba == b1)
- {
- var rgba = m_input.ReadInt32();
- r = (byte)rgba;
- g = (byte)(rgba >> 8);
- b = (byte)(rgba >> 16);
- a = (byte)(rgba >> 24);
- }
- else if (QoiCodec.Index == (b1 & QoiCodec.Mask2))
- {
- var p1 = (b1 & ~QoiCodec.Mask2) * 4;
- r = m_table[p1];
- g = m_table[p1 + 1];
- b = m_table[p1 + 2];
- a = m_table[p1 + 3];
- }
- else if (QoiCodec.Diff == (b1 & QoiCodec.Mask2))
- {
- r += (byte)(((b1 >> 4) & 0x03) - 2);
- g += (byte)(((b1 >> 2) & 0x03) - 2);
- b += (byte)((b1 & 0x03) - 2);
- }
- else if (QoiCodec.Luma == (b1 & QoiCodec.Mask2))
- {
- var b2 = m_input.ReadByte();
- if (-1 == b2)
- throw new EndOfStreamException();
- var vg = (b1 & 0x3F) - 32;
- r += (byte)(vg - 8 + ((b2 >> 4) & 0x0F));
- g += (byte)vg;
- b += (byte)(vg - 8 + (b2 & 0x0F));
- }
- else if (QoiCodec.Run == (b1 & QoiCodec.Mask2))
- {
- run = (b1 & 0x3F) + 1;
- }
-
- var p2 = (r * 3 + g * 5 + b * 7 + a * 11) % QoiCodec.HashTableSize * 4;
- m_table[p2 ] = r;
- m_table[p2 + 1] = g;
- m_table[p2 + 2] = b;
- m_table[p2 + 3] = a;
- m_pixel = (uint)(r | (g << 8) | (b << 16) | (a << 24));
- output = (uint)(b | (g << 8) | (r << 16) | (a << 24));
- return run;
- }
- }
}
}