(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 //! \date Thu Apr 23 15:57:17 2015
//! \brief Entis GLS engine archives implementation. //! \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 // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to // of this software and associated documentation files (the "Software"), to
@ -77,7 +77,7 @@ namespace GameRes.Formats.Entis
public NoaOpener () public NoaOpener ()
{ {
Extensions = new string[] { "noa", "dat", "rsa" }; Extensions = new string[] { "noa", "dat", "rsa", "arc" };
Settings = new[] { NoaEncoding }; Settings = new[] { NoaEncoding };
} }
@ -96,14 +96,14 @@ namespace GameRes.Formats.Entis
var reader = new IndexReader (file, NoaEncoding.Get<Encoding>()); var reader = new IndexReader (file, NoaEncoding.Get<Encoding>());
if (!reader.ParseRoot() || 0 == reader.Dir.Count) if (!reader.ParseRoot() || 0 == reader.Dir.Count)
return null; return null;
if (!reader.HasEncrypted) if (reader.HasEncrypted)
return new ArcFile (file, this, reader.Dir); {
var password = GetArcPassword (file.Name); var password = GetArcPassword (file.Name);
if (string.IsNullOrEmpty (password)) if (!string.IsNullOrEmpty (password))
return new ArcFile (file, this, reader.Dir);
return new NoaArchive (file, this, reader.Dir, password); return new NoaArchive (file, this, reader.Dir, password);
} }
return new ArcFile (file, this, reader.Dir);
}
public override Stream OpenEntry (ArcFile arc, Entry entry) public override Stream OpenEntry (ArcFile arc, Entry entry)
{ {
@ -118,8 +118,8 @@ namespace GameRes.Formats.Entis
if (EncType.ERISACode == nent.Encryption) if (EncType.ERISACode == nent.Encryption)
{ {
using (var enc = arc.File.CreateStream (entry.Offset+0x10, (uint)size-4)) var enc = arc.File.CreateStream (entry.Offset+0x10, (uint)size-4);
return DecodeNemesis (enc); return new ErisaNemesisStream (enc, (int)entry.Size);
} }
var narc = arc as NoaArchive; var narc = arc as NoaArchive;
@ -183,7 +183,7 @@ namespace GameRes.Formats.Entis
if (null == cotomi) if (null == cotomi)
continue; continue;
using (var res = new MemoryStream (cotomi)) using (var res = new MemoryStream (cotomi))
using (var input = DecodeNemesis (res)) using (var input = new ErisaNemesisStream (res))
{ {
var xml = new XmlDocument(); var xml = new XmlDocument();
xml.Load (input); xml.Load (input);
@ -210,24 +210,6 @@ namespace GameRes.Formats.Entis
return null; 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) Stream DecodeBSHF (Stream input, string password)
{ {
uint nTotalBytes = (uint)input.Length - 4; uint nTotalBytes = (uint)input.Length - 4;
@ -317,10 +299,11 @@ namespace GameRes.Formats.Entis
entry.Encryption = m_file.View.ReadUInt32 (dir_offset); entry.Encryption = m_file.View.ReadUInt32 (dir_offset);
m_found_encrypted = m_found_encrypted || (EncType.Raw != entry.Encryption && EncType.ERISACode != entry.Encryption); m_found_encrypted = m_found_encrypted || (EncType.Raw != entry.Encryption && EncType.ERISACode != entry.Encryption);
bool is_packed = EncType.ERISACode == entry.Encryption;
dir_offset += 4; dir_offset += 4;
entry.Offset = base_offset + m_file.View.ReadInt64 (dir_offset); 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); entry.Size = (uint)(m_file.MaxOffset - entry.Offset);
} }

View File

@ -5,6 +5,7 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
namespace GameRes.Formats.Entis namespace GameRes.Formats.Entis
{ {
@ -64,17 +65,16 @@ namespace GameRes.Formats.Entis
public override uint DecodeBytes (Array ptrDst, uint nCount) 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) if (m_flagEOF)
return 0; return 0;
ErisaProbBase pBase = m_pProbERISA; ErisaProbBase pBase = m_pProbERISA;
uint nDecoded = 0; uint nDecoded = 0;
int dst = 0;
byte bytSymbol; byte bytSymbol;
while (nDecoded < nCount) while (nDecoded < nCount)
{ {
@ -150,7 +150,6 @@ namespace GameRes.Formats.Entis
if (nSymbol != ErisaProbModel.EscCode) if (nSymbol != ErisaProbModel.EscCode)
{ {
pModel.AddSymbol ((short)nSymbol); pModel.AddSymbol ((short)nSymbol);
iSym = -1;
} }
else else
{ {
@ -214,7 +213,7 @@ namespace GameRes.Formats.Entis
if ((pBase.dwWorkUsed < ErisaProbBase.SlotMax) && (iDeg < 4)) 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) if (iSymbol >= ErisaProbModel.SubSortMax)
throw new InvalidFormatException ("Invalid Nemesis encoding sequence"); throw new InvalidFormatException ("Invalid Nemesis encoding sequence");
if (++pModel.SubModel[iSymbol].Occured >= ErisaProbBase.m_nNewProbLimit[iDeg]) if (++pModel.SubModel[iSymbol].Occured >= ErisaProbBase.m_nNewProbLimit[iDeg])
@ -295,4 +294,45 @@ namespace GameRes.Formats.Entis
public uint first; public uint first;
public uint[] index = new uint[Nemesis.IndexLimit]; 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();
}
}
} }