diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj
index 07fc84d9..72cad74a 100644
--- a/ArcFormats/ArcFormats.csproj
+++ b/ArcFormats/ArcFormats.csproj
@@ -59,6 +59,7 @@
+
@@ -101,6 +102,7 @@
+
True
diff --git a/ArcFormats/ArcKogado.cs b/ArcFormats/ArcKogado.cs
new file mode 100644
index 00000000..c6c70d38
--- /dev/null
+++ b/ArcFormats/ArcKogado.cs
@@ -0,0 +1,291 @@
+//! \file ArcKogado.cs
+//! \date Sun Aug 24 22:01:05 2014
+//! \brief Kogado game engine archive implementation.
+//
+// Copyright (C) 2014 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.IO;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.Security.Cryptography;
+using System.Diagnostics;
+using System.Text;
+using GameRes.Formats.Strings;
+using GameRes.Utility;
+
+namespace GameRes.Formats.Kogado
+{
+ public class KogadoEntry : PackedEntry
+ {
+ // 0 : Not compressed
+ // 1 : Mariel compression
+ // 2 : Cocotte compression
+ // 3 : Xor 0xff encryption
+ public byte CompressionType;
+ public bool HasCheckSum;
+ public ushort CheckSum;
+ public long FileTime;
+ }
+
+ [Export(typeof(ArchiveFormat))]
+ public class PakOpener : ArchiveFormat
+ {
+ public override string Tag { get { return "PAK"; } }
+ public override string Description { get { return arcStrings.KogadoDescription; } }
+ public override uint Signature { get { return 0x61507948; } } // 'HyPa'
+ public override bool IsHierarchic { get { return false; } }
+ public override bool CanCreate { get { return false; } }
+
+ public override ArcFile TryOpen (ArcView file)
+ {
+ if (!file.View.AsciiEqual (0, "HyPack\x00"))
+ return null;
+ int version = file.View.ReadByte (7);
+ if (2 != version && 3 != version)
+ return null;
+ uint index_offset = 0x10 + file.View.ReadUInt32 (8);
+ if (index_offset >= file.MaxOffset)
+ return null;
+ uint entry_count = file.View.ReadUInt32 (12);
+ if (entry_count > 0xfffff)
+ return null;
+ uint index_size = entry_count * 48;
+ if (index_size > file.View.Reserve (index_offset, index_size))
+ return null;
+ long data_offset = 0x10;
+
+ var dir = new List ((int)entry_count);
+ for (uint i = 0; i < entry_count; ++i)
+ {
+ string name = file.View.ReadString (index_offset, 0x15);
+ string ext = file.View.ReadString (index_offset+0x15, 3);
+ var entry = new KogadoEntry { Name = name+'.'+ext };
+ entry.Type = FormatCatalog.Instance.GetTypeFromName (entry.Name);
+ entry.Offset = data_offset + file.View.ReadUInt32 (index_offset + 0x18);
+ entry.UnpackedSize = file.View.ReadUInt32 (index_offset + 0x1c);
+ entry.Size = file.View.ReadUInt32 (index_offset + 0x20);
+ if (!entry.CheckPlacement (file.MaxOffset))
+ return null;
+ entry.CompressionType = file.View.ReadByte (index_offset + 0x24);
+ entry.HasCheckSum = 0 != file.View.ReadByte (index_offset + 0x25);
+ entry.CheckSum = file.View.ReadUInt16 (index_offset + 0x26);
+ entry.FileTime = file.View.ReadInt64 (index_offset + 0x28);
+ entry.IsPacked = 0 != entry.CompressionType;
+ dir.Add (entry);
+ index_offset += 48;
+ }
+ return new ArcFile (file, this, dir);
+ }
+
+ public override Stream OpenEntry (ArcFile arc, Entry entry)
+ {
+ var input = arc.File.CreateStream (entry.Offset, entry.Size);
+ var packed_entry = entry as KogadoEntry;
+ if (null == packed_entry || !packed_entry.IsPacked)
+ return input;
+ if (packed_entry.CompressionType > 3)
+ {
+ Trace.WriteLine (string.Format ("{1}: Unknown compression type {0}",
+ packed_entry.CompressionType, packed_entry.Name),
+ "Kogado.PakOpener.OpenEntry");
+ return input;
+ }
+ if (3 == packed_entry.CompressionType)
+ return new CryptoStream (input, new NotTransform(), CryptoStreamMode.Read);
+ try
+ {
+ if (2 == packed_entry.CompressionType)
+ {
+ var decoded = new MemoryStream ((int)packed_entry.UnpackedSize);
+ try
+ {
+ var cocotte = new CocotteEncoder();
+ if (!cocotte.Decode (input, decoded))
+ throw new InvalidFormatException ("Invalid Cocotte-encoded stream");
+ decoded.Position = 0;
+ return decoded;
+ }
+ catch
+ {
+ decoded.Dispose();
+ throw;
+ }
+ }
+ // if (1 == packed_entry.CompressionType)
+ var unpacked = new byte[packed_entry.UnpackedSize];
+ var mariel = new MarielEncoder();
+ mariel.Unpack (input, unpacked, unpacked.Length);
+ return new MemoryStream (unpacked, false);
+ }
+ finally
+ {
+ input.Dispose();
+ }
+ }
+
+ // files inside archive are aligned to 0x10 boundary.
+ // to convert DateTime structure into entry time:
+ // entry.FileTime = file_info.CreationTimeUtc.Ticks;
+ //
+ // last two bytes of archive is CRC16 of the whole file
+ }
+
+ public sealed class Crc16 : ICheckSum
+ {
+ private ushort m_value = 0xffff;
+
+ public uint Value { get { return m_value; } }
+
+ public void Update (byte[] buf, int pos, int len)
+ {
+ for (int i = 0; i < len; ++i)
+ {
+ m_value = (ushort)(Crc16Table[(m_value^buf[pos+i]) & 0xff] ^ (m_value >> 8));
+ }
+ }
+
+ private static readonly ushort[] Crc16Table = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78,
+ };
+ }
+
+ internal class MarielEncoder
+ {
+ public void Unpack (Stream input, byte[] dest, int dest_size)
+ {
+ int out_pos = 0;
+ using (var reader = new BinaryReader (input, Encoding.ASCII, true))
+ {
+ uint bits = 0;
+ while (dest_size > 0)
+ {
+ bool carry = 0 != (bits & 0x80000000);
+ bits <<= 1;
+ if (0 == bits)
+ {
+ bits = reader.ReadUInt32();
+ carry = 0 != (bits & 0x80000000);
+ bits = (bits << 1) | 1u;
+ }
+ int b = input.ReadByte();
+ if (-1 == b)
+ break;
+ if (!carry)
+ {
+ dest[out_pos++] = (byte)b;
+ dest_size--;
+ continue;
+ }
+ int offset = (b & 0x0f) + 1;
+ int count = ((b >> 4) & 0x0f) + 1;
+ if (0x0f == count)
+ {
+ b = input.ReadByte();
+ if (-1 == b)
+ break;
+ count = (byte)b;
+ }
+ else if (count > 0x0f)
+ {
+ count = reader.ReadUInt16();
+ }
+ if (offset >= 0x0b)
+ {
+ offset -= 0x0b;
+ offset <<= 8;
+ offset |= reader.ReadByte();
+ }
+ if (count > dest_size)
+ count = dest_size;
+ int src = out_pos - offset;
+ if (src < 0 || src >= out_pos)
+ break;
+ Binary.CopyOverlapped (dest, src, out_pos, count);
+ out_pos += count;
+ dest_size -= count;
+ }
+ }
+ }
+ }
+
+ public sealed class NotTransform : ICryptoTransform
+ {
+ public bool CanReuseTransform { get { return true; } }
+ public bool CanTransformMultipleBlocks { get { return true; } }
+ public int InputBlockSize { get { return 256; } }
+ public int OutputBlockSize { get { return 256; } }
+
+ public int TransformBlock (byte[] inputBuffer, int inputOffset, int inputCount,
+ byte[] outputBuffer, int outputOffset)
+ {
+ for (int i = 0; i < inputCount; ++i)
+ {
+ outputBuffer[outputOffset++] = (byte)~inputBuffer[inputOffset+i];
+ }
+ return inputCount;
+ }
+
+ public byte[] TransformFinalBlock (byte[] inputBuffer, int inputOffset, int inputCount)
+ {
+ byte[] outputBuffer = new byte[inputCount];
+ TransformBlock (inputBuffer, inputOffset, inputCount, outputBuffer, 0);
+ return outputBuffer;
+ }
+
+ public void Dispose ()
+ {
+ System.GC.SuppressFinalize (this);
+ }
+ }
+}
diff --git a/ArcFormats/KogadoCocotte.cs b/ArcFormats/KogadoCocotte.cs
new file mode 100644
index 00000000..0336c9d6
--- /dev/null
+++ b/ArcFormats/KogadoCocotte.cs
@@ -0,0 +1,833 @@
+//! \file KogadoCocotte.cs
+//! \date Mon Aug 25 13:35:37 2014
+//! \brief Kogado engine Cocotte compression/encryption implementation.
+//
+// 作者について:
+// あんたいとるどどきゅめんと < http://juicy.s53.xrea.com/program/ >
+// Written by juicy.gt < juicy[atm@rk]s53.xrea.com >
+//
+// Original code licensed under GNU GPL Version 2
+//
+// C# port by mørkt
+// 2014/08/25
+//
+
+using System;
+using System.IO;
+using System.Text;
+
+namespace GameRes.Formats.Kogado
+{
+ public class CocotteEncoder
+ {
+ BWTEncode m_cBWTEncode = new BWTEncode();
+ MTFEncode m_cMTFEncode = new MTFEncode();
+ CRangeCoder m_cRangeCoder = new CRangeCoder();
+
+ const int RANGECODER_BLOCKSIZE = 0x2000;
+
+ /*
+ // Encode
+ BOOL Encode( DWORD dwCompressionLevel, BYTE *pDestBuffer, const BYTE *pSrcBuffer, DWORD dwDestLength, DWORD dwSrcLength, DWORD *pdwWritten, HPA_Callback callback, LPVOID pCallbackArg )
+ {
+ // BWTEncode でサイズが 2 増えるので + 2 する
+ BYTE buffer[ RANGECODER_BLOCKSIZE + 2 ];
+ DWORD dwSrcCursor = 0, dwDestCursor = 0;
+ DWORD written;
+ DWORD write_src_size;
+
+ m_cMTFEncode.InitMTFOrder();
+ m_cRangeCoder.InitQSModel();
+ while ( dwSrcCursor < dwSrcLength ) {
+ if ( callback != NULL ) {
+ if ( !callback( pCallbackArg, dwSrcCursor, dwSrcLength ) )
+ return FALSE;
+ }
+ if ( dwDestLength - dwDestCursor <= 4 ) // バッファが足りない
+ return FALSE;
+ write_src_size = min( RANGECODER_BLOCKSIZE, dwSrcLength - dwSrcCursor );
+ m_cBWTEncode.Encode( buffer, pSrcBuffer, write_src_size );
+ // BWTEncode でサイズが 2 増えるので + 2 する
+ m_cMTFEncode.Encode( buffer, buffer, write_src_size + 2 );
+ switch ( dwCompressionLevel ) {
+ case CMPL_STORE:
+ loc_store_encode:
+ written = write_src_size + 2;
+ memcpy( pDestBuffer + 4, buffer, written );
+ break;
+ case CMPL_MAXIMUM:
+ if ( !m_cRangeCoder.Encode( pDestBuffer + 4, buffer, dwDestLength - dwDestCursor - 4, write_src_size + 2, &written ) )
+ return FALSE;
+ // オーバーしたら STORE にする
+ if ( written >= write_src_size + 2 ) {
+ m_cRangeCoder.InitQSModel();
+ goto loc_store_encode;
+ }
+ break;
+ default:
+ return FALSE;
+ }
+ written += 4;
+ // 書きすぎ
+ if ( written > 0xffff || written > dwDestLength - dwDestCursor )
+ return FALSE;
+ reinterpret_cast< unsigned short * >( pDestBuffer )[0] = static_cast< unsigned short >( written );
+ reinterpret_cast< unsigned short * >( pDestBuffer )[1] = static_cast< unsigned short >( write_src_size );
+
+ pSrcBuffer += write_src_size; dwSrcCursor += write_src_size;
+ pDestBuffer += written; dwDestCursor += written;
+ }
+ *pdwWritten = dwDestCursor;
+ if ( callback != NULL )
+ if ( !callback( pCallbackArg, dwSrcCursor, dwSrcLength ) )
+ return FALSE;
+
+ return ( dwSrcCursor == dwSrcLength );
+ }
+ */
+
+ // Decode
+ public bool Decode (Stream input, Stream output)
+ {
+ uint dwSrcLength = (uint)input.Length;
+ var buffer = new byte [RANGECODER_BLOCKSIZE*4+2];
+ uint dwSrcCursor = 0;
+ var input_buffer = new byte[RANGECODER_BLOCKSIZE];
+
+ m_cRangeCoder.InitQSModel();
+ m_cMTFEncode.InitMTFOrder();
+
+ using (var reader = new BinaryReader (input, Encoding.ASCII, true))
+ {
+ while (dwSrcCursor < dwSrcLength)
+ {
+ if (dwSrcCursor + 4 >= dwSrcLength)
+ return false;
+ ushort src_block_size = reader.ReadUInt16();
+ ushort dest_block_size = reader.ReadUInt16();
+ ushort comp_block_size = (ushort)(src_block_size - 4);
+ ushort decomp_block_size = (ushort)(dest_block_size + 2);
+
+ if (dwSrcCursor + src_block_size > dwSrcLength)
+ return false;
+ if (src_block_size <= 4 || dest_block_size == 0)
+ return false;
+ if (comp_block_size == decomp_block_size)
+ {
+ int read = input.Read (buffer, 0, comp_block_size);
+ m_cRangeCoder.InitQSModel();
+ }
+ else
+ {
+ if (comp_block_size > input_buffer.Length)
+ input_buffer = new byte[comp_block_size];
+ int read = input.Read (input_buffer, 0, comp_block_size);
+ if (read != comp_block_size)
+ return false;
+ uint written = m_cRangeCoder.Decode (buffer, input_buffer, decomp_block_size, comp_block_size);
+ if (0 == written)
+ break;
+ if (written != decomp_block_size)
+ return false;
+ }
+ m_cMTFEncode.Decode (buffer, buffer, decomp_block_size);
+ m_cBWTEncode.Decode (output, buffer, decomp_block_size);
+
+ dwSrcCursor += src_block_size;
+ }
+ }
+ return dwSrcCursor == dwSrcLength;
+ }
+ }
+
+ internal class CRangeCoder
+ {
+ byte[] m_pSrcBuffer;
+ byte[] m_pDestBuffer;
+ uint m_dwSrcLength;
+ uint m_dwDestLength;
+ uint m_dwSrcIndex;
+ uint m_dwDestIndex;
+ RangeCoder m_rc = new RangeCoder();
+ QSModel m_qsm;
+
+ const int CODE_BITS = 32;
+ const int SHIFT_BITS = CODE_BITS - 9;
+ const int EXTRA_BITS = (CODE_BITS - 2) % 8 + 1;
+ const uint Top_value = 1u << (CODE_BITS - 1);
+ const uint Bottom_value = Top_value >> 8;
+
+ static readonly int[] RANGECODER_INITFREQ = {
+ 1400, 640, 320, 240, 160, 120, 80, 64,
+ 48, 40, 32, 24, 20, 20, 20, 20,
+ 16, 16, 16, 16, 12, 12, 12, 12,
+ 12, 12, 8, 8, 8, 8, 8, 8,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+
+ 3, 3, 3, 3, 3, 3, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+
+ 2,
+ };
+
+ public void InitQSModel()
+ {
+ m_qsm = new QSModel (257, 12, 2000, RANGECODER_INITFREQ, false);
+ }
+
+ /*
+ public bool Encode (byte[] dest, const BYTE *src, DWORD destsize, DWORD srcsize, DWORD *pwritten )
+ {
+ BYTE ch_byte;
+ int syfreq, ltfreq;
+
+ m_dwSrcIndex = m_dwDestIndex = 0;
+ m_pSrcBuffer = src;
+ m_pDestBuffer = dest;
+ m_dwSrcLength = srcsize;
+ m_dwDestLength = destsize;
+
+ //this->InitQSModel();
+ this->StartEncoding();
+
+ while ( m_dwSrcIndex < m_dwSrcLength ) {
+ if ( !this->GetSrcByteImpl( &ch_byte ) )
+ return false;
+ qsgetfreq( &m_qsm, ch_byte, &syfreq, <freq );
+ this->EncodeShift( syfreq, ltfreq, 12 );
+ qsupdate( &m_qsm, ch_byte );
+ }
+ qsgetfreq( &m_qsm, 256, &syfreq, <freq );
+ this->EncodeShift( syfreq, ltfreq, 12 );
+
+ this->DoneEncoding();
+ *pwritten = m_dwDestIndex; // written-size
+
+ return true;
+ }
+ */
+
+ public uint Decode (byte[] dest, byte[] src, uint destsize, uint srcsize)
+ {
+ int ch, ltfreq, syfreq;
+
+ m_dwSrcIndex = m_dwDestIndex = 0;
+ m_pSrcBuffer = src;
+ m_pDestBuffer = dest;
+ m_dwSrcLength = srcsize;
+ m_dwDestLength = destsize;
+
+ StartDecoding();
+
+ while (m_dwSrcIndex < m_dwSrcLength)
+ {
+ ltfreq = (int)DecodeCulshift (12);
+ ch = m_qsm.GetSym (ltfreq);
+ if (256 == ch) // check for end-of-file
+ break;
+ if (m_dwDestIndex >= m_dwDestLength)
+ return 0;
+ SetDestByteImpl ((byte)ch);
+ m_qsm.GetFreq (ch, out syfreq, out ltfreq);
+ DecodeUpdate (syfreq, ltfreq, 1 << 12);
+ m_qsm.Update (ch);
+ }
+ m_qsm.GetFreq (256, out syfreq, out ltfreq);
+ DecodeUpdate (syfreq, ltfreq, 1 << 12);
+ DoneDecoding();
+ return m_dwDestIndex;
+ }
+/*
+ // Encode --------------------------------------------------------
+ void StartEncoding( char c = 0, int initlength = 0 )
+ {
+ m_rc.low = 0; // Full code range
+ m_rc.range = Top_value;
+ m_rc.buffer = c;
+ m_rc.help = 0; // No bytes to follow
+ m_rc.bytecount = initlength;
+ }
+ void EncNormalize()
+ {
+ while ( m_rc.range <= Bottom_value ) { // do we need renormalisation?
+ if ( m_rc.low < (uint)0xff << SHIFT_BITS ) { // no carry possible --> output
+ this->SetDestByteImpl( m_rc.buffer );
+ for ( ; m_rc.help; m_rc.help -- )
+ this->SetDestByteImpl( 0xff );
+ m_rc.buffer = (unsigned char)( m_rc.low >> SHIFT_BITS );
+ } else if ( m_rc.low & Top_value ) { // carry now, no future carry
+ this->SetDestByteImpl( m_rc.buffer+1 );
+ for ( ; m_rc.help; m_rc.help -- )
+ this->SetDestByteImpl( 0 );
+ m_rc.buffer = (unsigned char)( m_rc.low >> SHIFT_BITS );
+ } else // passes on a potential carry
+ m_rc.help ++;
+ m_rc.range <<= 8;
+ m_rc.low = ( m_rc.low << 8 ) & ( Top_value - 1 );
+ m_rc.bytecount ++;
+ }
+ }
+ void EncodeFreq (uint sy_f, uint lt_f, uint tot_f)
+ {
+ uint r, tmp;
+
+ this->EncNormalize();
+ r = m_rc.range / tot_f;
+ tmp = r * lt_f;
+ m_rc.low += tmp;
+ if ( lt_f + sy_f < tot_f )
+ m_rc.range = r * sy_f;
+ else
+ m_rc.range -= tmp;
+ }
+ void EncodeShift (uint sy_f, uint lt_f, uint shift)
+ {
+ uint r, tmp;
+
+ this->EncNormalize();
+ r = m_rc.range >> shift;
+ tmp = r * lt_f;
+ m_rc.low += tmp;
+ if ( ( lt_f + sy_f ) >> shift )
+ m_rc.range -= tmp;
+ else
+ m_rc.range = r * sy_f;
+ }
+ uint4 DoneEncoding()
+ {
+ uint tmp;
+
+ this->EncNormalize(); // now we have a normalized state
+ m_rc.bytecount += 5;
+ if ( ( m_rc.low & ( Bottom_value - 1 ) ) < ( ( m_rc.bytecount & 0xffffffL ) >> 1 ) )
+ tmp = m_rc.low >> SHIFT_BITS;
+ else
+ tmp = ( m_rc.low >> SHIFT_BITS ) + 1;
+ if ( tmp > 0xff ) { // we have a carry
+ this->SetDestByteImpl( m_rc.buffer + 1 );
+ for ( ; m_rc.help; m_rc.help -- )
+ this->SetDestByteImpl( 0 );
+ } else { // no carry
+ this->SetDestByteImpl( m_rc.buffer );
+ for ( ; m_rc.help; m_rc.help -- )
+ this->SetDestByteImpl( 0xff );
+ }
+ this->SetDestByteImpl( static_cast< BYTE >( tmp ) );
+ this->SetDestByteImpl( static_cast< BYTE >( m_rc.bytecount >> 16 ) );
+ this->SetDestByteImpl( static_cast< BYTE >( m_rc.bytecount >> 8 ) );
+ this->SetDestByteImpl( static_cast< BYTE >( m_rc.bytecount ) );
+
+ return m_rc.bytecount;
+ }
+*/
+
+ // Decode --------------------------------------------------------
+ int StartDecoding ()
+ {
+ byte c;
+
+ if (!GetSrcByteImpl (out c))
+ return -1;
+ if (!GetSrcByteImpl (out m_rc.buffer))
+ return -1;
+ m_rc.low = (uint)(m_rc.buffer >> (8 - EXTRA_BITS));
+ m_rc.range = (uint)1 << EXTRA_BITS;
+
+ return c;
+ }
+
+ bool DecNormalize()
+ {
+ while ( m_rc.range <= Bottom_value )
+ {
+ m_rc.low = ( m_rc.low << 8 ) | (byte)(m_rc.buffer << EXTRA_BITS);
+ if (!GetSrcByteImpl (out m_rc.buffer))
+ return false;
+ m_rc.low |= (uint)m_rc.buffer >> ( 8 - EXTRA_BITS );
+ m_rc.range <<= 8;
+ }
+ return true;
+ }
+
+ uint DecodeCulshift (int shift)
+ {
+ uint tmp;
+
+ DecNormalize();
+ m_rc.help = m_rc.range >> shift;
+ tmp = m_rc.low / m_rc.help;
+ return (0 != (tmp >> shift) ? (1u << shift) - 1u : tmp);
+ }
+
+ void DecodeUpdate (int sy_f, int lt_f, int tot_f)
+ {
+ uint tmp = m_rc.help * (uint)lt_f;
+
+ m_rc.low -= tmp;
+ if ( lt_f + sy_f < tot_f )
+ m_rc.range = m_rc.help * (uint)sy_f;
+ else
+ m_rc.range -= tmp;
+ }
+
+ void DoneDecoding()
+ {
+ DecNormalize(); // normalize to use up all bytes
+ }
+
+ // I/O -----------------------------------------------------------
+ bool GetSrcByteImpl (out byte pData)
+ {
+ if (m_dwSrcIndex >= m_dwSrcLength)
+ {
+ pData = 0;
+ return false;
+ }
+ pData = m_pSrcBuffer[m_dwSrcIndex++];
+ return true;
+ }
+
+ bool SetDestByteImpl (byte byData)
+ {
+ if (m_dwDestIndex >= m_dwDestLength)
+ return false;
+ m_pDestBuffer[m_dwDestIndex++] = byData;
+ return true;
+ }
+ }
+
+ internal class RangeCoder
+ {
+ public uint low; /* low end of interval */
+ public uint range; /* length of interval */
+ public uint help; /* bytes_to_follow resp. intermediate value */
+ public byte buffer; /* buffer for input/output */
+ /* the following is used only when encoding */
+ public uint bytecount; /* counter for outputed bytes */
+ }
+
+ // とりあえずこれで可逆性を概ね確認 (数タイトルの song.txt で確認)
+ internal class BWTEncode
+ {
+ public const ulong BWT_SORTTABLESIZE = 0x00010000;
+
+/*
+ byte[] m_pWorkTable = new byte[BWT_SORTTABLESIZE / 2];
+
+ void Encode( BYTE *dest, const BYTE *src, int size )
+ {
+ int top = 0; // 初期値は不要だが、警告回避のため
+ BYTE *ptr;
+ int count[256] = { 0, };
+ int count_sum[256+1];
+ BYTE *sort_buffer = new BYTE[size*2];
+ LPBYTE *sort_table = new LPBYTE[BWT_SORTTABLESIZE];
+
+ // 作業領域にコピー
+ memcpy( sort_buffer, src, size );
+ memcpy( sort_buffer + size, sort_buffer, size );
+
+ // 分布数え上げソート
+ for ( int i = 0; i < size; i ++ )
+ count[ sort_buffer[i] ]++;
+ count_sum[0] = 0;
+ for ( int i = 1; i <= 256; i ++ )
+ count_sum[i] = count[i-1] + count_sum[i-1];
+ for ( int i = 1; i < 256; i ++ )
+ count[i] += count[i-1];
+
+ for ( int i = size - 1; i >= 0; i -- ) {
+ ptr = sort_buffer + i;
+ sort_table[ -- count[*ptr] ] = ptr;
+ }
+
+ // 2 段階ソート
+ for ( int i = 1; i < 256; i ++ ) {
+ int j, k;
+ int high = count_sum[i+1];
+
+ for ( j = k = count_sum[i]; j < high; j ++ ) {
+ ptr = sort_table[j];
+ if ( *ptr > *(ptr + 1) ) {
+ sort_table[j] = sort_table[k];
+ sort_table[k ++] = ptr;
+ }
+ }
+ if ( high - k > 1 )
+ this->MergeSort( sort_table, k, high - 1, size );
+ }
+ // 0 は全てソート
+ if ( count_sum[1] > 1 )
+ this->MergeSort( sort_table, 0, count_sum[1] - 1, size );
+ // ソート不要部分
+ for ( int i = 0; i < size; i ++ ) {
+ ptr = sort_table[i];
+ if ( ptr == sort_buffer )
+ ptr += size;
+ if ( *(ptr - 1) > *ptr )
+ sort_table[ count_sum[*(ptr - 1)] ++ ] = ptr - 1;
+ }
+ // 出力
+ for ( int i = 0; i < size; i ++ ) {
+ ptr = sort_table[i];
+ if ( ptr == sort_buffer )
+ top = i;
+ dest[i+2] = *(ptr + size - 1);
+ }
+ *reinterpret_cast< unsigned short * >( dest ) = static_cast< unsigned short >( top );
+ // 解放
+ delete[] sort_buffer;
+ delete[] sort_table;
+ }
+*/
+ int[] sort_table = new int[BWT_SORTTABLESIZE];
+
+ public void Decode (Stream dest, byte[] src, int size)
+ {
+ int[] count = new int[256];
+ int top = src[0] | src[1] << 8;
+
+ int pos = 2;
+ size -= 2;
+ // 分布数え上げソート
+ for (int i = 0; i < size; i++)
+ count[ src[pos+i] ]++;
+ for (short i = 1; i < 256; i++)
+ count[i] += count[i-1];
+ for (int i = size - 1; i >= 0; i --)
+ {
+ sort_table[--count[src[pos+i]]] = i;
+ }
+ // 出力
+ int ptr = sort_table[top];
+ for (int i = 0; i < size; i++)
+ {
+ dest.WriteByte (src[pos+ptr]);
+ ptr = sort_table[ptr];
+ }
+ }
+/*
+ void MergeSort( BYTE *sort_table[], int low, int high, int size )
+ {
+ int len = size - 1;
+
+ if ( high - low <= 10 ) {
+ this->InsertSort( sort_table, low, high, size );
+ } else {
+ int middle = (low + high) / 2;
+ int i, p, j, k;
+
+ this->MergeSort( sort_table, low, middle, size );
+ this->MergeSort( sort_table, middle + 1, high, size );
+ p = 0;
+ i = low;
+ while ( i <= middle )
+ m_pWorkTable[p ++] = sort_table[i ++];
+ i = middle + 1;
+ j = 0;
+ k = low;
+ while ( i <= high && j < p ) {
+ if ( memcmp( m_pWorkTable[j] + 1, sort_table[i] + 1, len ) <= 0 )
+ sort_table[k ++] = m_pWorkTable[j ++];
+ else
+ sort_table[k ++] = sort_table[i ++];
+ }
+ while ( j < p )
+ sort_table[k ++] = m_pWorkTable[j ++];
+ }
+ }
+ void InsertSort( BYTE *sort_table[], int low, int high, int size )
+ {
+ int j, len = size - 1;
+
+ for ( int i = low + 1; i <= high ; i ++ ) {
+ BYTE *tmp = sort_table[i];
+
+ for ( j = i - 1; j >= low && memcmp( tmp + 1, sort_table[j] + 1, len ) < 0; j -- )
+ sort_table[j + 1] = sort_table[j];
+ sort_table[j + 1] = tmp;
+ }
+ }
+*/
+ }
+
+ internal class MTFEncode
+ {
+ byte[] m_MTFTable = new byte[256];
+
+ public void InitMTFOrder()
+ {
+ for (int i = 0; i < 256; i++)
+ m_MTFTable[i] = (byte)i;
+ }
+
+ // MTF は当然、destsize == srcsize
+ public void Encode (byte[] dest, byte[] src, int size)
+ {
+ for (int i = 0; i < size; i++)
+ {
+ byte c = src[i];
+ byte n = 0;
+
+ while (m_MTFTable[n] != c)
+ n++;
+ if (n > 0)
+ {
+ Array.Copy (m_MTFTable, 0, m_MTFTable, 1, n);
+ m_MTFTable[0] = c;
+ }
+ dest[i] = n;
+ }
+ }
+
+ // MTF は当然、destsize == srcsize
+ public void Decode (byte[] dest, byte[] src, int size)
+ {
+ for ( int i = 0; i < size; i++ )
+ {
+ byte n = src[i];
+ byte c = m_MTFTable[n];
+ if (n > 0)
+ {
+ Array.Copy (m_MTFTable, 0, m_MTFTable, 1, n);
+ m_MTFTable[0] = c;
+ }
+ dest[i] = c;
+ }
+ }
+ }
+
+ /*
+ Quasistatic probability model
+
+ // 若干改変 by juicy.gt at 2008/03/27 00:00
+
+ (c) Michael Schindler
+ 1997, 1998, 2000
+ http://www.compressconsult.com/
+ michael@compressconsult.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details. It may be that this
+ program violates local patents in your country, however it is
+ belived (NO WARRANTY!) to be patent-free here in Austria.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ Qsmodel is a quasistatic probability model that periodically
+ (at chooseable intervals) updates probabilities of symbols;
+ it also allows to initialize probabilities. Updating is done more
+ frequent in the beginning, so it adapts very fast even without
+ initialisation.
+
+ it provides function for creation, deletion, query for probabilities
+ and symbols and model updating.
+ */
+
+ internal class QSModel
+ {
+ public int m_n; /* number of symbols */
+ public int m_left; /* symbols to next rescale */
+ public int m_nextleft; /* symbols with other increment */
+ public int m_rescale; /* intervals between rescales */
+ public int m_targetrescale; /* should be interval between rescales */
+ public int m_incr; /* increment per update */
+ public int m_searchshift; /* shift for lt_freq before using as index */
+ public ushort[] m_cf; /* array of cumulative frequencies */
+ public ushort[] m_newf; /* array for collecting ststistics */
+ public ushort[] m_search; /* structure for searching on decompression */
+
+ public const int TBLSHIFT = 7;
+
+ ///
+ /// initialisation of qsmodel
+ ///
+ /// number of symbols in that model
+ /// base2 log of total frequency count
+ /// desired rescaling interval, should be < 1<<(lg_totf+1)
+ /// array of int's to be used for initialisation (NULL ok)
+ /// true on compression, false on decompression
+ public QSModel (int n, int lg_totf, int rescale, int[] init, bool compress)
+ {
+ m_n = n;
+ m_targetrescale = rescale;
+ m_searchshift = lg_totf - TBLSHIFT;
+ if (m_searchshift < 0)
+ m_searchshift = 0;
+ m_cf = new ushort[n+1];
+ m_newf = new ushort[n+1];
+ m_cf[n] = (ushort)(1 << lg_totf);
+ m_cf[0] = 0;
+ if (compress)
+ {
+ m_search = null;
+ }
+ else
+ {
+ m_search = new ushort[(1<
+ /// reinitialisation of qsmodel
+ ///
+ /// array of int's to be used for initialisation (NULL ok)
+ public void Reset (int[] init)
+ {
+ int i;
+ m_rescale = m_n>>4 | 2;
+ m_nextleft = 0;
+ if (init == null)
+ {
+ int initval = m_cf[m_n] / m_n;
+ int end = m_cf[m_n] % m_n;
+ for (i = 0; i < end; i++)
+ m_newf[i] = (ushort)(initval+1);
+ for (; i < m_n; i++)
+ m_newf[i] = (ushort)initval;
+ }
+ else
+ {
+ for (i = 0; i < m_n; i++)
+ m_newf[i] = (ushort)init[i];
+ }
+ DoRescale();
+ }
+
+ void DoRescale ()
+ {
+ if (0 != m_nextleft) /* we have some more before actual rescaling */
+ {
+ m_incr++;
+ m_left = m_nextleft;
+ m_nextleft = 0;
+ return;
+ }
+ if (m_rescale < m_targetrescale) /* double rescale interval if needed */
+ {
+ m_rescale <<= 1;
+ if (m_rescale > m_targetrescale)
+ m_rescale = m_targetrescale;
+ }
+ int i, cf, missing;
+ cf = missing = m_cf[m_n]; /* do actual rescaling */
+ for (i = m_n-1; i != 0; i--)
+ {
+ int tmp = m_newf[i];
+ cf -= tmp;
+ m_cf[i] = (ushort)cf;
+ tmp = tmp>>1 | 1;
+ missing -= tmp;
+ m_newf[i] = (ushort)tmp;
+ }
+ if (cf != m_newf[0])
+ throw new ApplicationException ("Run-time error in QSModel.DoRescale");
+
+ m_newf[0] = (ushort)(m_newf[0]>>1 | 1);
+ missing -= m_newf[0];
+ m_incr = missing / m_rescale;
+ m_nextleft = missing % m_rescale;
+ m_left = m_rescale - m_nextleft;
+ if (m_search != null)
+ {
+ i = m_n;
+ while (i != 0)
+ {
+ int end = (m_cf[i]-1) >> m_searchshift;
+ i--;
+ int start = m_cf[i] >> m_searchshift;
+ while (start <= end)
+ {
+ m_search[start] = (ushort)i;
+ start++;
+ }
+ }
+ }
+ }
+
+ ///
+ /// retrieval of estimated frequencies for a symbol
+ ///
+ /// symbol for which data is desired; must be <n
+ /// frequency of that symbol
+ /// frequency of all smaller symbols together
+ /// the total frequency is 1<<lg_totf
+ public void GetFreq (int sym, out int sy_f, out int lt_f)
+ {
+ lt_f = m_cf[sym];
+ sy_f = m_cf[sym+1] - lt_f;
+ }
+
+ ///
+ /// find out symbol for a given cumulative frequency.
+ ///
+ /// cumulative frequency
+ public int GetSym (int lt_f)
+ {
+ int lo, hi;
+ int tmp = lt_f >> m_searchshift;
+ lo = m_search[tmp];
+ hi = m_search[tmp+1] + 1;
+ while (lo+1 < hi)
+ {
+ int mid = (lo + hi) >> 1;
+ if (lt_f < m_cf[mid])
+ hi = mid;
+ else
+ lo = mid;
+ }
+ return lo;
+ }
+
+ ///
+ /// update model
+ ///
+ /// symbol that occurred (must be <n from init)
+ public void Update (int sym)
+ {
+ if (m_left <= 0)
+ DoRescale();
+ m_left--;
+ m_newf[sym] += (ushort)m_incr;
+ }
+ }
+}
diff --git a/ArcFormats/Strings/arcStrings.Designer.cs b/ArcFormats/Strings/arcStrings.Designer.cs
index 63d703b9..5eff4542 100644
--- a/ArcFormats/Strings/arcStrings.Designer.cs
+++ b/ArcFormats/Strings/arcStrings.Designer.cs
@@ -216,6 +216,15 @@ namespace GameRes.Formats.Strings {
}
}
+ ///
+ /// Looks up a localized string similar to Kogado game engine resource archive.
+ ///
+ public static string KogadoDescription {
+ get {
+ return ResourceManager.GetString("KogadoDescription", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Liar-soft image archive.
///
diff --git a/ArcFormats/Strings/arcStrings.resx b/ArcFormats/Strings/arcStrings.resx
index e8fc1998..7f4d8383 100644
--- a/ArcFormats/Strings/arcStrings.resx
+++ b/ArcFormats/Strings/arcStrings.resx
@@ -171,6 +171,9 @@ Choose appropriate encryption scheme.
Enter archive encryption key or choose
predefined encryption scheme.
+
+ Kogado game engine resource archive
+
Liar-soft image archive