Marble engine resources implementation.

This commit is contained in:
morkt 2015-03-31 14:34:49 +04:00
parent e95cec0f3c
commit 26905015c2
3 changed files with 542 additions and 0 deletions

94
ArcFormats/ArcMBL.cs Normal file
View File

@ -0,0 +1,94 @@
//! \file ArcMBL.cs
//! \date Fri Mar 27 23:11:19 2015
//! \brief Marble Engine archive 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.IO;
using GameRes.Utility;
namespace GameRes.Formats.Marble
{
[Export(typeof(ArchiveFormat))]
public class MblOpener : ArchiveFormat
{
public override string Tag { get { return "MBL"; } }
public override string Description { get { return "Marble engine resource archive"; } }
public override uint Signature { get { return 0; } }
public override bool IsHierarchic { get { return false; } }
public override bool CanCreate { get { return false; } }
public override ArcFile TryOpen (ArcView file)
{
int count = file.View.ReadInt32 (0);
if (count <= 0 || count > 0xfffff)
return null;
ArcFile arc = null;
uint filename_len = file.View.ReadUInt32 (4);
if (filename_len > 0 && filename_len <= 0xff)
arc = ReadIndex (file, count, filename_len, 8);
if (null == arc)
arc = ReadIndex (file, count, 0x10, 4);
return arc;
}
private ArcFile ReadIndex (ArcView file, int count, uint filename_len, uint index_offset)
{
uint index_size = (8u + filename_len) * (uint)count;
if (index_size > file.View.Reserve (index_offset, index_size))
return null;
var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i)
{
string name = file.View.ReadString (index_offset, filename_len);
if (name.Length+1 < filename_len)
{
uint ext_length = (uint)Math.Min (filename_len-name.Length-1, 3);
string ext = file.View.ReadString (index_offset+name.Length+1, ext_length);
if (!string.IsNullOrEmpty (ext))
{
if ("OG" == ext || "O" == ext)
ext = "OGG";
else if ("PR" == ext || "P" == ext)
ext = "PRS";
else if ("WA" == ext || "W" == ext)
ext = "WAY";
name = Path.ChangeExtension (name, ext);
}
}
name = name.ToLowerInvariant();
var entry = FormatCatalog.Instance.CreateEntry (name);
index_offset += (uint)filename_len;
entry.Offset = file.View.ReadUInt32 (index_offset);
entry.Size = file.View.ReadUInt32 (index_offset+4);
if (!entry.CheckPlacement (file.MaxOffset))
return null;
dir.Add (entry);
index_offset += 8;
}
return new ArcFile (file, this, dir);
}
}
}

227
ArcFormats/AudioWADY.cs Normal file
View File

@ -0,0 +1,227 @@
//! \file AudioWADY.cs
//! \date Sat Mar 28 01:36:42 2015
//! \brief Marble engine audio 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.Text;
namespace GameRes.Formats.Marble
{
public class WadyInput : SoundInput
{
byte MulValue;
public override long Position
{
get { return m_input.Position; }
set { m_input.Position = value; }
}
public override bool CanSeek { get { return m_input.CanSeek; } }
public override int SourceBitrate
{
get { return (int)Format.AverageBytesPerSecond * 8; }
}
public override long Seek (long offset, SeekOrigin origin)
{
return m_input.Seek (offset, origin);
}
public override int Read (byte[] buffer, int offset, int count)
{
return m_input.Read (buffer, offset, count);
}
public override int ReadByte ()
{
return m_input.ReadByte();
}
public WadyInput (Stream file) : base (new MemoryStream())
{
using (var input = new BinaryReader (file))
{
input.BaseStream.Seek (5, SeekOrigin.Begin);
MulValue = input.ReadByte();
input.BaseStream.Seek (6, SeekOrigin.Current);
int src_size = input.ReadInt32();
input.BaseStream.Seek (16, SeekOrigin.Current);
var format = new WaveFormat();
format.FormatTag = input.ReadUInt16();
format.Channels = input.ReadUInt16();
format.SamplesPerSecond = input.ReadUInt32();
format.AverageBytesPerSecond = input.ReadUInt32();
format.BlockAlign = input.ReadUInt16();
format.BitsPerSample = input.ReadUInt16();
format.ExtraSize = 0;
this.Format = format;
int remaining = (int)(input.BaseStream.Length-input.BaseStream.Position);
if (remaining == src_size)
{
(m_input as MemoryStream).Capacity = src_size * 2;
Decode (input, src_size, m_input);
}
else
Decode2 (input, m_input);
m_input.Position = 0;
this.PcmSize = m_input.Length;
}
}
private void Decode (BinaryReader input, int count, Stream output)
{
using (var buffer = new BinaryWriter (output, Encoding.ASCII, true))
{
ushort sampleL = 0;
ushort sampleR = 0;
for (int i = 0; i < count; ++i)
{
byte v = input.ReadByte();
if (0 != (v & 0x80))
sampleL = (ushort)(v << 9);
else
sampleL += (ushort)(MulValue * SampleTable[v]);
buffer.Write (sampleL);
if (1 != Format.Channels)
{
++i;
v = input.ReadByte();
if (0 != (v & 0x80))
sampleR = (ushort)(v << 9);
else
sampleR += (ushort)(MulValue * SampleTable[v]);
buffer.Write (sampleR);
}
}
}
}
private void Decode2 (BinaryReader input, Stream output)
{
if (1 != Format.Channels)
{
int channel_size = input.ReadInt32();
Decode3 (input, output, 2);
input.BaseStream.Position = 0x38 + channel_size;
output.Position = 2;
Decode3 (input, output, 2);
}
else
Decode3 (input, output, 0);
}
private void Decode3 (BinaryReader input, Stream output, int step)
{
input.ReadInt32(); // unpacked_size
int count = input.ReadInt32();
using (var buffer = new BinaryWriter (output, Encoding.ASCII, true))
{
short sample = input.ReadInt16();
buffer.Write (sample);
for (int i = 0; i < count; ++i)
{
if (count - 300 == i)
sample = 0;
ushort v = input.ReadByte();
if (0 != (v & 1))
{
ushort v14 = (ushort)((v >> 1) & 0x7F);
if (0 != (v14 & 0x40))
sample = (short)(v14 << 10);
else
sample += (short)SampleTable2[v14];
buffer.Write (sample);
if (step != 0)
buffer.BaseStream.Seek (step, SeekOrigin.Current);
}
else
{
v |= (ushort)(input.ReadByte() << 8);
int repeat = SizeTable[(v >> 1) & 7];
short end = (short)(v & 0xFFF0);
double inc = (end - sample) / (double)repeat;
double v8 = sample;
for (int j = 0; j < repeat; ++j)
{
v8 += inc;
buffer.Write ((short)v8);
if (step != 0)
buffer.BaseStream.Seek (step, SeekOrigin.Current);
}
sample = end;
}
}
}
}
static readonly int[] SizeTable = new int[] { 3, 4, 5, 6, 8, 0x10, 0x20, 0x100 };
static readonly ushort[] SampleTable = new ushort[] {
0x0000, 0x0002, 0x0004, 0x0006, 0x0008, 0x000A, 0x000C, 0x000F,
0x0012, 0x0015, 0x0018, 0x001C, 0x0020, 0x0024, 0x0028, 0x002C,
0x0031, 0x0036, 0x003B, 0x0040, 0x0046, 0x004C, 0x0052, 0x0058,
0x005F, 0x0066, 0x006D, 0x0074, 0x007C, 0x0084, 0x008C, 0x0094,
0x00A0, 0x00AA, 0x00B4, 0x00BE, 0x00C8, 0x00D2, 0x00DC, 0x00E6,
0x00F0, 0x00FF, 0x010E, 0x011D, 0x012C, 0x0140, 0x0154, 0x0168,
0x017C, 0x0190, 0x01A9, 0x01C2, 0x01DB, 0x01F4, 0x020D, 0x0226,
0x0244, 0x0262, 0x028A, 0x02BC, 0x02EE, 0x0320, 0x0384, 0x03E8,
0x0000, 0xFFFE, 0xFFFC, 0xFFFA, 0xFFF8, 0xFFF6, 0xFFF4, 0xFFF1,
0xFFEE, 0xFFEB, 0xFFE8, 0xFFE4, 0xFFE0, 0xFFDC, 0xFFD8, 0xFFD4,
0xFFCF, 0xFFCA, 0xFFC5, 0xFFC0, 0xFFBA, 0xFFB4, 0xFFAE, 0xFFA8,
0xFFA1, 0xFF9A, 0xFF93, 0xFF8C, 0xFF84, 0xFF7C, 0xFF74, 0xFF6C,
0xFF60, 0xFF56, 0xFF4C, 0xFF42, 0xFF38, 0xFF2E, 0xFF24, 0xFF1A,
0xFF10, 0xFF01, 0xFEF2, 0xFEE3, 0xFED4, 0xFEC0, 0xFEAC, 0xFE98,
0xFE84, 0xFE70, 0xFE57, 0xFE3E, 0xFE25, 0xFE0C, 0xFDF3, 0xFDDA,
0xFDBC, 0xFD9E, 0xFD76, 0xFD44, 0xFD12, 0xFCE0, 0xFC7C, 0xFC18,
};
static readonly ushort[] SampleTable2 = new ushort[] {
0x0000, 0x0004, 0x0008, 0x000C, 0x0013, 0x0018, 0x001E, 0x0026,
0x002F, 0x003B, 0x004A, 0x005C, 0x0073, 0x0090, 0x00B4, 0x00E1,
0x0119, 0x0160, 0x01B8, 0x0226, 0x02AF, 0x035B, 0x0431, 0x053E,
0x068E, 0x0831, 0x0A3D, 0x0CCD, 0x1000, 0x1400, 0x1900, 0x1F40,
0x0000, 0xFFFC, 0xFFF8, 0xFFF4, 0xFFED, 0xFFE8, 0xFFE2, 0xFFDA,
0xFFD1, 0xFFC5, 0xFFB6, 0xFFA4, 0xFF8D, 0xFF70, 0xFF4C, 0xFF1F,
0xFEE7, 0xFEA0, 0xFE48, 0xFDDA, 0xFD51, 0xFCA5, 0xFBCF, 0xFAC2,
0xF972, 0xF7CF, 0xF5C3, 0xF333, 0xF000, 0xEC00, 0xE700, 0xE0C0,
};
}
[Export(typeof(AudioFormat))]
public class WadyAudio : AudioFormat
{
public override string Tag { get { return "WAY"; } }
public override string Description { get { return "Marble engine wave audio format"; } }
public override uint Signature { get { return 0x59444157u; } } // 'WADY'
public override SoundInput TryOpen (Stream file)
{
return new WadyInput (file);
}
}
}

221
ArcFormats/ImagePRS.cs Normal file
View File

@ -0,0 +1,221 @@
//! \file ImagePRS.cs
//! \date Sat Mar 28 00:15:43 2015
//! \brief Marble engine 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.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using GameRes.Utility;
namespace GameRes.Formats.Marble
{
internal class PrsMetaData : ImageMetaData
{
public byte Flag;
public uint PackedSize;
}
[Export(typeof(ImageFormat))]
public class PrsFormat : ImageFormat
{
public override string Tag { get { return "PRS"; } }
public override string Description { get { return "Marble engine image format"; } }
public override uint Signature { get { return 0; } }
public override void Write (Stream file, ImageData image)
{
throw new NotImplementedException ("PrsFormat.Write not implemented");
}
public override ImageMetaData ReadMetaData (Stream stream)
{
var header = new byte[0x10];
if (header.Length != stream.Read (header, 0, header.Length))
return null;
if (header[0] != 'Y' || header[1] != 'B' || header[3] != 3)
return null;
return new PrsMetaData
{
Width = LittleEndian.ToUInt16 (header, 12),
Height = LittleEndian.ToUInt16 (header, 14),
BPP = 24,
Flag = header[2],
PackedSize = LittleEndian.ToUInt32 (header, 4),
};
}
public override ImageData Read (Stream stream, ImageMetaData info)
{
var meta = info as PrsMetaData;
if (null == meta)
throw new ArgumentException ("PrsFormat.Read should be supplied with PrsMetaData", "info");
stream.Position = 0x10;
using (var reader = new Reader (stream, meta))
{
reader.Unpack();
byte[] pixels = reader.Data;
var bitmap = BitmapSource.Create ((int)meta.Width, (int)meta.Height, 96, 96,
PixelFormats.Bgr24, null, pixels, (int)meta.Width*3);
bitmap.Freeze();
return new ImageData (bitmap, meta);
}
}
internal class Reader : IDisposable
{
BinaryReader m_input;
byte[] m_output;
uint m_size;
byte m_flag;
public byte[] Data { get { return m_output; } }
public Reader (Stream file, PrsMetaData info)
{
m_input = new BinaryReader (file, Encoding.ASCII, true);
m_output = new byte[info.Width*info.Height*3];
m_size = info.PackedSize;
m_flag = info.Flag;
}
static readonly int[] LengthTable = InitLengthTable();
private static int[] InitLengthTable ()
{
var length_table = new int[256];
for (int i = 0; i < 0xfe; ++i)
length_table[i] = i + 3;
length_table[0xfe] = 0x400;
length_table[0xff] = 0x1000;
return length_table;
}
public void Unpack ()
{
int dst = 0;
int remaining = (int)m_size;
int bit = 0;
int ctl = 0;
while (remaining > 0 && dst < m_output.Length)
{
bit >>= 1;
if (0 == bit)
{
ctl = m_input.ReadByte();
--remaining;
bit = 0x80;
}
if (0 == (ctl & bit))
{
m_output[dst++] = m_input.ReadByte();
--remaining;
continue;
}
int b = m_input.ReadByte();
--remaining;
int length = 0;
int shift = 0;
if (0 != (b & 0x80))
{
if (remaining <= 0)
break;
shift = m_input.ReadByte();
--remaining;
shift |= (b & 0x3f) << 8;
if (0 != (b & 0x40))
{
if (remaining <= 0)
break;
int offset = m_input.ReadByte();
--remaining;
length = LengthTable[offset];
}
else
{
length = (shift & 0xf) + 3;
shift >>= 4;
}
}
else
{
length = b >> 2;
b &= 3;
if (3 == b)
{
length += 9;
int read = m_input.Read (m_output, dst, length);
if (read < length)
break;
remaining -= length;
dst += length;
continue;
}
shift = length;
length = b + 2;
}
++shift;
if (dst < shift)
throw new InvalidFormatException ("Invalid offset value");
Binary.CopyOverlapped (m_output, dst-shift, dst, length);
dst += length;
}
if (m_flag != 0)
{
for (int i = 3; i < m_output.Length; ++i)
m_output[i] += m_output[i-3];
}
}
#region IDisposable Members
bool disposed = false;
public void Dispose ()
{
Dispose (true);
GC.SuppressFinalize (this);
}
protected virtual void Dispose (bool disposing)
{
if (!disposed)
{
if (disposing)
{
m_input.Dispose();
}
disposed = true;
}
}
#endregion
}
}
}