reorganized project directory structure.

This commit is contained in:
morkt
2015-08-19 21:40:32 +04:00
parent dd2c19c8c1
commit b05c253e8b
178 changed files with 6029 additions and 6016 deletions

548
ArcFormats/Entis/ArcNOA.cs Normal file
View File

@@ -0,0 +1,548 @@
//! \file ArcNOA.cs
//! \date Thu Apr 23 15:57:17 2015
//! \brief Entis GLS engine archives implementation.
//
// Copyright (C) 2015 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.Collections.Generic;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.IO;
using System.Text;
using GameRes.Formats.Properties;
using GameRes.Formats.Strings;
using GameRes.Utility;
namespace GameRes.Formats.Entis
{
internal class NoaOptions : ResourceOptions
{
public string Scheme { get; set; }
public string PassPhrase { get; set; }
}
internal class NoaEntry : Entry
{
public byte[] Extra;
public uint Encryption;
public uint Attr;
}
internal class NoaArchive : ArcFile
{
public string Password;
public NoaArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, string password = null)
: base (arc, impl, dir)
{
Password = password;
}
}
[Export(typeof(ArchiveFormat))]
public class NoaOpener : ArchiveFormat
{
public override string Tag { get { return "NOA"; } }
public override string Description { get { return "Entis GLS engine resource archive"; } }
public override uint Signature { get { return 0x69746e45; } } // 'Enti'
public override bool IsHierarchic { get { return true; } }
public override bool CanCreate { get { return false; } }
public NoaOpener ()
{
Extensions = new string[] { "noa", "dat" };
}
public static readonly Dictionary<string, Dictionary<string, string>> KnownKeys =
new Dictionary<string, Dictionary<string, string>> {
{ arcStrings.NOAIgnoreEncryption, new Dictionary<string, string>() },
{ "Alea Akaki Tsuki o Haruka ni Nozomi", new Dictionary<string, string> {
{ "data2.noa", "pnnAiYVqktMdLlVq9pnrXs1795vhu8ZluLh3MxmXyBBrhrhLoP2rlGn5dxcBP6d1cAAz08TMRIXNUFatVdJFWAwVphtAh4hx5NHMmLs8LoBE2KHAA8GnKJB1PpKeyMHu" },
{ "data4.noa", "yEbgydEFtIq3YiGUNMpCarJwR9mZbufPrbXtsoqbrJwT4F278kOWIgYzLtm1nP1Hns81u3F4Stwc42gdtrWIbnp9XfX3LsKiZe1TFUyrlTqsbhX8R8dEAVxLk9SVvCE7" } } },
{ "Do S Ane to Boku no Hounyou Kankei", new Dictionary<string, string> {
{ "d02.dat", "vwerc7s65r21bnfu" },
{ "d03.dat", "ctfvgbhnj67y8u" } } },
{ "Innyuu Famiresu", new Dictionary<string, string> {
{ "d01.dat", "vdiu$43AfUCfh9aksf" },
{ "d03.dat", "gaivnwq7365e021gf" } } },
{ "Konneko", new Dictionary<string, string> {
{ "script.noa", "convini_cat" } } },
{ "Yatohime Zankikou", new Dictionary<string, string> {
{ "data1.noa", "arcdatapass" },
{ "data6.noa", "cfe7231hf9qccda" },
{ "data7.noa", "ceiuvw86680efq0hHDUHF673j" } } },
{ "You! Apron Chakuyou", new Dictionary<string, string> {
{ "containerb.noa", "7DQ1Xm7ZahIv1ZwlFgyMTMryKC6OP9V6cAgL64WD5JLyvmeEyqTSA5rUbRigOtebnnK4MuOptwsbOf4K8UBDH4kpAUOQgB71Qr1qxtHGxQl8KZKj6WIYWpPh0G3JOJat" } } },
};
public override ArcFile TryOpen (ArcView file)
{
if (!file.View.AsciiEqual (0, "Entis\x1a"))
return null;
uint id = file.View.ReadUInt32 (8);
if (0x02000400 != id)
return null;
var reader = new IndexReader (file);
if (!reader.ParseRoot() || 0 == reader.Dir.Count)
return null;
if (!reader.HasEncrypted)
return new ArcFile (file, this, reader.Dir);
var options = Query<NoaOptions> (arcStrings.ArcEncryptedNotice);
string password = null;
if (!string.IsNullOrEmpty (options.PassPhrase))
{
password = options.PassPhrase;
}
else if (!string.IsNullOrEmpty (options.Scheme))
{
Dictionary<string, string> filemap;
if (KnownKeys.TryGetValue (options.Scheme, out filemap))
{
var filename = Path.GetFileName (file.Name).ToLowerInvariant();
filemap.TryGetValue (filename, out password);
}
}
if (string.IsNullOrEmpty (password))
return new ArcFile (file, this, reader.Dir);
return new NoaArchive (file, this, reader.Dir, password);
}
public override Stream OpenEntry (ArcFile arc, Entry entry)
{
var nent = entry as NoaEntry;
if (null == nent || !arc.File.View.AsciiEqual (entry.Offset, "filedata"))
return arc.File.CreateStream (entry.Offset, entry.Size);
ulong size = arc.File.View.ReadUInt64 (entry.Offset+8);
if (size > int.MaxValue)
throw new FileSizeException();
if (0 == size)
return Stream.Null;
var input = arc.File.CreateStream (entry.Offset+0x10, (uint)size);
try
{
var narc = arc as NoaArchive;
if (0 == nent.Encryption || size < 4 || null == narc || null == narc.Password)
return input;
if (0x40000000 != nent.Encryption)
{
Trace.WriteLine (string.Format ("{0}: unknown encryption scheme 0x{1:x8}",
nent.Name, nent.Encryption));
return input;
}
uint nTotalBytes = (uint)(size - 4);
var pBSHF = new BSHFDecodeContext (0x10000);
pBSHF.AttachInputFile (input);
pBSHF.PrepareToDecodeBSHFCode (narc.Password);
byte[] buf = new byte[nTotalBytes];
uint decoded = pBSHF.DecodeBSHFCodeBytes (buf, nTotalBytes);
if (decoded < nTotalBytes)
throw new EndOfStreamException ("Unexpected end of encrypted stream");
/* Something wrong with preceding length calculation, resulting CRC doesn't match
byte[] bufCRC = new byte[4];
int iCRC = 0;
for (int i = 0; i < buf.Length; ++i)
{
bufCRC[iCRC] ^= buf[i];
iCRC = (iCRC + 1) & 0x03;
}
uint orgCRC = arc.File.View.ReadUInt32 (entry.Offset+0x10+nTotalBytes);
uint crc = LittleEndian.ToUInt32 (bufCRC, 0);
if (orgCRC != crc)
{
Trace.WriteLine (string.Format ("{0}: CRC mismatch", nent.Name));
input.Position = 0;
return input;
}
*/
input.Dispose();
return new MemoryStream (buf);
}
catch
{
input.Dispose();
throw;
}
}
public override ResourceOptions GetDefaultOptions ()
{
return new NoaOptions {
Scheme = Settings.Default.NOAScheme,
PassPhrase = Settings.Default.NOAPassPhrase,
};
}
public override object GetAccessWidget ()
{
return new GUI.WidgetNOA();
}
internal class IndexReader
{
ArcView m_file;
List<Entry> m_dir = new List<Entry>();
bool m_found_encrypted = false;
const char PathSeparatorChar = '/';
public List<Entry> Dir { get { return m_dir; } }
public bool HasEncrypted { get { return m_found_encrypted; } }
public IndexReader (ArcView file)
{
m_file = file;
}
public bool ParseRoot ()
{
return ParseDirEntry (0x40, "");
}
private bool ParseDirEntry (long dir_offset, string cur_dir)
{
if (!m_file.View.AsciiEqual (dir_offset, "DirEntry"))
return false;
long size = m_file.View.ReadInt64 (dir_offset+8);
if (size <= 0 || size > int.MaxValue)
return false;
if ((uint)size > m_file.View.Reserve (dir_offset+8, (uint)size))
return false;
long base_offset = dir_offset;
dir_offset += 0x10;
int count = m_file.View.ReadInt32 (dir_offset);
dir_offset += 4;
if (m_dir.Capacity < m_dir.Count+count)
m_dir.Capacity = m_dir.Count+count;
for (int i = 0; i < count; ++i)
{
var entry = new NoaEntry();
entry.Size = m_file.View.ReadUInt32 (dir_offset);
dir_offset += 8;
entry.Attr = m_file.View.ReadUInt32 (dir_offset);
dir_offset += 4;
entry.Encryption = m_file.View.ReadUInt32 (dir_offset);
if (0 != entry.Encryption)
m_found_encrypted = true;
dir_offset += 4;
entry.Offset = base_offset + m_file.View.ReadInt64 (dir_offset);
if (!entry.CheckPlacement (m_file.MaxOffset))
{
entry.Size = (uint)(m_file.MaxOffset - entry.Offset);
}
dir_offset += 0x10;
uint extra_length = m_file.View.ReadUInt32 (dir_offset);
dir_offset += 4;
if (extra_length > 0 && 0 == (entry.Attr & 0x70))
{
entry.Extra = new byte[extra_length];
if (entry.Extra.Length != m_file.View.Read (dir_offset, entry.Extra, 0, extra_length))
return false;
}
dir_offset += extra_length;
uint name_length = m_file.View.ReadUInt32 (dir_offset);
dir_offset += 4;
string name = m_file.View.ReadString (dir_offset, name_length);
dir_offset += name_length;
if (string.IsNullOrEmpty (cur_dir))
entry.Name = name;
else
entry.Name = cur_dir + PathSeparatorChar + name;
entry.Type = FormatCatalog.Instance.GetTypeFromName (name);
if (0x10 == entry.Attr)
{
if (!ParseDirEntry (entry.Offset+0x10, entry.Name))
return false;
}
else if (0x20 == entry.Attr || 0x40 == entry.Attr)
{
break;
}
else
{
m_dir.Add (entry);
}
}
return true;
}
}
}
internal abstract class ERISADecodeContext
{
protected int m_nIntBufCount;
protected uint m_dwIntBuffer;
protected uint m_nBufferingSize;
protected uint m_nBufCount;
protected byte[] m_ptrBuffer;
protected int m_ptrNextBuf;
protected Stream m_pFile;
protected ERISADecodeContext m_pContext;
public ERISADecodeContext (uint nBufferingSize)
{
m_nIntBufCount = 0;
m_nBufferingSize = (nBufferingSize + 0x03) & ~0x03u;
m_nBufCount = 0;
m_ptrBuffer = new byte[nBufferingSize];
m_pFile = null;
m_pContext = null;
}
public void AttachInputFile (Stream file)
{
m_pFile = file;
m_pContext = null;
}
public void AttachInputContext (ERISADecodeContext context)
{
m_pFile = null;
m_pContext = context;
}
public uint ReadNextData (byte[] ptrBuffer, uint nBytes)
{
if (m_pFile != null)
{
return (uint)m_pFile.Read (ptrBuffer, 0, (int)nBytes);
}
else if (m_pContext != null)
{
return m_pContext.DecodeBytes (ptrBuffer, nBytes);
}
else
{
throw new ApplicationException ("Uninitialized ERISA encryption context");
}
}
public abstract uint DecodeBytes (Array ptrDst, uint nCount);
protected bool PrefetchBuffer()
{
if (0 == m_nIntBufCount)
{
if (0 == m_nBufCount)
{
m_ptrNextBuf = 0; // m_ptrBuffer;
m_nBufCount = ReadNextData (m_ptrBuffer, m_nBufferingSize);
if (0 == m_nBufCount)
{
return false;
}
if (0 != (m_nBufCount & 0x03))
{
uint i = m_nBufCount;
m_nBufCount += 4 - (m_nBufCount & 0x03);
while (i < m_nBufCount)
m_ptrBuffer[i ++] = 0;
}
}
m_nIntBufCount = 32;
m_dwIntBuffer =
((uint)m_ptrBuffer[m_ptrNextBuf] << 24) | ((uint)m_ptrBuffer[m_ptrNextBuf+1] << 16)
| ((uint)m_ptrBuffer[m_ptrNextBuf+2] << 8) | (uint)m_ptrBuffer[m_ptrNextBuf+3];
m_ptrNextBuf += 4;
m_nBufCount -= 4;
}
return true;
}
public void FlushBuffer ()
{
m_nIntBufCount = 0;
m_nBufCount = 0;
}
public int GetABit ()
{
if (!PrefetchBuffer())
{
return 1;
}
int nValue = ((int)m_dwIntBuffer) >> 31;
--m_nIntBufCount;
m_dwIntBuffer <<= 1;
return nValue;
}
public uint GetNBits (int n)
{
uint nCode = 0;
while (n != 0)
{
if (!PrefetchBuffer())
break;
int nCopyBits = Math.Min (n, m_nIntBufCount);
nCode = (nCode << nCopyBits) | (m_dwIntBuffer >> (32 - nCopyBits));
n -= nCopyBits;
m_nIntBufCount -= nCopyBits;
m_dwIntBuffer <<= nCopyBits;
}
return nCode;
}
}
internal class BSHFDecodeContext : ERISADecodeContext
{
ERIBshfBuffer m_pBshfBuf;
uint m_dwBufPos;
public BSHFDecodeContext (uint nBufferingSize) : base (nBufferingSize)
{
m_pBshfBuf = null;
}
public void PrepareToDecodeBSHFCode (string pszPassword)
{
if (null == m_pBshfBuf)
{
m_pBshfBuf = new ERIBshfBuffer();
}
if (string.IsNullOrEmpty (pszPassword))
{
pszPassword = " ";
}
int char_count = Encoding.ASCII.GetByteCount (pszPassword);
int length = Math.Max (char_count, 32);
var pass_bytes = new byte[length];
char_count = Encoding.ASCII.GetBytes (pszPassword, 0, pszPassword.Length, pass_bytes, 0);
if (char_count < 32)
{
pass_bytes[char_count++] = 0x1b;
for (int i = char_count; i < 32; ++i)
{
pass_bytes[i] = (byte)(pass_bytes[i % char_count] + pass_bytes[i - 1]);
}
}
m_pBshfBuf.m_strPassword = pass_bytes;
m_pBshfBuf.m_dwPassOffset = 0;
m_dwBufPos = 32;
}
public override uint DecodeBytes (Array ptrDst, uint nCount)
{
return DecodeBSHFCodeBytes (ptrDst as byte[], nCount);
}
public uint DecodeBSHFCodeBytes (byte[] ptrDst, uint nCount)
{
uint nDecoded = 0;
while (nDecoded < nCount)
{
if (m_dwBufPos >= 32)
{
for (int i = 0; i < 32; ++i)
{
if (0 == m_nBufCount)
{
m_ptrNextBuf = 0;
m_nBufCount = ReadNextData (m_ptrBuffer, m_nBufferingSize);
if (0 == m_nBufCount)
{
return nDecoded;
}
}
m_pBshfBuf.m_srcBSHF[i] = m_ptrBuffer[m_ptrNextBuf++];
m_nBufCount--;
}
m_pBshfBuf.DecodeBuffer();
m_dwBufPos = 0;
}
ptrDst[nDecoded++] = m_pBshfBuf.m_bufBSHF[m_dwBufPos++];
}
return nDecoded;
}
}
internal class ERIBshfBuffer
{
public byte[] m_strPassword;
public uint m_dwPassOffset = 0;
public byte[] m_bufBSHF = new byte[32];
public byte[] m_srcBSHF = new byte[32];
public byte[] m_maskBSHF = new byte[32];
public void DecodeBuffer ()
{
int nPassLen = m_strPassword.Length;
if ((int)m_dwPassOffset >= nPassLen)
{
m_dwPassOffset = 0;
}
for (int i = 0; i < 32; ++i)
{
m_bufBSHF[i] = 0;
m_maskBSHF[i] = 0;
}
int iPos = (int) m_dwPassOffset++;
int iBit = 0;
for (int i = 0; i < 256; ++i)
{
iBit = (iBit + m_strPassword[iPos++]) & 0xFF;
if (iPos >= nPassLen)
{
iPos = 0;
}
int iOffset = (iBit >> 3);
int iMask = (0x80 >> (iBit & 0x07));
while (0xFF == m_maskBSHF[iOffset])
{
iBit = (iBit + 8) & 0xFF;
iOffset = (iBit >> 3);
}
while (0 != (m_maskBSHF[iOffset] & iMask))
{
iBit ++;
iMask >>= 1;
if (0 == iMask)
{
iBit = (iBit + 8) & 0xFF;
iOffset = (iBit >> 3);
iMask = 0x80;
}
}
Debug.Assert (iMask != 0);
m_maskBSHF[iOffset] |= (byte) iMask;
if (0 != (m_srcBSHF[(i >> 3)] & (0x80 >> (i & 0x07))))
{
m_bufBSHF[iOffset] |= (byte)iMask;
}
}
}
}
}

View File

@@ -0,0 +1,387 @@
//! \file AudioMIO.cs
//! \date Thu May 28 13:33:07 2015
//! \brief Entis audio format implementation.
//
// Copyright (C) 2015 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.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.IO;
using System.Threading;
using GameRes.Utility;
namespace GameRes.Formats.Entis
{
[Export(typeof(AudioFormat))]
public class MioAudio : AudioFormat
{
public override string Tag { get { return "MIO"; } }
public override string Description { get { return "Entis engine compressed audio format"; } }
public override uint Signature { get { return 0x69746e45u; } } // 'Enti'
public override SoundInput TryOpen (Stream file)
{
byte[] header = new byte[0x40];
if (header.Length != file.Read (header, 0, header.Length))
return null;
if (0x03000100 != LittleEndian.ToUInt32 (header, 8))
return null;
if (!Binary.AsciiEqual (header, 0x10, "Music Interleaved and Orthogonal transformed"))
return null;
return new MioInput (file);
}
}
public class MioInput : SoundInput
{
MioInfoHeader m_info;
long m_stream_pos;
int m_bitrate;
uint m_total_samples;
ERISADecodeContext m_pmioc;
MioDecoder m_pmiod;
Stream m_decoded_stream;
public int ChannelCount { get { return m_info.ChannelCount; } }
public uint BitsPerSample { get { return m_info.BitsPerSample; } }
public override int SourceBitrate { get { return m_bitrate; } }
public override string SourceFormat { get { return "mio"; } }
#region Stream Members
public override bool CanSeek { get { return m_decoded_stream.CanSeek; } }
public override long Position
{
get { return m_decoded_stream.Position; }
set { m_decoded_stream.Position = value; }
}
public override int Read (byte[] buffer, int offset, int count)
{
return Read_Threaded (buffer, offset, count);
}
#endregion
public MioInput (Stream file) : base (file)
{
file.Position = 0x40;
using (var erif = new EriFile (file))
{
var section = erif.ReadSection();
if (section.Id != "Header " || section.Length <= 0 || section.Length > int.MaxValue)
throw new InvalidFormatException();
m_stream_pos = 0x50 + section.Length;
int header_size = (int)section.Length;
while (header_size > 8)
{
section = erif.ReadSection();
header_size -= 8;
if (section.Length <= 0 || section.Length > header_size)
break;
if ("SoundInf" == section.Id)
{
m_info = new MioInfoHeader();
m_info.Version = erif.ReadInt32();
m_info.Transformation = (CvType)erif.ReadInt32();
m_info.Architecture = (EriCode)erif.ReadInt32();
m_info.ChannelCount = erif.ReadInt32();
m_info.SamplesPerSec = erif.ReadUInt32();
m_info.BlocksetCount = erif.ReadUInt32();
m_info.SubbandDegree = erif.ReadInt32();
m_info.AllSampleCount = erif.ReadUInt32();
m_info.LappedDegree = erif.ReadUInt32();
m_info.BitsPerSample = erif.ReadUInt32();
break;
}
header_size -= (int)section.Length;
erif.BaseStream.Seek (section.Length, SeekOrigin.Current);
}
if (null == m_info)
throw new InvalidFormatException ("MIO sound header not found");
erif.BaseStream.Position = m_stream_pos;
var stream_size = erif.FindSection ("Stream ");
m_stream_pos = erif.BaseStream.Position;
m_pmiod = new MioDecoder (m_info);
if (EriCode.Nemesis != m_info.Architecture)
m_pmioc = new HuffmanDecodeContext (0x10000);
else
throw new NotImplementedException ("MIO Nemesis encoding not implemented");
int pcm_bitrate = (int)(m_info.SamplesPerSec * BitsPerSample * ChannelCount);
var format = new GameRes.WaveFormat();
format.FormatTag = 1;
format.Channels = (ushort)ChannelCount;
format.SamplesPerSecond = m_info.SamplesPerSec;
format.BitsPerSample = (ushort)BitsPerSample;
format.BlockAlign = (ushort)(BitsPerSample/8*format.Channels);
format.AverageBytesPerSecond = (uint)pcm_bitrate/8;
this.Format = format;
m_decoded_stream = LoadChunks (erif);
if (0 != m_total_samples)
m_bitrate = (int)(stream_size * 8 * m_info.SamplesPerSec / m_total_samples);
this.PcmSize = m_total_samples * ChannelCount * BitsPerSample / 8;
m_decoded_stream.Position = 0;
}
}
class MioChunk : MioDataHeader
{
public uint FirstSample;
public long Position;
public uint Size;
}
class ChunkStream : Stream
{
Stream m_source;
MioChunk m_chunk;
public ChunkStream (Stream source, MioChunk chunk)
{
m_source = source;
m_chunk = chunk;
m_source.Position = m_chunk.Position;
}
public override bool CanRead { get { return true; } }
public override bool CanWrite { get { return false; } }
public override bool CanSeek { get { return m_source.CanSeek; } }
public override long Length { get { return m_chunk.Size; } }
public override long Position
{
get { return m_source.Position-m_chunk.Position; }
set { Seek (value, SeekOrigin.Begin); }
}
public override long Seek (long offset, SeekOrigin origin)
{
if (origin == SeekOrigin.Begin)
offset += m_chunk.Position;
else if (origin == SeekOrigin.Current)
offset += m_source.Position;
else
offset += m_chunk.Position + m_chunk.Size;
if (offset < m_chunk.Position)
offset = m_chunk.Position;
m_source.Position = offset;
return offset - m_chunk.Position;
}
public override void Flush()
{
m_source.Flush();
}
public override int Read (byte[] buf, int index, int count)
{
long remaining = (m_chunk.Position + m_chunk.Size) - m_source.Position;
if (count > remaining)
count = (int)remaining;
if (count <= 0)
return 0;
return m_source.Read (buf, index, count);
}
public override void SetLength (long length)
{
throw new System.NotSupportedException ();
}
public override void Write (byte[] buffer, int offset, int count)
{
throw new System.NotSupportedException ();
}
public override void WriteByte (byte value)
{
throw new System.NotSupportedException ();
}
}
private Stream LoadChunks (EriFile erif)
{
uint current_sample = 0;
List<MioChunk> chunks = new List<MioChunk>();
try
{
erif.BaseStream.Position = m_stream_pos;
for (;;)
{
long chunk_length = erif.FindSection ("SoundStm");
if (chunk_length > int.MaxValue)
throw new FileSizeException();
var chunk = new MioChunk();
chunk.FirstSample = current_sample;
chunk.Version = erif.ReadByte();
chunk.Flags = erif.ReadByte();
erif.ReadInt16();
chunk.SampleCount = erif.ReadUInt32();
chunk.Position = erif.BaseStream.Position;
chunk.Size = (uint)(chunk_length - 8);
current_sample += chunk.SampleCount;
chunks.Add (chunk);
erif.BaseStream.Seek (chunk.Size, SeekOrigin.Current);
}
}
catch (EndOfStreamException) { /* ignore EOF errors */ }
m_total_samples = current_sample;
if (0 == m_total_samples)
{
m_decode_finished = true;
return Stream.Null;
}
uint sample_bytes = (uint)ChannelCount * BitsPerSample / 8;
var total_bytes = m_total_samples * sample_bytes;
m_wait_handles = new WaitHandle[2] { m_available_chunk, m_decode_complete };
m_chunk_queue = new ConcurrentQueue<byte[]>();
m_worker = new BackgroundWorker();
m_worker.WorkerSupportsCancellation = true;
m_worker.DoWork += DoWork_Decode;
m_worker.RunWorkerAsync (chunks);
return new MemoryStream ((int)total_bytes);
}
bool m_decode_finished = false;
AutoResetEvent m_decode_complete = new AutoResetEvent (false);
AutoResetEvent m_available_chunk = new AutoResetEvent (false);
WaitHandle[] m_wait_handles;
ConcurrentQueue<byte[]> m_chunk_queue;
BackgroundWorker m_worker;
Exception m_decode_error = null;
private void DoWork_Decode (object sender, DoWorkEventArgs e)
{
try
{
var worker = sender as BackgroundWorker;
var chunks = e.Argument as IEnumerable<MioChunk>;
uint sample_bytes = (uint)ChannelCount * BitsPerSample / 8;
foreach (var chunk in chunks)
{
if (worker.CancellationPending)
{
e.Cancel = true;
break;
}
using (var input = new ChunkStream (Source, chunk))
{
var wave_buf = new byte[chunk.SampleCount * sample_bytes];
m_pmioc.AttachInputFile (input);
if (!m_pmiod.DecodeSound (m_pmioc, chunk, wave_buf, 0))
throw new InvalidFormatException();
m_chunk_queue.Enqueue (wave_buf);
m_available_chunk.Set();
}
}
}
catch (Exception X)
{
Trace.WriteLine (X.Message, "[MIO]");
m_decode_error = X;
}
finally
{
m_decode_complete.Set();
}
}
private int Read_Threaded (byte[] buf, int idx, int count)
{
var current_pos = Position;
int total_read = 0;
if (current_pos < m_decoded_stream.Length)
{
int available_bytes = (int)(m_decoded_stream.Length - current_pos);
int read = m_decoded_stream.Read (buf, idx, Math.Min (count, available_bytes));
idx += read;
count -= read;
total_read += read;
}
if (count > 0 && (!m_decode_finished || m_chunk_queue.Count > 0))
{
current_pos = Position;
m_decoded_stream.Seek (0, SeekOrigin.End);
for (;;)
{
byte[] wave_buf = null;
while (m_chunk_queue.TryDequeue (out wave_buf))
{
m_decoded_stream.Write (wave_buf, 0, wave_buf.Length);
if (current_pos + count <= m_decoded_stream.Length)
break;
}
if (m_decode_finished || (current_pos + count <= m_decoded_stream.Length))
break;
int evt = WaitHandle.WaitAny (m_wait_handles);
if (1 == evt)
{
m_decode_finished = true;
if (m_decode_error != null)
{
m_decoded_stream.Position = current_pos;
throw m_decode_error;
}
}
}
m_decoded_stream.Position = current_pos;
total_read += m_decoded_stream.Read (buf, idx, count);
}
return total_read;
}
#region IDisposable Members
bool _mio_disposed = false;
protected override void Dispose (bool disposing)
{
if (!_mio_disposed)
{
if (disposing)
{
if (!m_decode_finished)
{
m_worker.CancelAsync();
m_decode_complete.WaitOne();
}
if (m_decoded_stream != null)
m_decoded_stream.Dispose();
m_decode_complete.Dispose();
m_available_chunk.Dispose();
}
_mio_disposed = true;
base.Dispose (disposing);
}
}
#endregion
}
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,229 @@
//! \file ImageERI.cs
//! \date Tue May 26 12:04:30 2015
//! \brief Entis rasterized image format.
//
// Copyright (C) 2015 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.ComponentModel.Composition;
using System.IO;
using System.Windows.Media;
using GameRes.Utility;
namespace GameRes.Formats.Entis
{
internal class EriMetaData : ImageMetaData
{
public int StreamPos;
public int Version;
public CvType Transformation;
public EriCode Architecture;
public int FormatType;
public bool VerticalFlip;
public int ClippedPixel;
public int SamplingFlags;
public ulong QuantumizedBits;
public ulong AllottedBits;
public int BlockingDegree;
public int LappedBlock;
public int FrameTransform;
public int FrameDegree;
}
public enum CvType
{
Lossless_ERI = 0x03020000,
DCT_ERI = 0x00000001,
LOT_ERI = 0x00000005,
LOT_ERI_MSS = 0x00000105,
}
public enum EriCode
{
RunlengthGamma = -1,
RunlengthHuffman = -4,
Nemesis = -16,
}
public enum EriImage
{
RGB = 0x00000001,
RGBA = 0x04000001,
Gray = 0x00000002,
TypeMask = 0x00FFFFFF,
WithPalette = 0x01000000,
UseClipping = 0x02000000,
WithAlpha = 0x04000000,
SideBySide = 0x10000000,
}
internal class EriFile : BinaryReader
{
internal struct Section
{
public AsciiString Id;
public long Length;
}
public EriFile (Stream stream) : base (stream, System.Text.Encoding.ASCII, true)
{
}
public Section ReadSection ()
{
var section = new Section();
section.Id = new AsciiString (8);
if (8 != this.Read (section.Id.Value, 0, 8))
throw new EndOfStreamException();
section.Length = this.ReadInt64();
return section;
}
public long FindSection (string name)
{
var id = new AsciiString (8);
for (;;)
{
if (8 != this.Read (id.Value, 0, 8))
throw new EndOfStreamException();
var length = this.ReadInt64();
if (length < 0)
throw new EndOfStreamException();
if (id == name)
return length;
this.BaseStream.Seek (length, SeekOrigin.Current);
}
}
}
[Export(typeof(ImageFormat))]
public class EriFormat : ImageFormat
{
public override string Tag { get { return "ERI"; } }
public override string Description { get { return "Entis rasterized image format"; } }
public override uint Signature { get { return 0x69746e45u; } } // 'Enti'
public override ImageMetaData ReadMetaData (Stream stream)
{
byte[] header = new byte[0x40];
if (header.Length != stream.Read (header, 0, header.Length))
return null;
if (0x03000100 != LittleEndian.ToUInt32 (header, 8))
return null;
if (!Binary.AsciiEqual (header, 0x10, "Entis Rasterized Image"))
return null;
using (var reader = new EriFile (stream))
{
var section = reader.ReadSection();
if (section.Id != "Header " || section.Length <= 0)
return null;
int header_size = (int)section.Length;
int stream_pos = 0x50 + header_size;
EriMetaData info = null;
while (header_size > 8)
{
section = reader.ReadSection();
header_size -= 8;
if (section.Length <= 0 || section.Length > header_size)
break;
if ("ImageInf" == section.Id)
{
int version = reader.ReadInt32();
if (version != 0x00020100 && version != 0x00020200)
return null;
info = new EriMetaData { StreamPos = stream_pos, Version = version };
info.Transformation = (CvType)reader.ReadInt32();
info.Architecture = (EriCode)reader.ReadInt32();
info.FormatType = reader.ReadInt32();
int w = reader.ReadInt32();
int h = reader.ReadInt32();
info.Width = (uint)Math.Abs (w);
info.Height = (uint)Math.Abs (h);
info.VerticalFlip = h < 0;
info.BPP = reader.ReadInt32();
info.ClippedPixel = reader.ReadInt32();
info.SamplingFlags = reader.ReadInt32();
info.QuantumizedBits = reader.ReadUInt64();
info.AllottedBits = reader.ReadUInt64();
info.BlockingDegree = reader.ReadInt32();
info.LappedBlock = reader.ReadInt32();
info.FrameTransform = reader.ReadInt32();
info.FrameDegree = reader.ReadInt32();
break;
}
header_size -= (int)section.Length;
reader.BaseStream.Seek (section.Length, SeekOrigin.Current);
}
return info;
}
}
public override ImageData Read (Stream stream, ImageMetaData info)
{
var meta = info as EriMetaData;
if (null == meta)
throw new ArgumentException ("EriFormat.Read should be supplied with EriMetaData", "info");
stream.Position = meta.StreamPos;
using (var input = new EriFile (stream))
{
Color[] palette = null;
for (;;) // ReadSection throws an exception in case of EOF
{
var section = input.ReadSection();
if ("Stream " == section.Id)
continue;
if ("ImageFrm" == section.Id)
break;
if ("Palette " == section.Id && info.BPP <= 8 && section.Length <= 0x400)
{
palette = ReadPalette (stream, (int)section.Length);
continue;
}
input.BaseStream.Seek (section.Length, SeekOrigin.Current);
}
var reader = new EriReader (stream, meta, palette);
reader.DecodeImage();
return ImageData.Create (info, reader.Format, reader.Palette, reader.Data, reader.Stride);
}
}
private Color[] ReadPalette (Stream input, int palette_length)
{
var palette_data = new byte[0x400];
if (palette_length > palette_data.Length)
throw new InvalidFormatException();
if (palette_length != input.Read (palette_data, 0, palette_length))
throw new InvalidFormatException();
var colors = new Color[256];
for (int i = 0; i < 256; ++i)
{
colors[i] = Color.FromRgb (palette_data[i*4+2], palette_data[i*4+1], palette_data[i*4]);
}
return colors;
}
public override void Write (Stream file, ImageData image)
{
throw new NotImplementedException ("EriFormat.Write not implemented");
}
}
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
<Grid x:Class="GameRes.Formats.GUI.WidgetNOA"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:entis="clr-namespace:GameRes.Formats.Entis"
xmlns:p="clr-namespace:GameRes.Formats.Properties"
MaxWidth="300">
<ComboBox Name="Scheme" ItemsSource="{Binding Source={x:Static entis:NoaOpener.KnownKeys}, Path=Keys, Mode=OneWay}"
SelectedValue="{Binding Source={x:Static p:Settings.Default}, Path=NOAScheme, Mode=TwoWay}"
Width="200"/>
</Grid>

View File

@@ -0,0 +1,18 @@
using System.Windows.Controls;
namespace GameRes.Formats.GUI
{
/// <summary>
/// Interaction logic for WidgetNOA.xaml
/// </summary>
public partial class WidgetNOA : Grid
{
public WidgetNOA ()
{
InitializeComponent ();
// select first scheme as default
if (-1 == Scheme.SelectedIndex)
Scheme.SelectedIndex = 0;
}
}
}