updated formats.

(ExeFile): limited support for 16-bit Windows executables.
(MbImageFormat): recognize 'MK' signature.
(XP3, VF, YPF): added common executable signature.
(PICT): improved parser.
(Macromedia): improved support, recognize archives within windows executables.
(SEEN): fixed decompression.
This commit is contained in:
morkt 2023-09-25 21:01:22 +04:00
parent bfd81f9ec4
commit 3c3f2013ef
16 changed files with 220 additions and 374 deletions

View File

@ -729,7 +729,6 @@
<Compile Include="AZSys\ArcAZSys.cs" /> <Compile Include="AZSys\ArcAZSys.cs" />
<Compile Include="Ethornell\ArcBGI.cs" /> <Compile Include="Ethornell\ArcBGI.cs" />
<Compile Include="Ffa\ArcBlackPackage.cs" /> <Compile Include="Ffa\ArcBlackPackage.cs" />
<None Include="Macromedia\ArcCCT.cs" />
<Compile Include="Cherry\ArcCherry.cs" /> <Compile Include="Cherry\ArcCherry.cs" />
<Compile Include="Circus\ArcCircus.cs" /> <Compile Include="Circus\ArcCircus.cs" />
<Compile Include="ArcCommon.cs" /> <Compile Include="ArcCommon.cs" />

View File

@ -55,7 +55,7 @@ namespace GameRes.Formats.TinkerBell
var entry = FormatCatalog.Instance.Create<Entry> (name); var entry = FormatCatalog.Instance.Create<Entry> (name);
entry.Offset = file.View.ReadUInt32 (index_offset+0x18); entry.Offset = file.View.ReadUInt32 (index_offset+0x18);
entry.Size = file.View.ReadUInt32 (index_offset+0x10); entry.Size = file.View.ReadUInt32 (index_offset+0x10);
if (!entry.CheckPlacement (file.MaxOffset)) if (entry.Offset <= index_offset || !entry.CheckPlacement (file.MaxOffset))
return null; return null;
dir.Add (entry); dir.Add (entry);
} }

View File

@ -40,6 +40,7 @@ namespace GameRes.Formats
Section m_overlay; Section m_overlay;
uint m_image_base = 0; uint m_image_base = 0;
List<ImageSection> m_section_list; List<ImageSection> m_section_list;
bool? m_is_NE;
public ExeFile (ArcView file) public ExeFile (ArcView file)
{ {
@ -56,9 +57,18 @@ namespace GameRes.Formats
/// </summary> /// </summary>
public Section Whole { get; private set; } public Section Whole { get; private set; }
public bool IsWin16 => m_is_NE ?? (m_is_NE = IsNe()).Value;
private bool IsNe ()
{
uint ne_offset = View.ReadUInt32 (0x3C);
return ne_offset < m_file.MaxOffset-2 && View.AsciiEqual (ne_offset, "NE");
}
/// <summary> /// <summary>
/// Dictionary of executable file sections. /// Dictionary of executable file sections.
/// </summary> /// </summary>
///
public IReadOnlyDictionary<string, Section> Sections public IReadOnlyDictionary<string, Section> Sections
{ {
get get
@ -255,6 +265,11 @@ namespace GameRes.Formats
private void InitSectionTable () private void InitSectionTable ()
{ {
if (IsWin16)
{
InitNe();
return;
}
long pe_offset = GetHeaderOffset(); long pe_offset = GetHeaderOffset();
int opt_header = View.ReadUInt16 (pe_offset+0x14); // SizeOfOptionalHeader int opt_header = View.ReadUInt16 (pe_offset+0x14); // SizeOfOptionalHeader
long section_table = pe_offset+opt_header+0x18; long section_table = pe_offset+opt_header+0x18;
@ -294,6 +309,26 @@ namespace GameRes.Formats
m_section_list = list; m_section_list = list;
} }
void InitNe ()
{
uint ne_offset = m_file.View.ReadUInt32 (0x3C);
int segment_count = m_file.View.ReadUInt16 (ne_offset + 0x1C);
uint seg_table = m_file.View.ReadUInt16 (ne_offset + 0x22) + ne_offset;
int shift = m_file.View.ReadUInt16 (ne_offset + 0x32);
uint last_seg_end = 0;
for (int i = 0; i < segment_count; ++i)
{
uint offset = (uint)m_file.View.ReadUInt16 (seg_table) << shift;
uint size = m_file.View.ReadUInt16 (seg_table+2);
if (offset + size > last_seg_end)
last_seg_end = offset + size;
}
m_overlay.Offset = last_seg_end;
m_overlay.Size = (uint)(m_file.MaxOffset - last_seg_end);
m_section_table = new Dictionary<string, Section>(); // these are empty for 16-bit executables
m_section_list = new List<ImageSection>(); //
}
/// <summary> /// <summary>
/// Helper class for executable file resources access. /// Helper class for executable file resources access.
/// </summary> /// </summary>

View File

@ -38,17 +38,18 @@ namespace GameRes.Formats
public MbImageFormat () public MbImageFormat ()
{ {
Extensions = new[] { "bmp", "gra" }; Extensions = new[] { "bmp", "gra", "xxx" };
} }
public override ImageMetaData ReadMetaData (IBinaryStream stream) public override ImageMetaData ReadMetaData (IBinaryStream stream)
{ {
int c1 = stream.ReadByte(); int c1 = stream.ReadByte();
int c2 = stream.ReadByte(); int c2 = stream.ReadByte();
// MB/MC/MK/CL
switch (c1) switch (c1)
{ {
case 'M': case 'M':
if ('B' != c2 && 'C' != c2) if ('B' != c2 && 'C' != c2 && 'K' != c2)
return null; return null;
break; break;
case 'C': case 'C':

View File

@ -88,7 +88,7 @@ namespace GameRes.Formats.KiriKiri
public Xp3Opener () public Xp3Opener ()
{ {
Signatures = new uint[] { 0x0d335058, 0 }; Signatures = new uint[] { 0x0d335058, 0x00905A4D, 0 };
Extensions = new[] { "XP3", "EXE" }; Extensions = new[] { "XP3", "EXE" };
ContainedFormats = new[] { "TLG", "BMP", "PNG", "JPEG", "OGG", "WAV", "TXT" }; ContainedFormats = new[] { "TLG", "BMP", "PNG", "JPEG", "OGG", "WAV", "TXT" };
} }

View File

@ -45,7 +45,7 @@ namespace GameRes.Formats.LiveMaker
public VffOpener () public VffOpener ()
{ {
Extensions = new string[] { "dat", "exe" }; Extensions = new string[] { "dat", "exe" };
Signatures = new uint[] { 0x666676, 0 }; Signatures = new uint[] { 0x666676, 0x00905A4D, 0 };
} }
public override ArcFile TryOpen (ArcView file) public override ArcFile TryOpen (ArcView file)

View File

@ -50,7 +50,7 @@ namespace GameRes.Formats.MAI
public CmFormat () public CmFormat ()
{ {
Extensions = new string[] { "cm" }; Extensions = new string[] { "cmp" };
} }
public override void Write (Stream file, ImageData image) public override void Write (Stream file, ImageData image)
@ -60,24 +60,22 @@ namespace GameRes.Formats.MAI
public override ImageMetaData ReadMetaData (IBinaryStream stream) public override ImageMetaData ReadMetaData (IBinaryStream stream)
{ {
if ('C' != stream.ReadByte() || 'M' != stream.ReadByte()) var header = stream.ReadHeader (0x20);
if ('C' != header[0] || 'M' != header[1])
return null; return null;
var header = stream.ReadBytes (0x1e); if (1 != header[0x0E])
if (header.Length != 0x1e)
return null; return null;
if (1 != header[0x0c]) uint size = LittleEndian.ToUInt32 (header, 2);
return null;
uint size = LittleEndian.ToUInt32 (header, 0);
if (size != stream.Length) if (size != stream.Length)
return null; return null;
var info = new CmMetaData(); var info = new CmMetaData();
info.Width = LittleEndian.ToUInt16 (header, 4); info.Width = LittleEndian.ToUInt16 (header, 6);
info.Height = LittleEndian.ToUInt16 (header, 6); info.Height = LittleEndian.ToUInt16 (header, 8);
info.Colors = LittleEndian.ToUInt16 (header, 8); info.Colors = LittleEndian.ToUInt16 (header, 0x0A);
info.BPP = header[0x0a]; info.BPP = header[0x0C];
info.IsCompressed = 0 != header[0x0b]; info.IsCompressed = 0 != header[0x0D];
info.DataOffset = LittleEndian.ToUInt32 (header, 0x0e); info.DataOffset = LittleEndian.ToUInt32 (header, 0x10);
info.DataLength = LittleEndian.ToUInt32 (header, 0x12); info.DataLength = LittleEndian.ToUInt32 (header, 0x14);
if (info.DataLength > size) if (info.DataLength > size)
return null; return null;
return info; return info;
@ -158,7 +156,7 @@ namespace GameRes.Formats.MAI
public AmFormat () public AmFormat ()
{ {
Extensions = new string[] { "am", "ami" }; Extensions = new string[] { "amp", "ami" };
} }
public override void Write (Stream file, ImageData image) public override void Write (Stream file, ImageData image)
@ -235,6 +233,8 @@ namespace GameRes.Formats.MAI
m_pixels = new byte[m_width*m_height*4]; m_pixels = new byte[m_width*m_height*4];
} }
static readonly Color Default8bppTransparencyColor = Color.FromRgb (0, 0xFE, 0);
public void Unpack () public void Unpack ()
{ {
if (m_info.Colors > 0) if (m_info.Colors > 0)
@ -262,13 +262,23 @@ namespace GameRes.Formats.MAI
m_pixels[dst+3] = alpha; m_pixels[dst+3] = alpha;
}; };
else else
{
const int alphaScale = 0x11;
var alphaColor = Color.FromRgb (0, 0xFE, 0);
copy_pixel = (src, dst, alpha) => { copy_pixel = (src, dst, alpha) => {
var color = Palette.Colors[m_output[src]]; var color = Palette.Colors[m_output[src]];
if (Default8bppTransparencyColor == color)
alpha = 0;
else if (0 == alpha)
alpha = 0xFF;
else
alpha *= alphaScale;
m_pixels[dst] = color.B; m_pixels[dst] = color.B;
m_pixels[dst+1] = color.G; m_pixels[dst+1] = color.G;
m_pixels[dst+2] = color.R; m_pixels[dst+2] = color.R;
m_pixels[dst+3] = alpha; m_pixels[dst+3] = alpha;
}; };
}
int src_stride = m_width * m_pixel_size; int src_stride = m_width * m_pixel_size;
for (int y = 0; y < m_height; ++y) for (int y = 0; y < m_height; ++y)
{ {

View File

@ -190,6 +190,9 @@ namespace GameRes.Formats.Apple
break; break;
} }
case 0x001E: // DefHilite
break;
case 0x0090: case 0x0090:
case 0x0091: case 0x0091:
case 0x0098: case 0x0098:

View File

@ -1,256 +0,0 @@
//! \file ArcCCT.cs
//! \date Fri Jun 26 01:15:26 2015
//! \brief Macromedia Director archive access 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 System.Linq;
using System.Text;
using GameRes.Compression;
using GameRes.Utility;
namespace GameRes.Formats.Macromedia
{
[Export(typeof(ArchiveFormat))]
public class CctOpener : ArchiveFormat
{
public override string Tag { get { return "CCT"; } }
public override string Description { get { return "Macromedia Shockwave resource archive"; } }
public override uint Signature { get { return 0x52494658; } } // 'XFIR'
public override bool IsHierarchic { get { return false; } }
public override bool CanWrite { get { return false; } }
public CctOpener ()
{
Extensions = new string[] { "cct", "dcr" };
}
public override ArcFile TryOpen (ArcView file)
{
uint id = file.View.ReadUInt32 (8);
if (id != 0x46474443 && id != 0x4647444D) // 'CDGF' || 'MDGF'
return null;
var reader = new CctReader (file);
var dir = reader.ReadIndex();
if (null != dir)
{
var arc = new ArcFile (file, this, dir);
SetEntryTypes (arc);
return arc;
}
return null;
}
public override Stream OpenEntry (ArcFile arc, Entry entry)
{
var input = arc.File.CreateStream (entry.Offset, entry.Size);
var packed_entry = entry as PackedEntry;
if (null == packed_entry || !packed_entry.IsPacked)
return input;
else
return new ZLibStream (input, CompressionMode.Decompress);
}
private void SetEntryTypes (ArcFile arc)
{
foreach (var entry in arc.Dir.OrderBy (x => x.Offset))
{
if (entry.Name.EndsWith (".edim"))
entry.Type = DetectEdimType (arc, entry);
else if (entry.Name.EndsWith (".bitd"))
entry.Type = "image";
}
}
private string DetectEdimType (ArcFile arc, Entry entry)
{
using (var input = OpenEntry (arc, entry))
{
uint signature = (uint)input.ReadByte() << 24;
signature |= (uint)input.ReadByte() << 16;
signature |= (uint)input.ReadByte() << 8;
signature |= (byte)input.ReadByte();
if (0xffd8ffe0 == signature)
return "image";
uint real_size = (entry as PackedEntry).UnpackedSize;
if (signature > 0xffff || signature+4 > real_size)
return "";
var header = new byte[signature+0x10];
if (header.Length != input.Read (header, 0, header.Length))
return "";
if (0xff == header[signature])
return "audio";
return "";
}
}
}
internal class CctReader
{
ArcView m_file;
long m_offset;
public CctReader (ArcView file)
{
m_file = file;
m_offset = 0x0C;
}
byte[] m_size_buffer = new byte[10];
public List<Entry> ReadIndex ()
{
uint section_size = ReadSectionSize ("Fver");
m_offset += section_size;
section_size = ReadSectionSize ("Fcdr");
/*
int Mcdr_size;
var Mcdr = ZlibUnpack (m_offset, section_size, out Mcdr_size);
*/
m_offset += section_size;
uint abmp_size = ReadSectionSize ("ABMP");
int max_count = m_file.View.Read (m_offset, m_size_buffer, 0, Math.Min (10, abmp_size));
int size_offset = 0;
ReadValue (m_size_buffer, ref size_offset, max_count);
max_count -= size_offset;
int bmp_unpacked_size = (int)ReadValue (m_size_buffer, ref size_offset, max_count);
m_offset += size_offset;
abmp_size -= (uint)size_offset;
int index_size;
var index = ZlibUnpack (m_offset, abmp_size, out index_size, bmp_unpacked_size);
m_offset += abmp_size;
section_size = ReadSectionSize ("FGEI");
if (0 != section_size)
throw new NotSupportedException();
int index_offset = 0;
ReadValue (index, ref index_offset, index_size-index_offset);
ReadValue (index, ref index_offset, index_size-index_offset);
int entry_count = (int)ReadValue (index, ref index_offset, index_size-index_offset);
if (entry_count <= 0 || entry_count > 0xfffff)
return null;
var type_buf = new char[4];
var dir = new List<Entry> (entry_count);
for (int i = 0; i < entry_count; ++i)
{
uint id = ReadValue (index, ref index_offset, index_size-index_offset);
uint offset = ReadValue (index, ref index_offset, index_size-index_offset);
uint size = ReadValue (index, ref index_offset, index_size-index_offset);
uint unpacked_size = ReadValue (index, ref index_offset, index_size-index_offset);
uint flag = ReadValue (index, ref index_offset, index_size-index_offset);
if (index_size-index_offset < 4)
return null;
uint type_id = LittleEndian.ToUInt32 (index, index_offset);
index_offset += 4;
if (0 == type_id || uint.MaxValue == offset)
continue;
Encoding.ASCII.GetChars (index, index_offset-4, 4, type_buf, 0);
var entry = new PackedEntry
{
Name = CreateName (id, type_buf),
Offset = (long)m_offset + offset,
Size = size,
UnpackedSize = unpacked_size,
IsPacked = 0 == flag,
};
if (entry.CheckPlacement (m_file.MaxOffset))
{
dir.Add (entry);
}
}
return dir;
}
string CreateName (uint id, char[] type_buf)
{
Array.Reverse (type_buf);
int t = 3;
while (t >= 0 && ' ' == type_buf[t])
t--;
if (t >= 0)
{
string ext = new string (type_buf, 0, t+1);
return string.Format ("{0:D8}.{1}", id, ext.ToLowerInvariant());
}
else
return id.ToString ("D8");
}
byte[] ZlibUnpack (long offset, uint size, out int actual_size, int unpacked_size_hint = 0)
{
using (var input = m_file.CreateStream (offset, size))
using (var zstream = new ZLibStream (input, CompressionMode.Decompress))
using (var mem = new MemoryStream (unpacked_size_hint))
{
zstream.CopyTo (mem);
actual_size = (int)mem.Length;
return mem.GetBuffer();
}
}
uint ReadSectionSize (string id_str)
{
uint id = ConvertId (id_str);
if (id != m_file.View.ReadUInt32 (m_offset))
throw new InvalidFormatException();
m_offset += 4;
if (5 != m_file.View.Read (m_offset, m_size_buffer, 0, 5))
throw new InvalidFormatException();
int off_count = 0;
uint size = ReadValue (m_size_buffer, ref off_count, 5);
m_offset += off_count;
return size;
}
static uint ReadValue (byte[] buffer, ref int offset, int length)
{
uint n = 0;
for (int off_count = 0; off_count < length; ++off_count)
{
uint bits = buffer[offset++];
n = (n << 7) | (bits & 0x7F);
if (0 == (bits & 0x80))
return n;
}
throw new InvalidFormatException();
}
static uint ConvertId (string id)
{
if (id.Length != 4)
throw new ArgumentException ("Invalid section id");
uint n = 0;
for (int i = 0; i < 4; ++i)
n = (n << 8) | (byte)id[i];
return n;
}
}
}

View File

@ -38,46 +38,58 @@ namespace GameRes.Formats.Macromedia
[Export(typeof(ArchiveFormat))] [Export(typeof(ArchiveFormat))]
public class DxrOpener : ArchiveFormat public class DxrOpener : ArchiveFormat
{ {
public override string Tag { get => "DXR"; } public override string Tag => "DXR";
public override string Description { get => "Macromedia Director resource archive"; } public override string Description => "Macromedia Director resource archive";
public override uint Signature { get => 0x52494658; } // 'XFIR' public override uint Signature => SignatureXFIR; // 'XFIR'
public override bool IsHierarchic { get => false; } public override bool IsHierarchic => false;
public override bool CanWrite { get => false; } public override bool CanWrite => false;
public const uint SignatureXFIR = 0x52494658u;
public const uint SignatureRIFX = 0x58464952u;
public DxrOpener () public DxrOpener ()
{ {
Extensions = new[] { "dxr", "cxt", "cct", "dcr" }; Extensions = new[] { "dxr", "cxt", "cct", "dcr", "exe" };
Signatures = new[] { 0x52494658u, 0x58464952u }; Signatures = new[] { SignatureXFIR, SignatureRIFX, 0x00905A4Du, 0u };
} }
internal static readonly HashSet<string> RawChunks = new HashSet<string> { internal static readonly HashSet<string> RawChunks = new HashSet<string> {
"RTE0", "RTE1", "FXmp", "VWFI", "VWSC", "Lscr", "STXT", "XMED", //"snd " "RTE0", "RTE1", "FXmp", "VWFI", "VWSC", "Lscr", "STXT", "XMED", "File"
}; };
internal bool ConvertText = true; internal bool ConvertText = true;
public override ArcFile TryOpen (ArcView file) public override ArcFile TryOpen (ArcView file)
{ {
long base_offset = 0;
if (file.View.AsciiEqual (0, "MZ"))
base_offset = LookForXfir (file);
uint signature = file.View.ReadUInt32 (base_offset);
if (signature != SignatureXFIR && signature != SignatureRIFX)
return null;
using (var input = file.CreateStream()) using (var input = file.CreateStream())
{ {
ByteOrder ord = input.Signature == 0x52494658u ? ByteOrder.LittleEndian : ByteOrder.BigEndian; ByteOrder ord = signature == SignatureXFIR ? ByteOrder.LittleEndian : ByteOrder.BigEndian;
var reader = new Reader (input, ord); var reader = new Reader (input, ord);
reader.Position = 4; reader.Position = base_offset;
uint length = reader.ReadU32();
var context = new SerializationContext(); var context = new SerializationContext();
var dir_file = new DirectorFile(); var dir_file = new DirectorFile();
if (!dir_file.Deserialize (context, reader)) if (!dir_file.Deserialize (context, reader))
return null; return null;
var dir = new List<Entry> (); var dir = new List<Entry> ();
ImportMedia (dir_file, dir); if (dir_file.Codec != "APPL")
ImportMedia (dir_file, dir);
foreach (DirectorEntry entry in dir_file.Directory) foreach (DirectorEntry entry in dir_file.Directory)
{ {
if (entry.Size != 0 && entry.Offset != -1 && RawChunks.Contains (entry.FourCC)) if (entry.Size != 0 && entry.Offset != -1 && RawChunks.Contains (entry.FourCC))
{ {
entry.Name = string.Format ("{0:D6}.{1}", entry.Id, entry.FourCC.Trim()); entry.Name = string.Format ("{0:D6}.{1}", entry.Id, entry.FourCC.Trim());
if ("snd " == entry.FourCC) if ("File" == entry.FourCC)
entry.Type = "audio"; {
entry.Offset -= 8;
entry.Size += 8;
}
dir.Add (entry); dir.Add (entry);
} }
} }
@ -155,7 +167,7 @@ namespace GameRes.Formats.Macromedia
entry = ImportBitmap (piece, dir_file, cast); entry = ImportBitmap (piece, dir_file, cast);
else if (piece.Type == DataType.Sound) else if (piece.Type == DataType.Sound)
entry = ImportSound (piece, dir_file); entry = ImportSound (piece, dir_file);
if (entry != null) if (entry != null && entry.Size > 0)
dir.Add (entry); dir.Add (entry);
} }
} }
@ -381,6 +393,56 @@ namespace GameRes.Formats.Macromedia
zstream.Read (data, 0, data.Length); zstream.Read (data, 0, data.Length);
return new BinMemoryStream (data, entry.Name); return new BinMemoryStream (data, entry.Name);
} }
static readonly byte[] s_xfir = { (byte)'X', (byte)'F', (byte)'I', (byte)'R' };
long LookForXfir (ArcView file)
{
var exe = new ExeFile (file);
long pos;
if (exe.IsWin16)
{
pos = exe.FindString (exe.Overlay, s_xfir);
if (pos < 0)
return 0;
}
else
{
pos = exe.Overlay.Offset;
if (pos >= file.MaxOffset)
return 0;
if (file.View.AsciiEqual (pos, "10JP"))
{
pos = file.View.ReadUInt32 (pos+4);
}
}
if (pos >= file.MaxOffset || !file.View.AsciiEqual (pos, "XFIR"))
return 0;
// TODO threat 'LPPA' archives the normal way, like archives that contain entries.
// the problem is, DXR archives contained within 'LPPA' have their offsets relative to executable file,
// so have to figure out way to handle it.
if (!file.View.AsciiEqual (pos+8, "LPPA"))
return pos;
var appl = new DirectorFile();
var context = new SerializationContext();
using (var input = file.CreateStream())
{
var reader = new Reader (input, ByteOrder.LittleEndian);
input.Position = pos + 12;
if (!appl.ReadMMap (context, reader))
return 0;
foreach (var entry in appl.Directory)
{
// only the first XFIR entry is matched here, but archive may contain multiple sub-archives.
if (entry.FourCC == "File")
{
if (file.View.AsciiEqual (entry.Offset-8, "XFIR"))
return entry.Offset-8;
}
}
return 0;
}
}
} }
internal class BitmapEntry : PackedEntry internal class BitmapEntry : PackedEntry
@ -409,8 +471,11 @@ namespace GameRes.Formats.Macromedia
Right = reader.ReadI16(); Right = reader.ReadI16();
reader.Skip (0x0C); reader.Skip (0x0C);
BitDepth = reader.ReadU16() & 0xFF; // ??? BitDepth = reader.ReadU16() & 0xFF; // ???
reader.Skip (2); if (data.Length >= 0x1C)
Palette = reader.ReadI16(); {
reader.Skip (2);
Palette = reader.ReadI16();
}
} }
} }
} }

View File

@ -1,6 +1,6 @@
//! \file AudioEDIM.cs //! \file AudioEDIM.cs
//! \date Fri Jun 26 06:52:33 2015 //! \date Fri Jun 26 06:52:33 2015
//! \brief Selen wrapper around MP3 stream. //! \brief Macromedia Director wrapper around MP3 stream.
// //
// Copyright (C) 2015 by morkt // Copyright (C) 2015 by morkt
// //
@ -33,7 +33,7 @@ namespace GameRes.Formats.Selen
public class EdimAudio : Mp3Audio public class EdimAudio : Mp3Audio
{ {
public override string Tag { get { return "EDIM"; } } public override string Tag { get { return "EDIM"; } }
public override string Description { get { return "Selen audio format (MP3)"; } } public override string Description { get { return "Macromedia Director audio format (MP3)"; } }
public override uint Signature { get { return 0x40010000; } } public override uint Signature { get { return 0x40010000; } }
public override bool CanWrite { get { return false; } } public override bool CanWrite { get { return false; } }

View File

@ -75,6 +75,9 @@ namespace GameRes.Formats.Macromedia
DirectorConfig m_config = new DirectorConfig(); DirectorConfig m_config = new DirectorConfig();
List<Cast> m_casts = new List<Cast>(); List<Cast> m_casts = new List<Cast>();
Dictionary<int, byte[]> m_ilsMap = new Dictionary<int, byte[]>(); Dictionary<int, byte[]> m_ilsMap = new Dictionary<int, byte[]>();
string m_codec;
public string Codec => m_codec;
public bool IsAfterBurned { get; private set; } public bool IsAfterBurned { get; private set; }
@ -96,14 +99,14 @@ namespace GameRes.Formats.Macromedia
public bool Deserialize (SerializationContext context, Reader reader) public bool Deserialize (SerializationContext context, Reader reader)
{ {
reader.Position = 8; reader.Skip (8);
string codec = reader.ReadFourCC(); m_codec = reader.ReadFourCC();
if (codec == "MV93" || codec == "MC95") if (m_codec == "MV93" || m_codec == "MC95")
{ {
if (!ReadMMap (context, reader)) if (!ReadMMap (context, reader))
return false; return false;
} }
else if (codec == "FGDC" || codec == "FGDM") else if (m_codec == "FGDC" || m_codec == "FGDM")
{ {
IsAfterBurned = true; IsAfterBurned = true;
if (!ReadAfterBurner (context, reader)) if (!ReadAfterBurner (context, reader))
@ -111,7 +114,7 @@ namespace GameRes.Formats.Macromedia
} }
else else
{ {
Trace.WriteLine (string.Format ("Unknown codec '{0}'", codec), "DXR"); Trace.WriteLine (string.Format ("Unknown m_codec '{0}'", m_codec), "DXR");
return false; return false;
} }
return ReadKeyTable (context, reader) return ReadKeyTable (context, reader)
@ -119,7 +122,7 @@ namespace GameRes.Formats.Macromedia
&& ReadCasts (context, reader); && ReadCasts (context, reader);
} }
bool ReadMMap (SerializationContext context, Reader reader) internal bool ReadMMap (SerializationContext context, Reader reader)
{ {
if (reader.ReadFourCC() != "imap") if (reader.ReadFourCC() != "imap")
return false; return false;

View File

@ -71,7 +71,7 @@ namespace GameRes.Formats.Macromedia
: info.BPP == 8 ? PixelFormats.Indexed8 : info.BPP == 8 ? PixelFormats.Indexed8
: info.BPP == 16 ? PixelFormats.Bgr555 : info.BPP == 16 ? PixelFormats.Bgr555
: info.DepthType == 0x87 // i have no clue what this is : info.DepthType == 0x87 // i have no clue what this is
|| info.DepthType == 0x8A ? PixelFormats.Bgra32 // depth type 0x87/0x8A || info.DepthType == 0x8A ? PixelFormats.Bgra32 // depth type 0x87/0x8A
: PixelFormats.Bgra32; // depth type 0x82/84/85/86/8C : PixelFormats.Bgra32; // depth type 0x82/84/85/86/8C
m_palette = palette; m_palette = palette;
} }
@ -151,32 +151,7 @@ namespace GameRes.Formats.Macromedia
{ {
for (int line = 0; line < m_output.Length; line += m_stride) for (int line = 0; line < m_output.Length; line += m_stride)
{ {
int x = 0; UnpackScanLine (m_output, line);
while (x < m_stride)
{
int b = m_input.ReadByte();
if (-1 == b)
throw new InvalidFormatException ("Unexpected end of file");
int count = b;
if (b > 0x7f)
count = (byte)-(sbyte)b;
++count;
if (x + count > m_stride)
throw new InvalidFormatException();
if (b > 0x7f)
{
b = m_input.ReadByte();
if (-1 == b)
throw new InvalidFormatException ("Unexpected end of file");
for (int i = 0; i < count; ++i)
m_output[line + x++] = (byte)b;
}
else
{
m_input.Read (m_output, line + x, count);
x += count;
}
}
} }
return m_output; return m_output;
} }
@ -186,33 +161,7 @@ namespace GameRes.Formats.Macromedia
var scan_line = new byte[m_stride]; var scan_line = new byte[m_stride];
for (int line = 0; line < m_output.Length; line += m_stride) for (int line = 0; line < m_output.Length; line += m_stride)
{ {
int x = 0; UnpackScanLine (scan_line, 0);
while (x < m_stride)
{
int b = m_input.ReadByte();
if (-1 == b)
break; // one in 5000 images somehow stumbles here
// throw new InvalidFormatException ("Unexpected end of file");
int count = b;
if (b > 0x7f)
count = (byte)-(sbyte)b;
++count;
if (x + count > m_stride)
throw new InvalidFormatException();
if (b > 0x7f)
{
b = m_input.ReadByte();
if (-1 == b)
throw new InvalidFormatException ("Unexpected end of file");
for (int i = 0; i < count; ++i)
scan_line[x++] = (byte)b;
}
else
{
m_input.Read (scan_line, x, count);
x += count;
}
}
int dst = line; int dst = line;
for (int i = 0; i < m_width; ++i) for (int i = 0; i < m_width; ++i)
{ {
@ -222,6 +171,36 @@ namespace GameRes.Formats.Macromedia
} }
} }
void UnpackScanLine (byte[] scan_line, int pos)
{
int x = 0;
while (x < m_stride)
{
int b = m_input.ReadByte();
if (-1 == b)
break; // one in 5000 images somehow stumbles here
int count = b;
if (b > 0x7f)
count = (byte)-(sbyte)b;
++count;
if (x + count > m_stride)
throw new InvalidFormatException();
if (b > 0x7f)
{
b = m_input.ReadByte();
if (-1 == b)
throw new InvalidFormatException ("Unexpected end of file");
for (int i = 0; i < count; ++i)
scan_line[pos + x++] = (byte)b;
}
else
{
m_input.Read (scan_line, pos+x, count);
x += count;
}
}
}
#region IDisposable Members #region IDisposable Members
bool m_disposed = false; bool m_disposed = false;

View File

@ -111,7 +111,7 @@ namespace GameRes.Formats.RealLive
int offset = input.ReadUInt16(); int offset = input.ReadUInt16();
int count = (offset & 0xF) + 2; int count = (offset & 0xF) + 2;
offset >>= 4; offset >>= 4;
Binary.CopyOverlapped (output, dst-offset, dst, count); Binary.CopyOverlapped (output, dst-offset-1, dst, count);
dst += count; dst += count;
} }
} }

View File

@ -105,7 +105,7 @@ namespace GameRes.Formats.YuRis
public YpfOpener () public YpfOpener ()
{ {
Signatures = new uint[] { 0x00465059, 0 }; Signatures = new uint[] { 0x00465059, 0x00905A4D, 0 };
} }
static public Dictionary<string, YpfScheme> KnownSchemes { get { return DefaultScheme.KnownSchemes; } } static public Dictionary<string, YpfScheme> KnownSchemes { get { return DefaultScheme.KnownSchemes; } }

View File

@ -52,30 +52,37 @@ namespace GameRes.Formats.Zyx
public override ImageMetaData ReadMetaData (IBinaryStream file) public override ImageMetaData ReadMetaData (IBinaryStream file)
{ {
int count = file.ReadInt16(); int count = file.ReadInt16();
if (count <= 0) if (count < 0 || count > 0x100)
return null; return null;
var tiles = new Tile[count]; Tile[] tiles = null;
for (int i = 0; i < count; ++i) if (count > 0)
{ {
var tile = new Tile(); tiles = new Tile[count];
tile.Left = file.ReadInt16(); for (int i = 0; i < count; ++i)
tile.Top = file.ReadInt16(); {
if (tile.Left < 0 || tile.Top < 0) var tile = new Tile();
return null; tile.Left = file.ReadInt16();
tile.Right = file.ReadInt16(); tile.Top = file.ReadInt16();
tile.Bottom = file.ReadInt16(); if (tile.Left < 0 || tile.Top < 0)
if (tile.Right <= tile.Left || tile.Bottom <= tile.Top) return null;
return null; tile.Right = file.ReadInt16();
tiles[i] = tile; tile.Bottom = file.ReadInt16();
if (tile.Right <= tile.Left || tile.Bottom <= tile.Top)
return null;
tiles[i] = tile;
}
} }
int width = file.ReadInt16(); int width = file.ReadInt16();
int height = file.ReadInt16(); int height = file.ReadInt16();
if (width <= 0 || height <= 0) if (width <= 0 || height <= 0)
return null; return null;
foreach (var tile in tiles) if (tiles != null)
{ {
if (tile.Right > width || tile.Bottom > height) foreach (var tile in tiles)
return null; {
if (tile.Right > width || tile.Bottom > height)
return null;
}
} }
return new SplMetaData return new SplMetaData
{ {