mirror of
https://github.com/crskycode/GARbro.git
synced 2026-07-08 01:30:18 +08:00
implemented 'Nemesis' compression.
This commit is contained in:
293
ArcFormats/Entis/ErisaNemesis.cs
Normal file
293
ArcFormats/Entis/ErisaNemesis.cs
Normal file
@@ -0,0 +1,293 @@
|
||||
//! \file ErisaNemesis.cs
|
||||
//! \date Tue Jan 19 18:24:23 2016
|
||||
//! \brief Erisa Nemesis encoding implementation.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace GameRes.Formats.Entis
|
||||
{
|
||||
class NemesisDecodeContext : ProbDecodeContext
|
||||
{
|
||||
int m_iLastSymbol;
|
||||
int m_nNemesisLeft;
|
||||
int m_nNemesisNext;
|
||||
byte[] m_pNemesisBuf;
|
||||
int m_nNemesisIndex;
|
||||
bool m_flagEOF;
|
||||
|
||||
ErisaProbBase m_pProbERISA;
|
||||
NemesisPhraseLookup[] m_pNemesisLookup;
|
||||
|
||||
public NemesisDecodeContext (uint buffer_size = 0x10000) : base (buffer_size)
|
||||
{
|
||||
m_flagEOF = false;
|
||||
}
|
||||
|
||||
public void PrepareToDecodeERISANCode ()
|
||||
{
|
||||
if (null == m_pProbERISA)
|
||||
m_pProbERISA = new ErisaProbBase();
|
||||
|
||||
m_iLastSymbol = 0;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
m_bytLastSymbol[i] = 0;
|
||||
|
||||
m_pProbERISA.dwWorkUsed = 0;
|
||||
m_pProbERISA.epmBaseModel.Initialize();
|
||||
|
||||
for (int i = 0; i < ErisaProbBase.SlotMax; ++i)
|
||||
{
|
||||
m_pProbERISA.ptrProbIndex[i] = new ErisaProbModel();
|
||||
}
|
||||
PrepareToDecodeERISACode();
|
||||
|
||||
if (null == m_pNemesisBuf)
|
||||
{
|
||||
m_pNemesisBuf = new byte[Nemesis.BufSize];
|
||||
}
|
||||
if (null == m_pNemesisLookup)
|
||||
{
|
||||
m_pNemesisLookup = new NemesisPhraseLookup[0x100];
|
||||
}
|
||||
for (int i = 0; i < m_pNemesisBuf.Length; ++i)
|
||||
m_pNemesisBuf[i] = 0;
|
||||
for (int i = 0; i < m_pNemesisLookup.Length; ++i)
|
||||
m_pNemesisLookup[i] = new NemesisPhraseLookup();
|
||||
m_nNemesisIndex = 0;
|
||||
|
||||
m_nNemesisLeft = 0;
|
||||
m_flagEOF = false;
|
||||
}
|
||||
|
||||
public override uint DecodeBytes (Array ptrDst, uint nCount)
|
||||
{
|
||||
return DecodeNemesisCodeBytes (ptrDst as byte[], nCount);
|
||||
}
|
||||
|
||||
public uint DecodeNemesisCodeBytes (byte[] ptrDst, uint nCount)
|
||||
{
|
||||
if (m_flagEOF)
|
||||
return 0;
|
||||
|
||||
ErisaProbBase pBase = m_pProbERISA;
|
||||
uint nDecoded = 0;
|
||||
int dst = 0;
|
||||
byte bytSymbol;
|
||||
while (nDecoded < nCount)
|
||||
{
|
||||
if (m_nNemesisLeft > 0)
|
||||
{
|
||||
uint nNemesisCount = (uint)m_nNemesisLeft;
|
||||
if (nNemesisCount > nCount - nDecoded)
|
||||
{
|
||||
nNemesisCount = nCount - nDecoded;
|
||||
}
|
||||
byte bytLastSymbol = m_pNemesisBuf[(m_nNemesisIndex - 1) & Nemesis.BufMask];
|
||||
|
||||
for (uint i = 0; i < nNemesisCount; ++i)
|
||||
{
|
||||
bytSymbol = bytLastSymbol;
|
||||
if (m_nNemesisNext >= 0)
|
||||
{
|
||||
bytSymbol = m_pNemesisBuf[m_nNemesisNext++];
|
||||
m_nNemesisNext &= Nemesis.BufMask;
|
||||
}
|
||||
m_bytLastSymbol[m_iLastSymbol++] = bytSymbol;
|
||||
m_iLastSymbol &= 3;
|
||||
|
||||
var phrase = m_pNemesisLookup[bytSymbol];
|
||||
phrase.index[phrase.first] = (uint)m_nNemesisIndex;
|
||||
phrase.first = (phrase.first + 1) & Nemesis.IndexMask;
|
||||
bytLastSymbol = bytSymbol;
|
||||
|
||||
m_pNemesisBuf[m_nNemesisIndex++] = bytSymbol;
|
||||
m_nNemesisIndex &= Nemesis.BufMask;
|
||||
|
||||
ptrDst[dst++] = bytSymbol;
|
||||
}
|
||||
m_nNemesisLeft -= (int)nNemesisCount;
|
||||
nDecoded += nNemesisCount;
|
||||
continue;
|
||||
}
|
||||
|
||||
int iDeg;
|
||||
ErisaProbModel pModel = pBase.epmBaseModel;
|
||||
for (iDeg = 0; iDeg < 4; ++iDeg)
|
||||
{
|
||||
int iLast = m_bytLastSymbol[(m_iLastSymbol + 3 - iDeg) & 3]
|
||||
>> ErisaProbBase.m_nShiftCount[iDeg];
|
||||
if (pModel.SubModel[iLast].Symbol < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
Debug.Assert ((uint)pModel.SubModel[iLast].Symbol < pBase.dwWorkUsed);
|
||||
pModel = pBase.ptrProbIndex[pModel.SubModel[iLast].Symbol];
|
||||
}
|
||||
int iSym = DecodeERISACodeIndex (pModel);
|
||||
if (iSym < 0)
|
||||
{
|
||||
return nDecoded;
|
||||
}
|
||||
int nSymbol = pModel.SymTable[iSym].Symbol;
|
||||
int iSymIndex = pModel.IncreaseSymbol (iSym);
|
||||
|
||||
bool fNemesis = false;
|
||||
if (nSymbol == ErisaProbModel.EscCode)
|
||||
{
|
||||
if (pModel != pBase.epmBaseModel)
|
||||
{
|
||||
iSym = DecodeERISACodeIndex (pBase.epmBaseModel);
|
||||
if (iSym < 0)
|
||||
{
|
||||
return nDecoded;
|
||||
}
|
||||
nSymbol = pBase.epmBaseModel.SymTable[iSym].Symbol;
|
||||
pBase.epmBaseModel.IncreaseSymbol (iSym);
|
||||
if (nSymbol != ErisaProbModel.EscCode)
|
||||
{
|
||||
pModel.AddSymbol ((short)nSymbol);
|
||||
iSym = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
fNemesis = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fNemesis = true;
|
||||
}
|
||||
}
|
||||
if (fNemesis)
|
||||
{
|
||||
int nLength, nPhraseIndex;
|
||||
nPhraseIndex = DecodeERISACode (m_pPhraseIndexProb);
|
||||
if (nPhraseIndex == ErisaProbModel.EscCode)
|
||||
{
|
||||
m_flagEOF = true;
|
||||
return nDecoded;
|
||||
}
|
||||
if (0 == nPhraseIndex)
|
||||
{
|
||||
nLength = DecodeERISACode (m_pRunLenProb);
|
||||
}
|
||||
else
|
||||
{
|
||||
nLength = DecodeERISACode (m_pPhraseLenProb);
|
||||
}
|
||||
if (nLength == ErisaProbModel.EscCode)
|
||||
{
|
||||
return nDecoded;
|
||||
}
|
||||
byte bytLastSymbol = m_pNemesisBuf[(m_nNemesisIndex - 1) & Nemesis.BufMask];
|
||||
var phrase = m_pNemesisLookup[bytLastSymbol];
|
||||
m_nNemesisLeft = nLength;
|
||||
if (0 == nPhraseIndex)
|
||||
{
|
||||
m_nNemesisNext = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_nNemesisNext = (int)phrase.index[(phrase.first - nPhraseIndex) & Nemesis.IndexMask];
|
||||
Debug.Assert (m_pNemesisBuf[m_nNemesisNext] == bytLastSymbol);
|
||||
m_nNemesisNext = (m_nNemesisNext + 1) & Nemesis.BufMask;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
bytSymbol = (byte)nSymbol;
|
||||
m_bytLastSymbol[m_iLastSymbol++] = bytSymbol;
|
||||
m_iLastSymbol &= 3;
|
||||
|
||||
var ppl = m_pNemesisLookup[bytSymbol];
|
||||
ppl.index[ppl.first] = (uint)m_nNemesisIndex;
|
||||
ppl.first = (ppl.first + 1) & Nemesis.IndexMask;
|
||||
m_pNemesisBuf[m_nNemesisIndex++] = bytSymbol;
|
||||
m_nNemesisIndex &= Nemesis.BufMask;
|
||||
|
||||
ptrDst[dst++] = bytSymbol;
|
||||
nDecoded++;
|
||||
|
||||
if ((pBase.dwWorkUsed < ErisaProbBase.SlotMax) && (iDeg < 4))
|
||||
{
|
||||
int iSymbol = ((byte)nSymbol) >> ErisaProbBase.m_nShiftCount[iDeg];
|
||||
Debug.Assert (iSymbol < ErisaProbModel.SubSortMax);
|
||||
if (++pModel.SubModel[iSymbol].Occured >= ErisaProbBase.m_nNewProbLimit[iDeg])
|
||||
{
|
||||
int i;
|
||||
ErisaProbModel pParent = pModel;
|
||||
pModel = pBase.epmBaseModel;
|
||||
for (i = 0; i <= iDeg; ++i)
|
||||
{
|
||||
iSymbol = m_bytLastSymbol[(m_iLastSymbol + 3 - i) & 3]
|
||||
>> ErisaProbBase.m_nShiftCount[i];
|
||||
if (pModel.SubModel[iSymbol].Symbol < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
Debug.Assert ((uint)pModel.SubModel[iSymbol].Symbol < pBase.dwWorkUsed);
|
||||
pModel = pBase.ptrProbIndex[pModel.SubModel[iSymbol].Symbol];
|
||||
}
|
||||
if ((i <= iDeg) && (pModel.SubModel[iSymbol].Symbol < 0))
|
||||
{
|
||||
ErisaProbModel pNew = pBase.ptrProbIndex[pBase.dwWorkUsed];
|
||||
pModel.SubModel[iSymbol].Symbol = (short)(pBase.dwWorkUsed++);
|
||||
|
||||
pNew.TotalCount = 0;
|
||||
int j = 0;
|
||||
for (i = 0; i < (int)pParent.SymbolSorts; ++i)
|
||||
{
|
||||
ushort wOccured = (ushort)(pParent.SymTable[i].Occured >> 4);
|
||||
if (wOccured > 0 && (pParent.SymTable[i].Symbol != ErisaProbModel.EscCode))
|
||||
{
|
||||
pNew.TotalCount += wOccured;
|
||||
pNew.SymTable[j].Occured = wOccured;
|
||||
pNew.SymTable[j].Symbol = pParent.SymTable[i].Symbol;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
pNew.TotalCount++;
|
||||
pNew.SymTable[j].Occured = 1;
|
||||
pNew.SymTable[j].Symbol = ErisaProbModel.EscCode;
|
||||
pNew.SymbolSorts = ++j;
|
||||
|
||||
for (i = 0; i < ErisaProbModel.SubSortMax; ++i)
|
||||
{
|
||||
pNew.SubModel[i].Occured = 0;
|
||||
pNew.SubModel[i].Symbol = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nDecoded;
|
||||
}
|
||||
}
|
||||
|
||||
internal class ErisaProbBase
|
||||
{
|
||||
public uint dwWorkUsed;
|
||||
public ErisaProbModel epmBaseModel = new ErisaProbModel();
|
||||
public ErisaProbModel[] ptrProbIndex = new ErisaProbModel[SlotMax];
|
||||
|
||||
public const int SlotMax = 0x800;
|
||||
|
||||
public static readonly int[] m_nShiftCount = { 1, 3, 4, 5 };
|
||||
public static readonly int[] m_nNewProbLimit = { 0x01, 0x08, 0x10, 0x20 };
|
||||
}
|
||||
|
||||
internal static class Nemesis
|
||||
{
|
||||
public const int BufSize = 0x10000;
|
||||
public const int BufMask = 0xFFFF;
|
||||
public const int IndexLimit = 0x100;
|
||||
public const int IndexMask = 0xFF;
|
||||
}
|
||||
|
||||
internal class NemesisPhraseLookup
|
||||
{
|
||||
public uint first;
|
||||
public uint[] index = new uint[Nemesis.IndexLimit];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user