(Entis): improved ErisaN compression handling.

This commit is contained in:
morkt 2018-09-07 15:47:10 +04:00
parent cc6747c537
commit e1771edc81
2 changed files with 59 additions and 36 deletions

View File

@ -2,7 +2,7 @@
//! \date Thu Apr 23 15:57:17 2015
//! \brief Entis GLS engine archives implementation.
//
// Copyright (C) 2015-2016 by morkt
// Copyright (C) 2015-2018 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
@ -77,7 +77,7 @@ namespace GameRes.Formats.Entis
public NoaOpener ()
{
Extensions = new string[] { "noa", "dat", "rsa" };
Extensions = new string[] { "noa", "dat", "rsa", "arc" };
Settings = new[] { NoaEncoding };
}
@ -96,14 +96,14 @@ namespace GameRes.Formats.Entis
var reader = new IndexReader (file, NoaEncoding.Get<Encoding>());
if (!reader.ParseRoot() || 0 == reader.Dir.Count)
return null;
if (!reader.HasEncrypted)
return new ArcFile (file, this, reader.Dir);
if (reader.HasEncrypted)
{
var password = GetArcPassword (file.Name);
if (string.IsNullOrEmpty (password))
return new ArcFile (file, this, reader.Dir);
if (!string.IsNullOrEmpty (password))
return new NoaArchive (file, this, reader.Dir, password);
}
return new ArcFile (file, this, reader.Dir);
}
public override Stream OpenEntry (ArcFile arc, Entry entry)
{
@ -118,8 +118,8 @@ namespace GameRes.Formats.Entis
if (EncType.ERISACode == nent.Encryption)
{
using (var enc = arc.File.CreateStream (entry.Offset+0x10, (uint)size-4))
return DecodeNemesis (enc);
var enc = arc.File.CreateStream (entry.Offset+0x10, (uint)size-4);
return new ErisaNemesisStream (enc, (int)entry.Size);
}
var narc = arc as NoaArchive;
@ -183,7 +183,7 @@ namespace GameRes.Formats.Entis
if (null == cotomi)
continue;
using (var res = new MemoryStream (cotomi))
using (var input = DecodeNemesis (res))
using (var input = new ErisaNemesisStream (res))
{
var xml = new XmlDocument();
xml.Load (input);
@ -210,24 +210,6 @@ namespace GameRes.Formats.Entis
return null;
}
Stream DecodeNemesis (Stream input)
{
var decoder = new NemesisDecodeContext();
decoder.AttachInputFile (input);
decoder.PrepareToDecodeERISANCode();
var file = new MemoryStream ((int)input.Length);
var buffer = new byte[0x10000];
for (;;)
{
int read = (int)decoder.DecodeNemesisCodeBytes (buffer, 0x10000);
if (0 == read)
break;
file.Write (buffer, 0, read);
}
file.Position = 0;
return file;
}
Stream DecodeBSHF (Stream input, string password)
{
uint nTotalBytes = (uint)input.Length - 4;
@ -317,10 +299,11 @@ namespace GameRes.Formats.Entis
entry.Encryption = m_file.View.ReadUInt32 (dir_offset);
m_found_encrypted = m_found_encrypted || (EncType.Raw != entry.Encryption && EncType.ERISACode != entry.Encryption);
bool is_packed = EncType.ERISACode == entry.Encryption;
dir_offset += 4;
entry.Offset = base_offset + m_file.View.ReadInt64 (dir_offset);
if (!entry.CheckPlacement (m_file.MaxOffset))
if (!is_packed && !entry.CheckPlacement (m_file.MaxOffset))
{
entry.Size = (uint)(m_file.MaxOffset - entry.Offset);
}

View File

@ -5,6 +5,7 @@
using System;
using System.Diagnostics;
using System.IO;
namespace GameRes.Formats.Entis
{
@ -64,17 +65,16 @@ namespace GameRes.Formats.Entis
public override uint DecodeBytes (Array ptrDst, uint nCount)
{
return DecodeNemesisCodeBytes (ptrDst as byte[], nCount);
return DecodeNemesisCodeBytes (ptrDst as byte[], 0, nCount);
}
public uint DecodeNemesisCodeBytes (byte[] ptrDst, uint nCount)
public uint DecodeNemesisCodeBytes (byte[] ptrDst, int dst, uint nCount)
{
if (m_flagEOF)
return 0;
ErisaProbBase pBase = m_pProbERISA;
uint nDecoded = 0;
int dst = 0;
byte bytSymbol;
while (nDecoded < nCount)
{
@ -150,7 +150,6 @@ namespace GameRes.Formats.Entis
if (nSymbol != ErisaProbModel.EscCode)
{
pModel.AddSymbol ((short)nSymbol);
iSym = -1;
}
else
{
@ -214,7 +213,7 @@ namespace GameRes.Formats.Entis
if ((pBase.dwWorkUsed < ErisaProbBase.SlotMax) && (iDeg < 4))
{
int iSymbol = ((byte)nSymbol) >> ErisaProbBase.m_nShiftCount[iDeg];
int iSymbol = bytSymbol >> ErisaProbBase.m_nShiftCount[iDeg];
if (iSymbol >= ErisaProbModel.SubSortMax)
throw new InvalidFormatException ("Invalid Nemesis encoding sequence");
if (++pModel.SubModel[iSymbol].Occured >= ErisaProbBase.m_nNewProbLimit[iDeg])
@ -295,4 +294,45 @@ namespace GameRes.Formats.Entis
public uint first;
public uint[] index = new uint[Nemesis.IndexLimit];
}
internal class ErisaNemesisStream : InputProxyStream
{
NemesisDecodeContext m_decoder;
int m_remaining;
bool m_eof = false;
public ErisaNemesisStream (Stream input, int output_length = 0) : base (input)
{
m_decoder = new NemesisDecodeContext();
m_decoder.AttachInputFile (input);
m_decoder.PrepareToDecodeERISANCode();
m_remaining = output_length > 0 ? output_length : int.MaxValue;
}
public override int Read (byte[] buffer, int offset, int count)
{
int read = 0;
if (!m_eof && m_remaining > 0)
{
uint chunk_size = (uint)Math.Min (count, m_remaining);
read = (int)m_decoder.DecodeNemesisCodeBytes (buffer, offset, chunk_size);
m_eof = read < count;
m_remaining -= read;
}
return read;
}
public override bool CanSeek { get { return false; } }
public override long Length { get { throw new NotSupportedException(); } }
public override long Position {
get { throw new NotSupportedException(); }
set { throw new NotSupportedException(); }
}
public override long Seek (long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
}
}