implemented Kogado resource archive.

This commit is contained in:
morkt 2014-08-25 15:11:34 +04:00
parent ad1b51b24c
commit fa4b8b6bce
5 changed files with 1138 additions and 0 deletions

View File

@ -59,6 +59,7 @@
<Compile Include="ArcAMI.cs" /> <Compile Include="ArcAMI.cs" />
<Compile Include="ArcDRS.cs" /> <Compile Include="ArcDRS.cs" />
<Compile Include="ArcINT.cs" /> <Compile Include="ArcINT.cs" />
<Compile Include="ArcKogado.cs" />
<Compile Include="ArcMajiro.cs" /> <Compile Include="ArcMajiro.cs" />
<Compile Include="ArcNPA.cs" /> <Compile Include="ArcNPA.cs" />
<Compile Include="ArcNSA.cs" /> <Compile Include="ArcNSA.cs" />
@ -101,6 +102,7 @@
<Compile Include="ImageRCT.cs" /> <Compile Include="ImageRCT.cs" />
<Compile Include="ImageTLG.cs" /> <Compile Include="ImageTLG.cs" />
<Compile Include="ImageWCG.cs" /> <Compile Include="ImageWCG.cs" />
<Compile Include="KogadoCocotte.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\Settings.Designer.cs"> <Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>

291
ArcFormats/ArcKogado.cs Normal file
View File

@ -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<Entry> ((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);
}
}
}

833
ArcFormats/KogadoCocotte.cs Normal file
View File

@ -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, &ltfreq );
this->EncodeShift( syfreq, ltfreq, 12 );
qsupdate( &m_qsm, ch_byte );
}
qsgetfreq( &m_qsm, 256, &syfreq, &ltfreq );
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;
/// <summary>
/// initialisation of qsmodel
/// </summary>
/// <param name="n">number of symbols in that model</param>
/// <param name="lg_totf">base2 log of total frequency count</param>
/// <param name="rescale">desired rescaling interval, should be &lt; 1&lt;&lt;(lg_totf+1)</param>
/// <param name="init">array of int's to be used for initialisation (NULL ok)</param>
/// <param name="compress">true on compression, false on decompression</param>
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<<TBLSHIFT)+1];
m_search[1<<TBLSHIFT] = (ushort)(n-1);
}
Reset (init);
}
/// <summary>
/// reinitialisation of qsmodel
/// </summary>
/// <param name="init">array of int's to be used for initialisation (NULL ok)</param>
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++;
}
}
}
}
/// <summary>
/// retrieval of estimated frequencies for a symbol
/// </summary>
/// <param name="sym">symbol for which data is desired; must be &lt;n</param>
/// <param name="sy_f">frequency of that symbol</param>
/// <param name="lt_f">frequency of all smaller symbols together</param>
/// the total frequency is 1&lt;&lt;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;
}
/// <summary>
/// find out symbol for a given cumulative frequency.
/// </summary>
/// <param name="lt_f">cumulative frequency</param>
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;
}
/// <summary>
/// update model
/// </summary>
/// <param name="sym">symbol that occurred (must be &lt;n from init)</param>
public void Update (int sym)
{
if (m_left <= 0)
DoRescale();
m_left--;
m_newf[sym] += (ushort)m_incr;
}
}
}

View File

@ -216,6 +216,15 @@ namespace GameRes.Formats.Strings {
} }
} }
/// <summary>
/// Looks up a localized string similar to Kogado game engine resource archive.
/// </summary>
public static string KogadoDescription {
get {
return ResourceManager.GetString("KogadoDescription", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Liar-soft image archive. /// Looks up a localized string similar to Liar-soft image archive.
/// </summary> /// </summary>

View File

@ -171,6 +171,9 @@ Choose appropriate encryption scheme.</value>
Enter archive encryption key or choose Enter archive encryption key or choose
predefined encryption scheme.</value> predefined encryption scheme.</value>
</data> </data>
<data name="KogadoDescription" xml:space="preserve">
<value>Kogado game engine resource archive</value>
</data>
<data name="LWGDescription" xml:space="preserve"> <data name="LWGDescription" xml:space="preserve">
<value>Liar-soft image archive</value> <value>Liar-soft image archive</value>
</data> </data>