diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj
index dcea0a57..9a5e8763 100644
--- a/ArcFormats/ArcFormats.csproj
+++ b/ArcFormats/ArcFormats.csproj
@@ -90,6 +90,7 @@
+
@@ -228,6 +229,7 @@
+
diff --git a/ArcFormats/rUGP/ArcRIO.cs b/ArcFormats/rUGP/ArcRIO.cs
new file mode 100644
index 00000000..aaddea77
--- /dev/null
+++ b/ArcFormats/rUGP/ArcRIO.cs
@@ -0,0 +1,1385 @@
+//! \file ArcRIO.cs
+//! \date Thu Nov 03 13:21:56 2016
+//! \brief rUGP engine resource archive.
+//
+// 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;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+namespace GameRes.Formats.Rugp
+{
+ [Export(typeof(ArchiveFormat))]
+ public class RioOpener : ArchiveFormat
+ {
+ public override string Tag { get { return "RIO"; } }
+ public override string Description { get { return "rUGP engine resource archive"; } }
+ public override uint Signature { get { return 0; } }
+ public override bool IsHierarchic { get { return false; } }
+ public override bool CanWrite { get { return false; } }
+
+ public RioOpener ()
+ {
+ Signatures = new uint[] { CRioArchive.RioSignature, 0 };
+ }
+
+ static readonly Dictionary SupportedClasses = new Dictionary {
+ { "CRip007", "image" },
+ { "CRip", "image" },
+ { "CS5i", "image" },
+ { "CIcon", "image" },
+ { "CRsa", "script" },
+ { "CVmFunc", "script" },
+ { "CrelicHicompAudio", "audio" },
+ };
+
+ public override ArcFile TryOpen (ArcView file)
+ {
+ using (var reader = RioReader.Create (file))
+ {
+ if (null == reader)
+ return null;
+ reader.DeserializeRelic();
+ var nodes = reader.Arc.LoadArray.OfType();
+ var types = nodes.Select (n => n.ClassName).Distinct();
+ var dir = nodes.Where (n => SupportedClasses.ContainsKey (n.ClassName))
+ .Select (n => new Entry {
+ Name = n.Name,
+ Type = SupportedClasses[n.ClassName],
+ Offset = n.Offset,
+ Size = n.Size
+ });
+ if (!dir.Any())
+ return null;
+ return new ArcFile (file, this, dir.ToList());
+ }
+ }
+ }
+
+ internal sealed class RioReader : IDisposable
+ {
+ IBinaryStream m_input;
+ IBinaryStream m_toc;
+ CRioArchive m_arc;
+ bool m_read_toc;
+ CrelicUnitedGameProject m_relic;
+
+ const uint IciKey = 0xB29D5A0C;
+
+ public CRioArchive Arc { get { return m_arc; } }
+
+ public CrelicUnitedGameProject DeserializeRelic ()
+ {
+ if (!m_read_toc)
+ {
+ m_read_toc = true;
+ m_relic = m_arc.DeserializeRoot() as CrelicUnitedGameProject;
+ if (m_toc != m_input)
+ {
+ m_toc.Dispose();
+ m_toc = m_input;
+ m_arc.SetSource (m_input);
+ }
+ }
+ return m_relic;
+ }
+
+ static public RioReader Create (ArcView file)
+ {
+ if (CRioArchive.RioSignature == file.View.ReadUInt32 (0))
+ return new RioReader (file);
+
+ if (file.Name.EndsWith (".ici", StringComparison.InvariantCultureIgnoreCase))
+ return null;
+ var ici_name = file.Name + ".ici";
+ if (!VFS.FileExists (ici_name))
+ {
+ ici_name = Path.ChangeExtension (file.Name, ".ici");
+ if (!VFS.FileExists (ici_name))
+ return null;
+ }
+ byte[] ici_data;
+ using (var ici = VFS.OpenBinaryStream (ici_name))
+ ici_data = ReadIci (ici, IciKey);
+
+ CObjectArcMan arc_man;
+ using (var ici = new BinMemoryStream (ici_data))
+ {
+ var rio = new CRioArchive (ici);
+ arc_man = rio.DeserializeRoot() as CObjectArcMan;
+ if (null == arc_man)
+ return null;
+ }
+ var base_name = Path.GetFileName (file.Name);
+ var arc_object = arc_man.ArcList.FirstOrDefault();
+ if (null == arc_object || !base_name.Equals (arc_object.RioName, StringComparison.InvariantCultureIgnoreCase))
+ return null;
+ return new RioReader (arc_man, file);
+ }
+
+ private RioReader (ArcView file)
+ {
+ m_input = file.CreateStream();
+ m_toc = m_input;
+ m_arc = new CRioArchive (m_input);
+ }
+
+ private RioReader (CObjectArcMan arc_man, ArcView file)
+ {
+ long toc_offset = arc_man.TocOffset;
+ uint signature = file.View.ReadUInt32 (toc_offset);
+ int shift = 0;
+ if (signature != CRioArchive.EncryptedSignature)
+ {
+ toc_offset *= 2;
+ signature = file.View.ReadUInt32 (toc_offset);
+ if (signature != CRioArchive.EncryptedSignature)
+ throw new InvalidFormatException ("CPmArchive signature not found");
+ shift = 1;
+ }
+ m_input = file.CreateStream();
+ m_toc = file.CreateStream (toc_offset, (uint)arc_man.TocSize);
+ m_arc = new CRioArchive (m_toc, shift, true);
+ }
+
+ public CObject ReadObject (COceanNode node)
+ {
+ return m_arc.ReadObject (node);
+ }
+
+ static byte[] ReadIci (IBinaryStream ici, uint key)
+ {
+ var rio = new CRioArchive (ici);
+ var ici_data = rio.ReadEncrypted (key);
+ return DecryptIci (ici_data);
+ }
+
+ static byte[] DecryptIci (byte[] input)
+ {
+ var output = new byte[input.Length];
+ int src = 0;
+ int dst = 0;
+ int tail_size;
+ int chunk_count = Math.DivRem (input.Length, 6, out tail_size);
+ for (int n = chunk_count; n > 0; --n)
+ {
+ output[dst++] = input[src];
+ output[dst++] = input[src + chunk_count];
+ output[dst++] = input[src + chunk_count * 2];
+ output[dst++] = input[src + chunk_count * 3];
+ output[dst++] = input[src + chunk_count * 4];
+ output[dst++] = input[src + chunk_count * 5];
+ ++src;
+ }
+ if (tail_size > 0)
+ Buffer.BlockCopy (input, input.Length - tail_size, output, dst, tail_size);
+
+ byte acc = 0;
+ for (int i = 0; i < output.Length; ++i)
+ {
+ output[i] -= acc;
+ acc += output[i];
+ output[i] ^= 0xA5;
+ }
+
+ src = 0;
+ dst = 0;
+ chunk_count = Math.DivRem (input.Length, 5, out tail_size);
+ for (int n = chunk_count; n > 0; --n)
+ {
+ input[dst++] = output[src];
+ input[dst++] = output[src + chunk_count];
+ input[dst++] = output[src + chunk_count * 2];
+ input[dst++] = output[src + chunk_count * 3];
+ input[dst++] = output[src + chunk_count * 4];
+ ++src;
+ }
+ if (tail_size > 0)
+ Buffer.BlockCopy (output, output.Length - tail_size, input, dst, tail_size);
+
+ acc = 0;
+ for (int i = input.Length-1; i >= 0; --i)
+ {
+ input[i] -= acc;
+ acc += input[i];
+ }
+
+ src = 0;
+ dst = 0;
+ chunk_count = Math.DivRem (input.Length, 3, out tail_size);
+ for (int n = chunk_count; n > 0; --n)
+ {
+ output[dst++] = (byte)(input[src] ^ 0x18);
+ output[dst++] = (byte)(input[src + chunk_count] ^ 0x3F);
+ output[dst++] = (byte)(input[src + chunk_count * 2] ^ 0xE2);
+ ++src;
+ }
+ if (tail_size > 0)
+ Buffer.BlockCopy (input, input.Length - tail_size, output, dst, tail_size);
+
+ return output;
+ }
+
+ #region IDisposable Members
+ bool _disposed = false;
+ public void Dispose ()
+ {
+ if (!_disposed)
+ {
+ m_input.Dispose();
+ if (m_toc != m_input)
+ m_toc.Dispose();
+ _disposed = true;
+ }
+ }
+ #endregion
+ }
+
+ internal class CRioArchive
+ {
+ IBinaryStream m_input;
+ int m_field_4C;
+ string m_field_50;
+ int m_field_54;
+ bool m_field_60;
+ int m_shift;
+ int m_objectSchema = -1;
+
+ Dictionary m_OceanMap = new Dictionary();
+ ArrayList m_LoadArray = new ArrayList();
+
+ public IBinaryStream Input { get { return m_input; } }
+ public ArrayList LoadArray { get { return m_LoadArray; } }
+ public bool IsEncrypted { get { return 0 != (m_field_4C & 4); } }
+ private bool m_field_5C { get { return IsEncrypted; } }
+
+ public const uint EncryptedSignature = 0x1EDB927C;
+ public const uint ObjectSignature = 0x29F6CBA4;
+ public const uint RioSignature = 0x596E32CD;
+ public const uint IciSignature = 0x673CE92A;
+
+ public CRioArchive (IBinaryStream input)
+ {
+ m_input = input;
+ }
+
+ public CRioArchive (IBinaryStream input, int shift, bool encrypted) : this (input)
+ {
+ m_shift = shift;
+ if (encrypted)
+ m_field_4C |= 4;
+ }
+
+ public IBinaryStream SetSource (IBinaryStream source)
+ {
+ var prev = m_input;
+ m_input = source;
+ return prev;
+ }
+
+ public int GetObjectSchema ()
+ {
+ int schema = m_objectSchema;
+ m_objectSchema = -1;
+ return schema;
+ }
+
+ public CObject DeserializeRoot ()
+ {
+ PopulateLoadArray();
+ uint signature;
+ var arc_class = LoadRioTypeCore (out signature);
+ var obj = CreateObject (arc_class);
+ MapObjectEntry (obj);
+ if (RioSignature == signature)
+ obj.Flags |= 0x80;
+ else if (EncryptedSignature == signature)
+ obj.Flags |= 0x180;
+ DeserializeClassList (obj);
+ obj.Deserialize (this);
+ return obj;
+ }
+
+ public CObject ReadObject (COceanNode node)
+ {
+ var obj = CreateObject (node.Name);
+ PopulateLoadArray();
+ m_input.Position = ((long)node.Offset << m_shift);
+ int f1 = ReadByte() & 3;
+ int f2 = ReadByte();
+ int f3 = ReadByte();
+ int f4 = 0;
+ switch (f2 >> 6)
+ {
+ case 0: f4 = f3 >> 6; break;
+ case 1: f4 = f3 >> 2; break;
+ case 2: f4 = (f3 & 0xFE) << 1 | (ReadByte() & 0xC0) << 1; break;
+ case 3: f4 = (f3 & 0xFE) << 1 | (ReadByte() & 0xFE) << 6; break;
+ }
+ obj.Flags = node.Flags;
+ MapObjectEntry (obj);
+ if (2 == f1)
+ {
+ int flags = ReadUInt16();
+ m_field_4C = (m_field_4C & 0xFFFF) | flags << 16;
+ }
+ else if (3 == f1)
+ {
+ int schema = ReadUInt16();
+ int flags = ReadUInt16();
+ m_field_4C = (m_field_4C & 0xFFFF) | flags << 16;
+ }
+ obj.Deserialize (this);
+ return obj;
+ }
+
+ CObject CreateObject (string class_name)
+ {
+ if (!s_classTable.ContainsKey (class_name))
+ throw new InvalidFormatException (string.Format ("[RIO] Unknown class '{0}'", class_name));
+ return s_classTable[class_name].CreateObject();
+ }
+
+ void PopulateLoadArray ()
+ {
+ m_LoadArray.Clear();
+ m_LoadArray.Add (null);
+ m_LoadArray.Add (this);
+ }
+
+ static readonly ISet CoreSignatures = new HashSet {
+ IciSignature, EncryptedSignature, RioSignature, ObjectSignature
+ };
+
+ public string LoadRioTypeCore (out uint signature)
+ {
+ signature = m_input.ReadUInt32();
+ if (!CoreSignatures.Contains (signature))
+ throw new InvalidFormatException ("[RIO] invalid signature");
+ int version = ReadUInt16();
+ if (version < 0x10 || version > 0x3FFF)
+ throw new InvalidFormatException ("[RIO] invalid version");
+ if (version >= 0x11)
+ {
+ m_field_4C &= 0xFFFF;
+ m_field_4C |= ReadUInt16() << 16;
+ }
+ if (EncryptedSignature == signature)
+ m_field_4C |= 0xC;
+
+ return ReadClass();
+ }
+
+ int m_depth = 0;
+ const int MaxRecursionDepth = 40; // arbitrary
+
+ void DeserializeClassList (CObject root)
+ {
+ if (IsEncrypted && 0 != (root.Flags & 0x200))
+ return;
+ try
+ {
+ if (++m_depth > MaxRecursionDepth)
+ throw new InvalidFormatException ("[RIO] deserialization recursion limit exceeded");
+ int count = ReadCount();
+ for (int i = 0; i < count; ++i)
+ {
+ if (IsEncrypted)
+ {
+ var node = CreateOceanEntry1 ("unrefix");
+ DeserializeNode (node, node != null);
+ if (node != null)
+ node.Parent = root;
+ }
+ else
+ {
+ m_field_50 = ReadString();
+ m_field_54 = 0;
+ var node = CreateOceanEntry2 (m_field_50);
+ if (node != null)
+ {
+ MapObjectEntry (node);
+ DeserializeNode (node);
+ node.Parent = root;
+ }
+ else
+ {
+ node = FindObject (m_field_50) as COceanNode;
+ MapObjectEntry (node);
+ DeserializeNode (node, false);
+ }
+ }
+ }
+ }
+ finally
+ {
+ --m_depth;
+ }
+ }
+
+ void DeserializeNode (COceanNode node, bool store_to_map = true)
+ {
+ int flags = ReadUInt16(); // this.field_1E
+ string class_ref;
+ switch (flags & 7)
+ {
+ case 0:
+ if (0 != (flags & 0x8000))
+ ReadByte();
+ else
+ ReadUInt16();
+ class_ref = ReadClass();
+ break;
+
+ case 1:
+ ReadInt32();
+ class_ref = ReadCType();
+ break;
+
+ default:
+ throw new InvalidFormatException();
+ }
+ node.ClassName = class_ref;
+ if (node != null)
+ {
+ if (store_to_map)
+ node.Flags = flags;
+ if (0 != (flags & 8))
+ {
+ if (!store_to_map)
+ node.Flags |= 8;
+ int id1 = ReadInt32(); // this.field_20
+ int id2 = ReadInt32(); // this.field_24
+ if (IsEncrypted && store_to_map)
+ {
+ node.Flags |= 0x100;
+ if (!m_OceanMap.ContainsKey (id1))
+ m_OceanMap[id1] = node;
+ }
+ if (node != null)
+ {
+ node.Offset = (uint)id1;
+ node.Size = (uint)id2;
+ }
+ }
+ }
+ else
+ {
+ if (0 == (flags & 8))
+ throw new InvalidFormatException();
+ int id = ReadInt32();
+ ReadInt32();
+ if (!m_OceanMap.TryGetValue (id, out node))
+ throw new InvalidFormatException();
+ }
+ DeserializeClassList (node);
+ }
+
+ string ReadCType ()
+ {
+ int c = ReadUInt16();
+ switch (c)
+ {
+ case 0x1E57:
+ return ReadClass();
+ case 0x2D6B:
+ return ReadBasicType();
+ case 0x2F1A:
+ return ReadBasicType();
+ case 0x369E:
+ if (m_field_54 > 0x13)
+ return ReadMsgClass();
+ else
+ return ReadMessage();
+ default:
+ throw new InvalidFormatException();
+ }
+ }
+
+ string ReadMessage ()
+ {
+ int id = ReadUInt16();
+ int cLen = ReadUInt16();
+ if (id != -1 || cLen >= 0x400)
+ throw new InvalidFormatException();
+ var buffer = new byte[cLen];
+ if (cLen != Read (buffer, 0, cLen))
+ return null;
+ var name = Encodings.cp932.GetString (buffer, 2, cLen-2);
+ return GetRtcFromMessageName (name);
+ }
+
+ string ReadBasicType ()
+ {
+ ReadUInt16();
+ int cLen = ReadUInt16();
+ if (cLen >= 0x40 || cLen != Read (m_name_buf, 0, cLen))
+ throw new InvalidFormatException();
+ var name = Encodings.cp932.GetString (m_name_buf, 0, cLen);
+ if (!s_basicTypeList.ContainsKey (name))
+ throw new InvalidFormatException (string.Format ("[RIO] Unknown basic type '{0}'", name));
+ return s_basicTypeList[name];
+ }
+
+ string ReadMsgClass ()
+ {
+ int wTag = ReadUInt16();
+ int obTag;
+ if (0x7FFF == wTag)
+ obTag = ReadInt32();
+ else
+ obTag = ((wTag & wClassTag) << 16) | (wTag & ~wClassTag);
+
+ if (0 == (obTag & dwBigClassTag))
+ throw new InvalidFormatException ("[RIO] invalid message class");
+ if (0xFFFF == wTag)
+ {
+ throw new NotImplementedException();
+ }
+ else
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ string GetRtcFromMessageName (string name)
+ {
+ throw new NotImplementedException();
+ }
+
+ void MapObjectEntry (CObject node) // CArchive::MapObject
+ {
+ m_LoadArray.Add (node);
+ }
+
+ CObject FindObject (string name)
+ {
+ throw new NotImplementedException();
+ }
+
+ COceanNode CreateOceanEntry1 (string name)
+ {
+ return new COceanNode (name);
+ }
+
+ COceanNode CreateOceanEntry2 (string name)
+ {
+ return new COceanNode (name);
+ }
+
+ COceanNode CreateAnonymousRio (string name)
+ {
+ throw new NotImplementedException();
+ }
+
+ const int wClassTag = 0x8000; // 0x8000 indicates class tag (OR'd)
+ const int dwBigClassTag = (int)-0x80000000; // 0x8000000 indicates big class tag (OR'd)
+
+ public CObject ReadRioReference (string base_ref)
+ {
+ if (!m_field_60)
+ {
+ m_field_60 = true;
+ if (m_field_5C)
+ {
+ int count = ReadShortCount();
+ for (int i = 0; i < count; ++i)
+ MapObjectEntry (null); // new CObject()
+ }
+ }
+ int tag;
+ var class_ref = ReadClass (out tag);
+ if (null == class_ref)
+ {
+ if (tag >= m_LoadArray.Count)
+ throw new InvalidFormatException ("Bad class");
+ return m_LoadArray[tag] as CObject;
+ }
+
+ COceanNode obj;
+ int flags = ReadUInt16();
+ if (0 != (flags & 0x40))
+ {
+ obj = CreateAnonymousRio (class_ref);
+ MapObjectEntry (obj);
+ return obj;
+ }
+ int id1 = 0, id2 = 0;
+ string name = null;
+ if (IsEncrypted)
+ {
+ id1 = ReadInt32();
+ id2 = ReadInt32();
+ }
+ else
+ {
+ name = ReadString();
+ }
+ var rio = ReadRioReference ("CRio");
+ if (null == rio)
+ throw new InvalidFormatException();
+ if (0 != (flags & 7))
+ ReadInt32();
+ else if (0 != (flags & 0x8000))
+ ReadByte();
+ else
+ ReadUInt16();
+ if (IsEncrypted)
+ {
+ obj = ReadEncryptedObject (rio, class_ref, flags, id1, id2);
+ obj.Name = base_ref;
+ }
+ else
+ {
+ throw new NotImplementedException();
+ }
+ MapObjectEntry (obj);
+ return obj;
+ }
+
+ string ReadClass ()
+ {
+ int tag;
+ return ReadClass (out tag);
+ }
+
+ string ReadClass (out int obTag)
+ {
+ int wTag = ReadUInt16();
+ if (0x7FFF == wTag)
+ {
+ obTag = ReadInt32();
+ }
+ else
+ {
+ obTag = ((wTag & wClassTag) << 16) | (wTag & ~wClassTag);
+ }
+ if (0 == (obTag & dwBigClassTag))
+ {
+ return null;
+ }
+
+ string class_ref;
+ if (0xFFFF == wTag)
+ {
+ uint schema = 0;
+ if ((m_field_4C & 8) != 0)
+ class_ref = LoadScrambledClass (out schema);
+ else
+ class_ref = LoadRuntimeClass (out schema);
+ if (null == class_ref)
+ throw new InvalidFormatException();
+// m_objectSchema = (int)schema;
+
+ m_LoadArray.Add (class_ref);
+ }
+ else
+ {
+ obTag &= 0x7FFFFFFF;
+ if (0 == obTag || obTag >= m_LoadArray.Count)
+ throw new InvalidFormatException ("Bad class");
+ class_ref = (string)m_LoadArray[obTag];
+ }
+ return class_ref;
+ }
+
+ byte[] m_name_buf = new byte[0x100];
+
+ // loads a runtime class description
+ string LoadRuntimeClass (out uint schema)
+ {
+ schema = ReadUInt16();
+ int nLen = ReadUInt16();
+
+ // load the class name
+ if (nLen >= 0x40 || Read (m_name_buf, 0, nLen) != nLen)
+ return null;
+ return Encoding.ASCII.GetString (m_name_buf, 0, nLen);
+ }
+
+ string LoadScrambledClass (out uint schema)
+ {
+ schema = ReadUInt16();
+ int length = ReadByte();
+ if (0xFF == length)
+ {
+ length = ReadUInt16();
+ }
+ if (length >= 0x100 || Read (m_name_buf, 0, length) != length)
+ return null;
+
+ return DecodeClassName (m_name_buf, length);
+ }
+
+ COceanNode ReadEncryptedObject (CObject rio, string class_ref, int flags, int id1, int id2)
+ {
+ COceanNode node;
+ if (!m_OceanMap.TryGetValue (id1, out node))
+ throw new InvalidFormatException();
+ if (node != null)
+ {
+ node.Offset = DecodeOffset (id1);
+ node.Size = DecodeSize (id2);
+ }
+ return node;
+ }
+
+ public int ReadInt32 ()
+ {
+ return m_input.ReadInt32();
+ }
+
+ public long ReadInt64 ()
+ {
+ return m_input.ReadInt64();
+ }
+
+ public ushort ReadUInt16 ()
+ {
+ return m_input.ReadUInt16();
+ }
+
+ public byte ReadByte ()
+ {
+ return m_input.ReadUInt8();
+ }
+
+ public int Read (byte[] buffer, int offset, int count)
+ {
+ return m_input.Read (buffer, offset, count);
+ }
+
+ public byte[] ReadBytes (int count)
+ {
+ return m_input.ReadBytes (count);
+ }
+
+ public bool ReadBool ()
+ {
+ return m_input.ReadUInt8() != 0;
+ }
+
+ public string ReadString ()
+ {
+ int nLength = ReadStringLength();
+ if (0 == nLength)
+ return "";
+ if (nLength < 0)
+ throw new InvalidFormatException();
+ var buffer = m_input.ReadBytes (nLength);
+ if (buffer.Length != nLength)
+ throw new EndOfStreamException();
+ return Encodings.cp932.GetString (buffer);
+ }
+
+ public int ReadCount ()
+ {
+ int count = ReadUInt16();
+ if (count != 0xFFFF)
+ return count;
+ return ReadInt32();
+ }
+
+ public int ReadShortCount ()
+ {
+ int count = ReadByte();
+ if (count != 0xFF)
+ return count;
+ return ReadUInt16();
+ }
+
+ int ReadStringLength ()
+ {
+ // First, try to read a one-byte length
+ int length = ReadByte();
+ if (length < 0xFF)
+ return length;
+
+ // Try a two-byte length
+ length = ReadUInt16();
+ if (0xFFFE == length)
+ throw new NotSupportedException ("[RIO] Unicode strings not supported");
+ if (length < 0xFFFF)
+ return length;
+
+ // 4-byte length
+ return ReadInt32();
+ }
+
+ public byte[] ReadEncrypted (uint key)
+ {
+ uint size1 = m_input.ReadUInt32() ^ 0xC92E568B;
+ uint size2 = m_input.ReadUInt32() ^ 0xC92E568F;
+ size2 >>= 3;
+ size1 = ~size1;
+ if (size1 != size2)
+ throw new InvalidFormatException ("Invalid encrypted chunk");
+ var ici_data = new byte[size1];
+ int dst = 0;
+ while (dst < ici_data.Length)
+ {
+ ushort checksum = 0;
+ int portion = Math.Min (0x20, ici_data.Length - dst);
+ portion = m_input.Read (ici_data, dst, portion);
+ for (int i = portion; i > 0; --i)
+ {
+ byte b = (byte)(ici_data[dst] ^ key);
+ ici_data[dst++] = b;
+ checksum += (ushort)(b * i);
+ uint bit = key;
+ bit = (bit >> 15) & 1;
+ key = ~(bit + key*2 + 0xA3B376C9u);
+ }
+ if (portion < 0x20)
+ break;
+ ushort chunk_sum = m_input.ReadUInt16();
+ if (chunk_sum != checksum)
+ throw new InvalidFormatException ("Encrypted chunk checksum mismatch");
+ }
+ return ici_data;
+ }
+
+ static uint DecodeOffset (int offset)
+ {
+ return (uint)offset - 0xA2FB6AD1;
+ }
+
+ static uint DecodeSize (int size)
+ {
+ uint a = (uint)size - 0xE7B5D9F8;
+ uint b = a >> 13;
+ return (a - (b & 0xFFF)) << 19 | b;
+ }
+
+ static string DecodeClassName (byte[] enc, int length)
+ {
+ using (var output = new MemoryStream())
+ using (var input = new MemoryStream (enc, 0, length))
+ using (var bits = new LsbBitStream (input))
+ {
+ if (0 == bits.GetNextBit())
+ output.WriteByte ((byte)'C');
+ for (;;)
+ {
+ int b = bits.GetNextBit();
+ if (-1 == b)
+ break;
+ int c;
+ if (0 == b)
+ {
+ int idx = bits.GetBits (4);
+ if (-1 == idx)
+ break;
+ c = CharMap1[idx];
+ }
+ else if (bits.GetNextBit() != 0)
+ {
+ int idx = bits.GetBits (5);
+ if (-1 == idx)
+ break;
+ c = CharMap3[idx];
+ }
+ else
+ {
+ int idx = bits.GetBits (4);
+ if (-1 == idx)
+ break;
+ if (idx != 0)
+ {
+ c = CharMap2[idx];
+ }
+ else
+ {
+ c = bits.GetBits (8);
+ if (-1 == c)
+ break;
+ }
+ }
+ output.WriteByte ((byte)c);
+ }
+ var buf = output.GetBuffer();
+ return Encodings.cp932.GetString (buf, 0, (int)output.Length);
+ }
+ }
+
+ static readonly char[] CharMap1 = {
+ 'e', 'a', 'i', 't', 'r', 'o', 's', 'd', 'u', 'c', 'm', 'n', 'S', 'g', 'l', 'R' };
+ static readonly char[] CharMap2 = {
+ '\x1', 'C', 'O', 'F', 'L', 'f', 'B', 'M', 'x', 'p', 'h', 'y', 'A', 'V', 'b', 'I' };
+ static readonly char[] CharMap3 = {
+ 'E', 'H', 'T', 'D', 'P', 'W', 'X', 'k', 'q', 'v', 'N', 'j', 'w', 'G', 'z', '0',
+ '2', 'U', '_', 'K', '1', '5', 'J', 'Q', 'Z', '4', '6', '7', '8', '3', '9', '\x0' };
+
+ static readonly Dictionary s_basicTypeList = new Dictionary
+ {
+ { "バイト", "byte" },
+ { "短正整数", "short" },
+ { "短整数", "ushort" },
+ { "正整数", "int" },
+ { "整数", "uint" },
+ { "色", "Color" },
+ };
+
+ static readonly Dictionary s_classTable = new Dictionary
+ {
+ { "CObjectArcMan", new CObjectFactory() },
+ { "CrelicUnitedGameProject", new CObjectFactory() },
+ { "CStdb", new CObjectFactory() },
+ };
+ }
+
+ internal abstract class CObject
+ {
+ public int Flags;
+ public string ClassName;
+
+ public abstract void Deserialize (CRioArchive arc);
+ }
+
+ internal class CStringArray : CObject, IReadOnlyList
+ {
+ string[] m_data = new string[0];
+
+ public int Count { get { return m_data.Length; } }
+
+ public string this[int index]
+ {
+ get { return m_data[index]; }
+ set { m_data[index] = value; }
+ }
+
+ public void SetSize (int count)
+ {
+ Array.Resize (ref m_data, count);
+ }
+
+ public override void Deserialize (CRioArchive arc)
+ {
+ int count = arc.ReadCount();
+ SetSize (count);
+ for (int i = 0; i < count; ++i)
+ m_data[i] = arc.ReadString();
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ foreach (var s in m_data)
+ yield return s;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator ()
+ {
+ return m_data.GetEnumerator();
+ }
+ }
+
+ internal class CPtrArray : CObject, IReadOnlyList where CType : CObject, new()
+ {
+ public CType[] m_data = new CType[0];
+
+ public int Count { get { return m_data.Length; } }
+
+ public CType this[int index]
+ {
+ get { return m_data[index]; }
+ set { m_data[index] = value; }
+ }
+
+ public void SetSize (int count)
+ {
+ Array.Resize (ref m_data, count);
+ }
+
+ public override void Deserialize (CRioArchive arc)
+ {
+ int count = arc.ReadCount();
+ SetSize (count);
+ for (int i = 0; i < count; ++i)
+ {
+ if (arc.ReadBool())
+ {
+ var obj = new CType();
+ m_data[i] = obj;
+ obj.Deserialize (arc);
+ }
+ }
+
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ foreach (var item in m_data)
+ yield return item;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator ()
+ {
+ return m_data.GetEnumerator ();
+ }
+ }
+
+ internal class CInstallSource : CObject
+ {
+ public ushort Version;
+ public int field_14;
+ public int field_18;
+ public string RioName; // rio filename [dst]
+ public long RioOffset; // rio offset
+ public long RioSize; // rio size
+ public int field_A8;
+ public int field_B0;
+ public byte[] field_D4;
+
+ public override void Deserialize (CRioArchive arc)
+ {
+ Version = arc.ReadUInt16();
+ if (Version >= 7)
+ {
+ field_14 = arc.ReadInt32();
+ field_18 = arc.ReadInt32();
+ arc.ReadByte();
+ arc.ReadString();
+ }
+ arc.ReadString(); // registry branch
+ arc.ReadString(); // disk name
+ arc.ReadString(); // rio filename [src]
+ arc.ReadString();
+ arc.ReadString();
+ arc.ReadInt64(); // rio offset [=0]
+ arc.ReadInt64(); // rio size
+ if (Version < 6)
+ {
+ arc.ReadInt32();
+ arc.ReadInt32();
+ }
+ else
+ {
+ arc.ReadInt32();
+ }
+ RioName = arc.ReadString();
+ RioOffset = arc.ReadInt64();
+ RioSize = arc.ReadInt64();
+ if (Version < 6)
+ {
+ arc.ReadInt64();
+ }
+ arc.ReadInt32();
+ arc.ReadString();
+ arc.ReadInt32();
+ arc.ReadInt32();
+ arc.ReadInt32();
+ arc.ReadInt32();
+ arc.ReadInt32();
+ arc.ReadString();
+ int count = arc.ReadCount();
+ arc.ReadBytes (count*4);
+ PrepareBuffer(); // sub_10011700 (this);
+ arc.Read (field_D4, 0, field_D4.Length);
+ }
+
+ void PrepareBuffer ()
+ {
+ field_B0 = (int)((RioSize + 0xFFFF) >> 16);
+ field_A8 = 16;
+ int length = (field_B0 + 7) >> 3;
+ field_D4 = new byte[length];
+ }
+ }
+
+ internal class CObjectArcMan : CObject
+ {
+ public int Version;
+ public string Title;
+ public CPtrArray ArcList = new CPtrArray();
+ public int field_14 = 0x1873BE26;
+ public int field_1C;
+ public int field_20;
+ public int TocOffset; // RioTocOffset
+ public int TocSize; // RioTocSize
+ public int field_38 = 0x14;
+ public string RioFileName;
+ public CStringArray field_80 = new CStringArray();
+ public int field_98 = 0x30;
+
+ public override void Deserialize (CRioArchive arc)
+ {
+ Version = arc.ReadInt32();
+ field_14 = arc.ReadInt32();
+ arc.ReadByte();
+ arc.ReadByte();
+ if (Version < 10)
+ {
+ field_1C = 0;
+ field_20 = 0;
+ }
+ else
+ {
+ field_1C = arc.ReadInt32();
+ field_20 = arc.ReadInt32();
+ }
+ arc.ReadInt32();
+ arc.ReadInt32();
+ arc.ReadInt32();
+ if (Version >= 6)
+ {
+ TocOffset = arc.ReadInt32();
+ TocSize = arc.ReadInt32();
+ arc.ReadInt32();
+ }
+ if (Version >= 8)
+ field_38 = arc.ReadInt32();
+ Title = arc.ReadString();
+ arc.ReadInt32();
+ arc.ReadString();
+ arc.ReadInt32();
+ arc.ReadString();
+ arc.ReadString(); // registry branch
+ arc.ReadString();
+ arc.ReadInt32();
+ arc.ReadString();
+ field_80.Deserialize (arc);
+ arc.ReadInt32();
+ if (Version >= 9)
+ RioFileName = arc.ReadString();
+ if (Version >= 7)
+ arc.ReadString(); // InstallManual
+ if (Version >= 5)
+ field_98 = arc.ReadInt32();
+ ArcList.Deserialize (arc); // CPtrArray::Serialize
+ for (int i = 0; i < ArcList.Count; ++i)
+ {
+ var entry = ArcList[i];
+ if (entry != null)
+ {
+ entry.field_14 = field_1C;
+ entry.field_18 = field_20;
+ }
+ }
+ }
+ }
+
+ internal class CrelicUnitedGameProject : CObject
+ {
+ public int Version;
+ public CObject field_08;
+ public CObject field_0C;
+ public CObject field_10;
+ public CObject field_14;
+ public CObject field_18;
+ public CObject field_1C;
+ public CObject field_24;
+ public CObject field_28;
+ public CObject field_2C;
+ public CObject field_30;
+ public CUnknown1 field_34 = new CUnknown1();
+ public CObject field_38;
+
+ internal const uint RioKey = 0x7E6B8CE2;
+
+ public override void Deserialize (CRioArchive arc)
+ {
+ if (arc.IsEncrypted)
+ {
+ var data = arc.ReadEncrypted (RioKey);
+ using (var input = new BinMemoryStream (data))
+ {
+ var prev_source = arc.SetSource (input);
+ try
+ {
+ ReadRelic (arc);
+ }
+ finally
+ {
+ arc.SetSource (prev_source);
+ }
+ }
+ }
+ else
+ ReadRelic (arc);
+ }
+
+ void ReadRelic (CRioArchive arc)
+ {
+ Version = arc.ReadInt32();
+ if (Version >= 0x24)
+ {
+ field_24 = arc.ReadRioReference ("CDatabaseBase"); // UnivUI
+ field_28 = arc.ReadRioReference ("CDatabaseBase");
+ field_10 = arc.ReadRioReference ("CBoxOcean"); // rvmm
+ field_14 = arc.ReadRioReference ("CObjectOcean"); // UnivUI
+ field_18 = arc.ReadRioReference ("CObjectOcean"); // UnivUI
+ field_0C = arc.ReadRioReference ("CProcessOcean"); // Vm60
+ if (Version >= 0x25)
+ field_30 = arc.ReadRioReference ("CStdb"); // UnivUI
+ if (Version >= 0x26)
+ field_2C = arc.ReadRioReference ("CRio"); // UnivUI
+ if (Version >= 0x27)
+ field_1C = arc.ReadRioReference ("CRio");
+ if (Version >= 0x29)
+ field_38 = arc.ReadRioReference ("CRio");
+ field_34.Deserialize (arc);
+ if (Version >= 0x28)
+ field_08 = arc.ReadRioReference ("CRio");
+ }
+ else if (Version >= 0x20)
+ {
+ field_0C = arc.ReadRioReference ("CProcessOcean");
+ field_10 = arc.ReadRioReference ("CBoxOcean");
+ field_14 = arc.ReadRioReference ("CObjectOcean");
+ field_18 = arc.ReadRioReference ("CObjectOcean");
+ field_1C = arc.ReadRioReference ("CSoundManEx");
+ if (Version >= 0x23)
+ field_24 = arc.ReadRioReference ("CDatabaseBase");
+ if (Version >= 0x22)
+ field_28 = arc.ReadRioReference ("CDatabaseBase");
+ if (Version >= 0x21)
+ field_34.Deserialize (arc);
+ }
+ else
+ throw new NotSupportedException (string.Format ("rUGP schema {0} not supported", Version));
+ }
+ }
+
+ internal class CUnknown1 : CObject
+ {
+ public int Version = 0;
+ public string field_04; // registry branch
+ public string field_08; // version
+ public string field_0C;
+ public string field_14;
+ public int field_18 = 1000;
+ public string field_1C; // copyright
+ public string field_10;
+ public int field_20 = 1;
+ public int field_24 = 1;
+ public byte[] field_28;
+ public int field_38;
+ public string field_3C;
+ public string field_40;
+ public string field_44;
+ public string field_48;
+ public string field_4C;
+ public string field_50;
+
+ public override void Deserialize (CRioArchive arc)
+ {
+ Version = arc.ReadInt32();
+ if (Version >= 2)
+ field_04 = arc.ReadString();
+ field_08 = arc.ReadString();
+ field_0C = arc.ReadString();
+ field_14 = arc.ReadString();
+ field_1C = arc.ReadString();
+ field_18 = arc.ReadInt32();
+ if (0 == Version)
+ return;
+ if (Version >= 2)
+ {
+ field_28 = arc.ReadBytes (16);
+ field_38 = arc.ReadInt32();
+ }
+ field_3C = arc.ReadString();
+ field_40 = arc.ReadString();
+ if (Version >= 3)
+ field_44 = arc.ReadString();
+ if (Version >= 4)
+ {
+ field_10 = arc.ReadString();
+ field_20 = arc.ReadInt32();
+ }
+ if (Version >= 5)
+ {
+ field_24 = arc.ReadInt32();
+ }
+ else
+ {
+ if (field_28.ToUInt16 (0) < 0x7D3)
+ field_24 = 2;
+ else
+ field_24 = 1;
+ }
+ if (Version >= 6)
+ {
+ field_48 = arc.ReadString();
+ field_4C = arc.ReadString();
+ field_50 = arc.ReadString();
+ }
+ }
+ }
+
+ internal class CStdb : CObject
+ {
+ public string field_0C;
+
+ public override void Deserialize (CRioArchive arc)
+ {
+ field_0C = arc.ReadString();
+ }
+ }
+
+ internal class CBoxOcean : CObject
+ {
+ public CObject field_10;
+
+ public override void Deserialize (CRioArchive arc)
+ {
+ field_10 = arc.ReadRioReference ("CFrameBuffer");
+ }
+ }
+
+ internal class COceanNode : CObject
+ {
+ public string Name;
+ public CObject Parent;
+ public uint Offset;
+ public uint Size;
+
+ public COceanNode (string name)
+ {
+ Name = name;
+ }
+
+ public override void Deserialize (CRioArchive arc)
+ {
+ throw new NotImplementedException ("COceanNode.Deserialize not impelemented");
+ }
+ }
+
+ internal interface IObjectFactory
+ {
+ CObject CreateObject ();
+ }
+
+ internal class CObjectFactory : IObjectFactory where CType : CObject, new()
+ {
+ public CObject CreateObject ()
+ {
+ return new CType();
+ }
+ }
+}
diff --git a/ArcFormats/rUGP/ImageRIP.cs b/ArcFormats/rUGP/ImageRIP.cs
new file mode 100644
index 00000000..928be83b
--- /dev/null
+++ b/ArcFormats/rUGP/ImageRIP.cs
@@ -0,0 +1,385 @@
+//! \file ImageRIP.cs
+//! \date Mon Nov 07 17:01:32 2016
+//! \brief rUGP 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;
+using System.ComponentModel.Composition;
+using System.IO;
+using System.Windows.Media;
+using GameRes.Utility;
+
+namespace GameRes.Formats.Rugp
+{
+ internal class Rio007MetaData : ImageMetaData
+ {
+ public uint ObjectOffset;
+ }
+
+ [Export(typeof(ImageFormat))]
+ public class Rip007Format : ImageFormat
+ {
+ public override string Tag { get { return "RIP"; } }
+ public override string Description { get { return "rUGP compressed image format"; } }
+ public override uint Signature { get { return 0; } }
+
+ public override ImageMetaData ReadMetaData (IBinaryStream file)
+ {
+ if (file.Signature != CRioArchive.ObjectSignature)
+ return null;
+ var rio = new CRioArchive (file);
+ uint signature;
+ var class_ref = rio.LoadRioTypeCore (out signature);
+ if (class_ref != "CRip007")
+ return null;
+ uint object_pos = (uint)file.Position;
+ file.ReadInt32();
+ return new Rio007MetaData
+ {
+ Width = file.ReadUInt16(),
+ Height = file.ReadUInt16(),
+ BPP = 32,
+ ObjectOffset = object_pos,
+ };
+ }
+
+ public override ImageData Read (IBinaryStream file, ImageMetaData info)
+ {
+ var meta = (Rio007MetaData)info;
+ file.Position = meta.ObjectOffset;
+ var arc = new CRioArchive (file);
+ var img = new CRip007();
+ img.Deserialize (arc);
+ return ImageData.Create (info, img.Format, null, img.Pixels);
+ }
+
+ public override void Write (Stream file, ImageData image)
+ {
+ throw new System.NotImplementedException ("Rip007Format.Write not implemented");
+ }
+ }
+
+ internal class CRip007 : CObject
+ {
+ int Version;
+ int m_width;
+ int m_height;
+ byte[] CompressInfo;
+ byte[] field_30;
+ int field_38;
+ int field_3C;
+ CObject field_4C;
+ byte[] m_pixels;
+
+ public PixelFormat Format { get; private set; }
+ public byte[] Pixels { get { return m_pixels; } }
+ public bool HasAlpha { get { return ((field_38 & 0xFF) - 2) == 1; } }
+
+ public override void Deserialize (CRioArchive arc)
+ {
+ Version = arc.ReadInt32();
+ m_width = arc.ReadUInt16();
+ m_height = arc.ReadUInt16();
+ field_30 = arc.ReadBytes (4);
+ arc.ReadUInt16();
+ arc.ReadUInt16();
+ field_38 = arc.ReadInt32();
+ CompressInfo = arc.ReadBytes (7);
+ if (arc.GetObjectSchema() >= 2)
+ field_4C = arc.ReadRioReference ("CSbm");
+ int size = arc.ReadInt32();
+ field_3C = arc.ReadInt32();
+ var data = arc.ReadBytes (size);
+ m_pixels = Uncompress (data);
+ Format = HasAlpha ? PixelFormats.Bgra32 : PixelFormats.Bgr32;
+ }
+
+ byte[] Uncompress (byte[] data)
+ {
+ using (var input = new MemoryStream (data))
+ using (var bits = new MsbBitStream (input))
+ {
+ var pixels = new byte[4 * m_width * m_height];
+ if (HasAlpha)
+ UncompressRgba (bits, pixels);
+ else
+ UncompressRgb (bits, pixels);
+ return pixels;
+ }
+ }
+
+ void UncompressRgb (IBitStream input, byte[] output)
+ {
+ int stride = m_width * 4;
+ int q = CompressInfo[0];
+ int b_bits = CompressInfo[4];
+ int g_bits = CompressInfo[5];
+ int r_bits = CompressInfo[6];
+ bool is_bgr676 = 6 == b_bits && 7 == g_bits && 6 == r_bits;
+ int b_shift = 8 - b_bits;
+ int g_shift = 16 - g_bits;
+ int r_shift = 24 - r_bits;
+ int baseline = 0xFF >> b_bits | (0xFF >> g_bits | (0xFF >> r_bits | 0xFF00) << 8) << 8;
+ {
+ for (int y = 0; y < m_height; ++y)
+ {
+ int rgb = 0, rgb_ = 0;
+ int dst = y * stride;
+ int x = 0;
+ while (x < m_width)
+ {
+ int count = 1;
+ while (input.GetNextBit() > 0)
+ {
+ count <<= 1;
+ count |= input.GetNextBit();
+ }
+ x += count;
+ do
+ {
+ if (input.GetNextBit() > 0)
+ {
+ rgb = LittleEndian.ToInt32 (output, dst - stride);
+ rgb_ = rgb;
+ if (rgb != 0)
+ {
+ rgb -= baseline;
+ rgb_ = rgb;
+ }
+ }
+ else
+ {
+ int r = 0, g = 0, b = 0;
+ if (input.GetNextBit() > 0)
+ {
+ g = 1;
+ bool sign = input.GetNextBit() > 0;
+ while (input.GetNextBit() > 0)
+ {
+ g <<= 1;
+ g |= input.GetNextBit();
+ }
+ if (sign)
+ g = -g;
+ }
+ int b_inc = 0;
+ if (input.GetNextBit() > 0)
+ {
+ int v = 1;
+ bool sign = input.GetNextBit() > 0;
+ while (input.GetNextBit() > 0)
+ {
+ v <<= 1;
+ v |= input.GetNextBit();
+ }
+ b_inc = tblQuantTransfer[q, v];
+ if (sign)
+ b_inc = -b_inc;
+ }
+ int r_inc = 0;
+ if (input.GetNextBit() > 0)
+ {
+ int v = 1;
+ bool sign = input.GetNextBit() > 0;
+ while (input.GetNextBit() > 0)
+ {
+ v <<= 1;
+ v |= input.GetNextBit();
+ }
+ r_inc = tblQuantTransfer[q, v];
+ if (sign)
+ r_inc = -r_inc;
+ }
+ int gg = g;
+ if (is_bgr676)
+ gg >>= 1;
+ if (CompressInfo[3] != 0)
+ {
+ int c1 = (0xFF >> b_shift) & (rgb_ >> b_shift);
+ int c2 = (0xFF >> b_shift) - c1;
+ c1 = -c1;
+ if (gg >= c1)
+ {
+ c1 = c2;
+ if (gg <= c2)
+ c1 = gg;
+ }
+ b = c1 + b_inc;
+ c1 = (0xFF0000 >> r_shift) & (rgb_ >> r_shift);
+ c2 = (0xFF0000 >> r_shift) - c1;
+ c1 = -c1;
+ if (gg >= c1)
+ {
+ c1 = c2;
+ if (gg <= c2)
+ c1 = gg;
+ }
+ r = c1 + r_inc;
+ }
+ else
+ {
+ b = gg + b_inc;
+ r = gg + r_inc;
+ }
+ rgb_ += (b << b_shift) + (r << r_shift) + (g << g_shift);
+ rgb = rgb_;
+ }
+ if (rgb != 0)
+ rgb += baseline;
+ LittleEndian.Pack (rgb, output, dst);
+ dst += 4;
+ --count;
+ }
+ while (count > 0);
+ if (x >= m_width)
+ break;
+
+ count = 1;
+ while (input.GetNextBit() > 0)
+ {
+ count <<= 1;
+ count |= input.GetNextBit();
+ }
+ x += count;
+ while (count --> 0)
+ {
+ LittleEndian.Pack (rgb, output, dst);
+ dst += 4;
+ }
+ }
+ }
+ }
+ }
+
+ void UncompressRgba (IBitStream input, byte[] output)
+ {
+ throw new NotImplementedException();
+ }
+
+ static readonly byte[,] tblQuantTransfer = {
+ {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
+ }, {
+ 0x00, 0x01, 0x02, 0x04, 0x06, 0x09, 0x0C, 0x0F, 0x13, 0x16, 0x19, 0x1C, 0x1F, 0x23, 0x27, 0x2B,
+ 0x30, 0x34, 0x38, 0x3C, 0x40, 0x44, 0x48, 0x4C, 0x50, 0x54, 0x58, 0x5C, 0x60, 0x64, 0x68, 0x6C,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07,
+ 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x0A, 0x0A, 0x0A, 0x0B, 0x0B, 0x0B, 0x0C,
+ 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13,
+ 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17,
+ 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x1A, 0x1A, 0x1A, 0x1A, 0x1B, 0x1B, 0x1B, 0x1B,
+ 0x1C, 0x1C, 0x1C, 0x1C, 0x1D, 0x1D, 0x1D, 0x1D, 0x1E, 0x1E, 0x1E, 0x1E, 0x1F, 0x1F, 0x1F, 0x1F,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
+ }, {
+ 0x00, 0x01, 0x02, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1B, 0x1E, 0x22, 0x26, 0x2A, 0x2E, 0x32,
+ 0x36, 0x3A, 0x3E, 0x42, 0x46, 0x4B, 0x50, 0x55, 0x5A, 0x5F, 0x64, 0x69, 0x6E, 0x73, 0x78, 0x7D,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05,
+ 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x0A, 0x0A,
+ 0x0A, 0x0A, 0x0B, 0x0B, 0x0B, 0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0E, 0x0E,
+ 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12,
+ 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1C, 0x1C,
+ 0x1C, 0x1C, 0x1C, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1F, 0x1F, 0x1F,
+ }, {
+ 0x00, 0x01, 0x03, 0x07, 0x0C, 0x10, 0x15, 0x1A, 0x20, 0x25, 0x2A, 0x30, 0x36, 0x3C, 0x42, 0x48,
+ 0x50, 0x54, 0x58, 0x5C, 0x60, 0x64, 0x68, 0x6C, 0x70, 0x74, 0x78, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D,
+ 0x0D, 0x0D, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13,
+ 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17,
+ 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x1A, 0x1A, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ }, {
+ 0x00, 0x01, 0x03, 0x07, 0x0D, 0x13, 0x1A, 0x21, 0x28, 0x2F, 0x36, 0x3E, 0x46, 0x4E, 0x56, 0x5E,
+ 0x68, 0x6A, 0x6C, 0x6E, 0x70, 0x72, 0x74, 0x76, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0B, 0x0B,
+ 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D,
+ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x10, 0x10, 0x11, 0x11, 0x12, 0x12, 0x13, 0x13,
+ 0x14, 0x14, 0x15, 0x15, 0x16, 0x16, 0x17, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ }, {
+ 0x00, 0x01, 0x04, 0x0A, 0x11, 0x18, 0x20, 0x28, 0x32, 0x3C, 0x46, 0x50, 0x5A, 0x64, 0x6E, 0x78,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+ 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0E, 0x0E,
+ 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ }
+ };
+ }
+}