mirror of
https://github.com/crskycode/GARbro.git
synced 2025-01-11 20:39:29 +08:00
implemented Kogado resource archive.
This commit is contained in:
parent
ad1b51b24c
commit
fa4b8b6bce
@ -59,6 +59,7 @@
|
||||
<Compile Include="ArcAMI.cs" />
|
||||
<Compile Include="ArcDRS.cs" />
|
||||
<Compile Include="ArcINT.cs" />
|
||||
<Compile Include="ArcKogado.cs" />
|
||||
<Compile Include="ArcMajiro.cs" />
|
||||
<Compile Include="ArcNPA.cs" />
|
||||
<Compile Include="ArcNSA.cs" />
|
||||
@ -101,6 +102,7 @@
|
||||
<Compile Include="ImageRCT.cs" />
|
||||
<Compile Include="ImageTLG.cs" />
|
||||
<Compile Include="ImageWCG.cs" />
|
||||
<Compile Include="KogadoCocotte.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
|
291
ArcFormats/ArcKogado.cs
Normal file
291
ArcFormats/ArcKogado.cs
Normal 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
833
ArcFormats/KogadoCocotte.cs
Normal 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, <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;
|
||||
|
||||
/// <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 < 1<<(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 <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<<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 <n from init)</param>
|
||||
public void Update (int sym)
|
||||
{
|
||||
if (m_left <= 0)
|
||||
DoRescale();
|
||||
m_left--;
|
||||
m_newf[sym] += (ushort)m_incr;
|
||||
}
|
||||
}
|
||||
}
|
9
ArcFormats/Strings/arcStrings.Designer.cs
generated
9
ArcFormats/Strings/arcStrings.Designer.cs
generated
@ -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>
|
||||
/// Looks up a localized string similar to Liar-soft image archive.
|
||||
/// </summary>
|
||||
|
@ -171,6 +171,9 @@ Choose appropriate encryption scheme.</value>
|
||||
Enter archive encryption key or choose
|
||||
predefined encryption scheme.</value>
|
||||
</data>
|
||||
<data name="KogadoDescription" xml:space="preserve">
|
||||
<value>Kogado game engine resource archive</value>
|
||||
</data>
|
||||
<data name="LWGDescription" xml:space="preserve">
|
||||
<value>Liar-soft image archive</value>
|
||||
</data>
|
||||
|
Loading…
x
Reference in New Issue
Block a user