mirror of
https://github.com/crskycode/GARbro.git
synced 2026-07-08 01:30:18 +08:00
bunch of stuff.
This commit is contained in:
@@ -32,13 +32,13 @@ using System.Text;
|
||||
using GameRes.Compression;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Selen
|
||||
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 Director resource archive"; } }
|
||||
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; } }
|
||||
|
||||
286
ArcFormats/Macromedia/ArcDXR.cs
Normal file
286
ArcFormats/Macromedia/ArcDXR.cs
Normal file
@@ -0,0 +1,286 @@
|
||||
//! \file ArcDXR.cs
|
||||
//! \date 2023 Aug 17
|
||||
//! \brief Macromedia Director presentation container.
|
||||
//
|
||||
// Copyright (C) 2023 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 GameRes.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace GameRes.Formats.Macromedia
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class DxrOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get => "DXR"; }
|
||||
public override string Description { get => "Macromedia Director resource archive"; }
|
||||
public override uint Signature { get => 0x52494658; } // 'XFIR'
|
||||
public override bool IsHierarchic { get => false; }
|
||||
public override bool CanWrite { get => false; }
|
||||
|
||||
public DxrOpener ()
|
||||
{
|
||||
Extensions = new[] { "dxr", "cxt" };
|
||||
Signatures = new[] { 0x52494658u, 0x58464952u };
|
||||
}
|
||||
|
||||
internal static readonly HashSet<string> RawChunks = new HashSet<string> {
|
||||
"RTE0", "RTE1", "FXmp", "VWFI", "VWSC", "Lscr", "STXT",
|
||||
};
|
||||
|
||||
internal bool ConvertText = true;
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
using (var input = file.CreateStream())
|
||||
{
|
||||
ByteOrder ord = input.Signature == 0x52494658u ? ByteOrder.LittleEndian : ByteOrder.BigEndian;
|
||||
var reader = new Reader (input, ord);
|
||||
reader.Position = 4;
|
||||
uint length = reader.ReadU32();
|
||||
var context = new SerializationContext();
|
||||
var dir_file = new DirectorFile();
|
||||
if (!dir_file.Deserialize (context, reader))
|
||||
return null;
|
||||
|
||||
var dir = new List<Entry> ();
|
||||
ImportMedia (dir_file, dir);
|
||||
foreach (MemoryMapEntry entry in dir_file.MMap.Dir)
|
||||
{
|
||||
if (RawChunks.Contains (entry.FourCC))
|
||||
{
|
||||
entry.Name = string.Format ("{0:D6}.{1}", entry.Id, entry.FourCC.Trim());
|
||||
dir.Add (entry);
|
||||
}
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var snd = entry as SoundEntry;
|
||||
if (snd != null)
|
||||
return OpenSound (arc, snd);
|
||||
var ment = entry as MemoryMapEntry;
|
||||
if (!ConvertText || null == ment || ment.FourCC != "STXT")
|
||||
return base.OpenEntry (arc, entry);
|
||||
uint offset = Binary.BigEndian (arc.File.View.ReadUInt32 (entry.Offset));
|
||||
uint length = Binary.BigEndian (arc.File.View.ReadUInt32 (entry.Offset + 4));
|
||||
return arc.File.CreateStream (entry.Offset + offset, length);
|
||||
}
|
||||
|
||||
internal Stream OpenSound (ArcFile arc, SoundEntry entry)
|
||||
{
|
||||
var header = arc.File.View.ReadBytes (entry.Header.Offset, entry.Header.Size);
|
||||
var format = entry.DeserializeHeader (header);
|
||||
var riff = new MemoryStream (0x2C);
|
||||
WaveAudio.WriteRiffHeader (riff, format, entry.Size);
|
||||
if (format.BitsPerSample < 16)
|
||||
{
|
||||
using (riff)
|
||||
{
|
||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
return new PrefixStream (riff.ToArray(), input);
|
||||
}
|
||||
}
|
||||
// samples are stored in big-endian format
|
||||
var samples = arc.File.View.ReadBytes (entry.Offset, entry.Size);
|
||||
for (int i = 1; i < samples.Length; i += 2)
|
||||
{
|
||||
byte s = samples[i-1];
|
||||
samples[i-1] = samples[i];
|
||||
samples[i] = s;
|
||||
}
|
||||
riff.Write (samples, 0, samples.Length);
|
||||
riff.Position = 0;
|
||||
return riff;
|
||||
}
|
||||
|
||||
void ImportMedia (DirectorFile dir_file, List<Entry> dir)
|
||||
{
|
||||
var seen_ids = new HashSet<int>();
|
||||
var mmap = dir_file.MMap;
|
||||
foreach (var cast in dir_file.Casts)
|
||||
{
|
||||
foreach (var piece in cast.Members.Values)
|
||||
{
|
||||
if (seen_ids.Contains (piece.Id))
|
||||
continue;
|
||||
seen_ids.Add (piece.Id);
|
||||
Entry entry = null;
|
||||
if (piece.Type == DataType.Bitmap)
|
||||
entry = ImportBitmap (piece, dir_file, cast);
|
||||
else if (piece.Type == DataType.Sound)
|
||||
entry = ImportSound (piece, dir_file);
|
||||
if (entry != null)
|
||||
dir.Add (entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Entry ImportSound (CastMember sound, DirectorFile dir_file)
|
||||
{
|
||||
var sndHrec = dir_file.KeyTable.FindByCast (sound.Id, "sndH");
|
||||
var sndSrec = dir_file.KeyTable.FindByCast (sound.Id, "sndS");
|
||||
if (sndHrec == null || sndSrec == null)
|
||||
return null;
|
||||
var name = sound.Info.Name;
|
||||
var sndH = dir_file.MMap[sndHrec.Id];
|
||||
var sndS = dir_file.MMap[sndSrec.Id];
|
||||
if (string.IsNullOrEmpty (name))
|
||||
name = sndSrec.Id.ToString ("D6");
|
||||
return new SoundEntry
|
||||
{
|
||||
Name = name + ".snd",
|
||||
Type = "audio",
|
||||
Offset = sndS.Offset,
|
||||
Size = sndS.Size,
|
||||
Header = sndH,
|
||||
};
|
||||
}
|
||||
|
||||
Entry ImportBitmap (CastMember bitmap, DirectorFile dir_file, Cast cast)
|
||||
{
|
||||
var bitd = dir_file.KeyTable.FindByCast (bitmap.Id, "BITD");
|
||||
if (bitd == null)
|
||||
return null;
|
||||
var entry = new BitmapEntry();
|
||||
entry.DeserializeHeader (bitmap.SpecificData);
|
||||
var name = bitmap.Info.Name;
|
||||
if (string.IsNullOrEmpty (name))
|
||||
name = bitd.Id.ToString ("D6");
|
||||
var chunk = dir_file.MMap[bitd.Id];
|
||||
entry.Name = name + ".BITD";
|
||||
entry.Type = "image";
|
||||
entry.Offset = chunk.Offset;
|
||||
entry.Size = chunk.Size;
|
||||
if (entry.Palette > 0)
|
||||
{
|
||||
var cast_id = cast.Index[entry.Palette-1];
|
||||
var clut = dir_file.KeyTable.FindByCast (cast_id, "CLUT");
|
||||
if (clut != null)
|
||||
entry.PaletteRef = dir_file.MMap[clut.Id];
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
|
||||
{
|
||||
var bent = entry as BitmapEntry;
|
||||
if (null == bent)
|
||||
return base.OpenImage (arc, entry);
|
||||
BitmapPalette palette = null;
|
||||
if (bent.PaletteRef != null)
|
||||
{
|
||||
var pal_bytes = arc.File.View.ReadBytes (bent.PaletteRef.Offset, bent.PaletteRef.Size);
|
||||
palette = ReadPalette (pal_bytes);
|
||||
}
|
||||
else if (bent.BitDepth <= 8)
|
||||
{
|
||||
switch (bent.Palette)
|
||||
{
|
||||
case 0: palette = Palettes.SystemMac; break;
|
||||
case -1: palette = Palettes.Rainbow; break;
|
||||
case -2: palette = Palettes.Grayscale; break;
|
||||
case -100: palette = Palettes.WindowsDirector4; break;
|
||||
default:
|
||||
case -101: palette = Palettes.SystemWindows; break;
|
||||
}
|
||||
}
|
||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
var info = new ImageMetaData {
|
||||
Width = (uint)(bent.Right - bent.Left),
|
||||
Height = (uint)(bent.Bottom - bent.Top),
|
||||
BPP = bent.BitDepth
|
||||
};
|
||||
return new BitdDecoder (input.AsStream, info, palette);
|
||||
}
|
||||
|
||||
BitmapPalette ReadPalette (byte[] data)
|
||||
{
|
||||
int num_colors = data.Length / 6;
|
||||
var colors = new Color[num_colors];
|
||||
for (int i = 0; i < data.Length; i += 6)
|
||||
{
|
||||
colors[i/6] = Color.FromRgb (data[i], data[i+2], data[i+4]);
|
||||
}
|
||||
return new BitmapPalette (colors);
|
||||
}
|
||||
}
|
||||
|
||||
internal class BitmapEntry : Entry
|
||||
{
|
||||
public byte Flags;
|
||||
public byte DepthType;
|
||||
public int Top;
|
||||
public int Left;
|
||||
public int Bottom;
|
||||
public int Right;
|
||||
public int BitDepth;
|
||||
public int Palette;
|
||||
public Entry PaletteRef;
|
||||
|
||||
public void DeserializeHeader (byte[] data)
|
||||
{
|
||||
using (var input = new MemoryStream (data, false))
|
||||
{
|
||||
var reader = new Reader (input, ByteOrder.BigEndian);
|
||||
DepthType = reader.ReadU8();
|
||||
Flags = reader.ReadU8();
|
||||
Top = reader.ReadI16();
|
||||
Left = reader.ReadI16();
|
||||
Bottom = reader.ReadI16();
|
||||
Right = reader.ReadI16();
|
||||
reader.Skip (0x0C);
|
||||
BitDepth = reader.ReadU16() & 0xFF; // ???
|
||||
reader.Skip (2);
|
||||
Palette = reader.ReadI16();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class SoundEntry : Entry
|
||||
{
|
||||
public Entry Header;
|
||||
|
||||
public WaveFormat DeserializeHeader (byte[] header)
|
||||
{
|
||||
// pure guesswork
|
||||
return new WaveFormat {
|
||||
FormatTag = 1,
|
||||
Channels = (ushort)BigEndian.ToUInt32 (header, 0x4C),
|
||||
SamplesPerSecond = BigEndian.ToUInt32 (header, 0x2C),
|
||||
AverageBytesPerSecond = BigEndian.ToUInt32 (header, 0x30),
|
||||
BlockAlign = (ushort)BigEndian.ToUInt32 (header, 0x50),
|
||||
BitsPerSample = (ushort)BigEndian.ToUInt32 (header, 0x44),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
712
ArcFormats/Macromedia/DirectorFile.cs
Normal file
712
ArcFormats/Macromedia/DirectorFile.cs
Normal file
@@ -0,0 +1,712 @@
|
||||
//! \file DirectorFile.cs
|
||||
//! \date 2023 Aug 21
|
||||
//! \brief Macromedia Director container deserialization.
|
||||
//
|
||||
// Copyright (C) 2023 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 GameRes.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace GameRes.Formats.Macromedia
|
||||
{
|
||||
internal enum ByteOrder
|
||||
{
|
||||
LittleEndian, BigEndian
|
||||
}
|
||||
|
||||
internal enum DataType
|
||||
{
|
||||
Null = 0,
|
||||
Bitmap = 1,
|
||||
FilmLoop = 2,
|
||||
Text = 3,
|
||||
Palette = 4,
|
||||
Picture = 5,
|
||||
Sound = 6,
|
||||
Button = 7,
|
||||
Shape = 8,
|
||||
Movie = 9,
|
||||
DigitalVideo = 10,
|
||||
Script = 11,
|
||||
RTE = 12,
|
||||
}
|
||||
|
||||
internal class SerializationContext
|
||||
{
|
||||
public int Version;
|
||||
public Encoding Encoding;
|
||||
|
||||
public SerializationContext ()
|
||||
{
|
||||
Encoding = Encodings.cp932;
|
||||
}
|
||||
}
|
||||
|
||||
internal class DirectorFile
|
||||
{
|
||||
MemoryMap m_mmap = new MemoryMap();
|
||||
KeyTable m_keyTable = new KeyTable();
|
||||
DirectorConfig m_config = new DirectorConfig();
|
||||
List<Cast> m_casts = new List<Cast>();
|
||||
|
||||
public MemoryMap MMap => m_mmap;
|
||||
public KeyTable KeyTable => m_keyTable;
|
||||
public DirectorConfig Config => m_config;
|
||||
public List<Cast> Casts => m_casts;
|
||||
|
||||
public bool Deserialize (SerializationContext context, Reader reader)
|
||||
{
|
||||
reader.Position = 8;
|
||||
string codec = reader.ReadFourCC();
|
||||
if (codec != "MV93" && codec != "MC95")
|
||||
return false;
|
||||
return ReadMMap (context, reader)
|
||||
&& ReadKeyTable (context, reader)
|
||||
&& ReadConfig (context, reader)
|
||||
&& ReadCasts (context, reader);
|
||||
}
|
||||
|
||||
bool ReadMMap (SerializationContext context, Reader reader)
|
||||
{
|
||||
if (reader.ReadFourCC() != "imap")
|
||||
return false;
|
||||
reader.Skip (8);
|
||||
uint mmap_pos = reader.ReadU32();
|
||||
reader.Position = mmap_pos;
|
||||
if (reader.ReadFourCC() != "mmap")
|
||||
return false;
|
||||
reader.Position = mmap_pos + 8;
|
||||
MMap.Deserialize (context, reader);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadKeyTable (SerializationContext context, Reader reader)
|
||||
{
|
||||
var key_chunk = MMap.Find ("KEY*");
|
||||
if (null == key_chunk)
|
||||
return false;
|
||||
reader.Position = key_chunk.Offset;
|
||||
KeyTable.Deserialize (context, reader);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadConfig (SerializationContext context, Reader reader)
|
||||
{
|
||||
var config_chunk = MMap.Find ("VWCF") ?? MMap.Find ("DRCF");
|
||||
if (null == config_chunk)
|
||||
return false;
|
||||
reader.Position = config_chunk.Offset;
|
||||
Config.Deserialize (context, reader);
|
||||
context.Version = Config.Version;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadCasts (SerializationContext context, Reader reader)
|
||||
{
|
||||
if (context.Version > 1200)
|
||||
{
|
||||
var mcsl = MMap.Find ("MCsL");
|
||||
if (mcsl != null)
|
||||
{
|
||||
reader.Position = mcsl.Offset;
|
||||
var cast_list = new CastList();
|
||||
cast_list.Deserialize (context, reader);
|
||||
foreach (var entry in cast_list.Entries)
|
||||
{
|
||||
var key_entry = KeyTable.FindByCast (entry.Id, "CAS*");
|
||||
if (key_entry != null)
|
||||
{
|
||||
var mmap_entry = MMap[key_entry.Id];
|
||||
var cast = new Cast (context, reader, mmap_entry);
|
||||
if (!PopulateCast (cast, context, reader, entry))
|
||||
return false;
|
||||
Casts.Add (cast);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
var cas_chunk = MMap.Find ("CAS*");
|
||||
if (null == cas_chunk)
|
||||
return false;
|
||||
var new_entry = new CastListEntry { Name = "internal", Id = 0x400, MinMember = Config.MinMember };
|
||||
var new_cast = new Cast (context, reader, cas_chunk);
|
||||
if (!PopulateCast (new_cast, context, reader, new_entry))
|
||||
return false;
|
||||
Casts.Add (new_cast);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool PopulateCast (Cast cast, SerializationContext context, Reader reader, CastListEntry entry)
|
||||
{
|
||||
cast.Name = entry.Name;
|
||||
/*
|
||||
var lctx_ref = KeyTable.Table.Find (e => e.CastId == entry.Id && (e.FourCC == "Lctx" || e.FourCC == "LctX"));
|
||||
MemoryMapEntry lctx_chunk = null;
|
||||
if (lctx_ref != null)
|
||||
lctx_chunk = MMap[lctx_ref.Id];
|
||||
else
|
||||
lctx_chunk = MMap.Dir.Find (e => e.FourCC == "Lctx" || e.FourCC == "LctX");
|
||||
if (null == lctx_chunk)
|
||||
return false;
|
||||
reader.Position = lctx_chunk.Offset;
|
||||
var lctx = new ScriptContext();
|
||||
lctx.Deserialize (context, reader);
|
||||
cast.Context = lctx;
|
||||
*/
|
||||
for (int i = 0; i < cast.Index.Length; ++i)
|
||||
{
|
||||
int chunk_id = cast.Index[i];
|
||||
if (chunk_id > 0)
|
||||
{
|
||||
var chunk = MMap[chunk_id];
|
||||
var member = new CastMember();
|
||||
member.Id = chunk_id;
|
||||
reader.Position = chunk.Offset;
|
||||
member.Deserialize (context, reader);
|
||||
cast.Members[member.Id] = member;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal class CastMember
|
||||
{
|
||||
public DataType Type;
|
||||
public CastInfo Info = new CastInfo();
|
||||
public byte[] SpecificData;
|
||||
public byte Flags;
|
||||
public int Id;
|
||||
|
||||
public void Deserialize (SerializationContext context, Reader reader)
|
||||
{
|
||||
reader = reader.CloneUnless (ByteOrder.BigEndian);
|
||||
if (context.Version > 1200)
|
||||
{
|
||||
Type = (DataType)reader.ReadI32();
|
||||
int info_length = reader.ReadI32();
|
||||
int data_length = reader.ReadI32();
|
||||
if (info_length > 0)
|
||||
{
|
||||
Info.Deserialize (context, reader);
|
||||
}
|
||||
SpecificData = reader.ReadBytes (data_length);
|
||||
}
|
||||
else
|
||||
{
|
||||
int data_length = reader.ReadU16();
|
||||
int info_length = reader.ReadI32();
|
||||
Type = (DataType)reader.ReadU8();
|
||||
--data_length;
|
||||
if (data_length > 0)
|
||||
{
|
||||
Flags = reader.ReadU8();
|
||||
--data_length;
|
||||
}
|
||||
SpecificData = reader.ReadBytes (data_length);
|
||||
if (info_length > 0)
|
||||
{
|
||||
Info.Deserialize (context, reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class CastInfo
|
||||
{
|
||||
public uint DataOffset;
|
||||
public uint ScriptKey;
|
||||
public uint Flags;
|
||||
public int ScriptId;
|
||||
public string Name;
|
||||
public string SourceText;
|
||||
public List<byte[]> Items = new List<byte[]>();
|
||||
|
||||
public void Deserialize (SerializationContext context, Reader reader)
|
||||
{
|
||||
long base_offset = reader.Position;
|
||||
DataOffset = reader.ReadU32();
|
||||
ScriptKey = reader.ReadU32();
|
||||
reader.Skip (4);
|
||||
Flags = reader.ReadU32();
|
||||
ScriptId = reader.ReadI32();
|
||||
reader.Position = base_offset + DataOffset;
|
||||
int table_len = reader.ReadU16();
|
||||
var offsets = new int[table_len];
|
||||
for (int i = 0; i < table_len; ++i)
|
||||
offsets[i] = reader.ReadI32();
|
||||
|
||||
int data_length = reader.ReadI32();
|
||||
long list_offset = reader.Position;
|
||||
Items.Clear();
|
||||
Items.Capacity = offsets.Length;
|
||||
for (int i = 0; i < offsets.Length; ++i)
|
||||
{
|
||||
int offset = offsets[i];
|
||||
int next_offset = (i + 1 < offsets.Length) ? offsets[i+1] : data_length;
|
||||
reader.Position = list_offset + offset;
|
||||
Items.Add (reader.ReadBytes (next_offset - offset));
|
||||
}
|
||||
|
||||
SourceText = Items.Count > 0 ? Binary.GetCString (Items[0], 0) : string.Empty;
|
||||
Name = GetString (1, context.Encoding);
|
||||
}
|
||||
|
||||
string GetString (int item_idx, Encoding enc)
|
||||
{
|
||||
if (item_idx >= Items.Count)
|
||||
return string.Empty;
|
||||
var src = Items[item_idx];
|
||||
if (src.Length <= 1 || 0 == src[0])
|
||||
return string.Empty;
|
||||
int len = src[0];
|
||||
return enc.GetString (src, 1, len);
|
||||
}
|
||||
}
|
||||
|
||||
internal class Cast
|
||||
{
|
||||
public int[] Index;
|
||||
public string Name;
|
||||
public Dictionary<int, CastMember> Members = new Dictionary<int, CastMember>();
|
||||
|
||||
public Cast (SerializationContext context, Reader reader, MemoryMapEntry entry)
|
||||
{
|
||||
int count = (int)(entry.Size / 4);
|
||||
Index = new int[count];
|
||||
reader.Position = entry.Offset;
|
||||
Deserialize (context, reader);
|
||||
}
|
||||
|
||||
public void Deserialize (SerializationContext context, Reader reader)
|
||||
{
|
||||
reader = reader.CloneUnless (ByteOrder.BigEndian);
|
||||
for (int i = 0; i < Index.Length; ++i)
|
||||
Index[i] = reader.ReadI32();
|
||||
}
|
||||
}
|
||||
|
||||
internal class CastList
|
||||
{
|
||||
public uint DataOffset;
|
||||
public int OffsetCount;
|
||||
public int[] OffsetTable;
|
||||
public int ItemsLength;
|
||||
public int CastCount;
|
||||
public int ItemsPerCast;
|
||||
public List<byte[]> Items = new List<byte[]>();
|
||||
|
||||
public readonly List<CastListEntry> Entries = new List<CastListEntry>();
|
||||
|
||||
public void Deserialize (SerializationContext context, Reader reader)
|
||||
{
|
||||
long base_offset = reader.Position;
|
||||
reader = reader.CloneUnless (ByteOrder.BigEndian);
|
||||
DataOffset = reader.ReadU32();
|
||||
reader.Skip (2);
|
||||
CastCount = reader.ReadU16();
|
||||
ItemsPerCast = reader.ReadU16();
|
||||
reader.Skip (2);
|
||||
reader.Position = base_offset + DataOffset;
|
||||
OffsetCount = reader.ReadU16();
|
||||
OffsetTable = new int[OffsetCount];
|
||||
for (int i = 0; i < OffsetCount; ++i)
|
||||
{
|
||||
OffsetTable[i] = reader.ReadI32();
|
||||
}
|
||||
ItemsLength = reader.ReadI32();
|
||||
long items_offset = reader.Position;
|
||||
Items.Clear();
|
||||
Items.Capacity = OffsetCount;
|
||||
for (int i = 0; i < OffsetCount; ++i)
|
||||
{
|
||||
int offset = OffsetTable[i];
|
||||
int next_offset = (i + 1 < OffsetCount) ? OffsetTable[i + 1] : ItemsLength;
|
||||
int item_size = next_offset - offset;
|
||||
Items.Add (reader.ReadBytes (item_size));
|
||||
}
|
||||
|
||||
Entries.Clear();
|
||||
Entries.Capacity = CastCount;
|
||||
int item_idx = 0;
|
||||
for (int i = 0; i < CastCount; ++i)
|
||||
{
|
||||
var entry = new CastListEntry();
|
||||
if (ItemsPerCast >= 1)
|
||||
entry.Name = GetString (item_idx + 1, context.Encoding);
|
||||
if (ItemsPerCast >= 2)
|
||||
entry.Path = GetString (item_idx + 2, context.Encoding);
|
||||
if (ItemsPerCast >= 3 && Items[item_idx + 3].Length >= 2)
|
||||
entry.Flags = BigEndian.ToUInt16 (Items[item_idx + 3], 0);
|
||||
if (ItemsPerCast >= 4 && Items[item_idx + 4].Length >= 8)
|
||||
{
|
||||
entry.MinMember = BigEndian.ToUInt16 (Items[item_idx + 4], 0);
|
||||
entry.MaxMember = BigEndian.ToUInt16 (Items[item_idx + 4], 2);
|
||||
entry.Id = BigEndian.ToInt32 (Items[item_idx + 4], 4);
|
||||
}
|
||||
Entries.Add (entry);
|
||||
}
|
||||
}
|
||||
|
||||
string GetString (int item_idx, Encoding enc)
|
||||
{
|
||||
var src = Items[item_idx];
|
||||
if (src.Length <= 1 || 0 == src[0])
|
||||
return string.Empty;
|
||||
int len = src[0];
|
||||
return enc.GetString (src, 1, len);
|
||||
}
|
||||
}
|
||||
|
||||
internal class CastListEntry
|
||||
{
|
||||
public string Name;
|
||||
public string Path;
|
||||
public ushort Flags;
|
||||
public int MinMember;
|
||||
public int MaxMember;
|
||||
public int Id;
|
||||
}
|
||||
|
||||
internal class ScriptContext
|
||||
{
|
||||
public int EntriesOffset;
|
||||
public int LnamChunkId;
|
||||
public int ValidCount;
|
||||
public ushort Flags;
|
||||
public short FreePtr;
|
||||
public List<ScriptContextMap> ChunkMap = new List<ScriptContextMap>();
|
||||
|
||||
public void Deserialize (SerializationContext context, Reader reader)
|
||||
{
|
||||
long base_offset = reader.Position;
|
||||
reader = reader.CloneUnless (ByteOrder.BigEndian);
|
||||
reader.Skip (8);
|
||||
int count = reader.ReadI32();
|
||||
reader.Skip (4);
|
||||
EntriesOffset = reader.ReadU16();
|
||||
reader.Skip (14);
|
||||
LnamChunkId = reader.ReadI32();
|
||||
ValidCount = reader.ReadU16();
|
||||
Flags = reader.ReadU16();
|
||||
FreePtr = reader.ReadI16();
|
||||
reader.Position = base_offset + EntriesOffset;
|
||||
|
||||
ChunkMap.Clear();
|
||||
ChunkMap.Capacity = count;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var entry = new ScriptContextMap();
|
||||
entry.Deserialize (context, reader);
|
||||
ChunkMap.Add (entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class ScriptContextMap
|
||||
{
|
||||
public int Key;
|
||||
public int ChunkId;
|
||||
|
||||
public void Deserialize (SerializationContext context, Reader reader)
|
||||
{
|
||||
Key = reader.ReadI32();
|
||||
ChunkId = reader.ReadI32();
|
||||
reader.Skip (4);
|
||||
}
|
||||
}
|
||||
|
||||
internal class DirectorConfig
|
||||
{
|
||||
public short Length;
|
||||
public short FileVersion;
|
||||
public short StageTop;
|
||||
public short StageLeft;
|
||||
public short StageBottom;
|
||||
public short StageRight;
|
||||
public short MinMember;
|
||||
public short MaxMember;
|
||||
public ushort StageColor;
|
||||
public ushort BitDepth;
|
||||
public int Version;
|
||||
public int FrameRate;
|
||||
public int Platform;
|
||||
public int Protection;
|
||||
public uint CheckSum;
|
||||
public int DefaultPalette;
|
||||
|
||||
public void Deserialize (SerializationContext context, Reader reader)
|
||||
{
|
||||
long base_offset = reader.Position;
|
||||
reader = reader.CloneUnless (ByteOrder.BigEndian);
|
||||
|
||||
reader.Position = base_offset + 0x24;
|
||||
Version = reader.ReadU16();
|
||||
reader.Position = base_offset;
|
||||
Length = reader.ReadI16();
|
||||
FileVersion = reader.ReadI16();
|
||||
StageTop = reader.ReadI16();
|
||||
StageLeft = reader.ReadI16();
|
||||
StageBottom = reader.ReadI16();
|
||||
StageRight = reader.ReadI16();
|
||||
MinMember = reader.ReadI16();
|
||||
MaxMember = reader.ReadI16();
|
||||
reader.Skip (0x0A);
|
||||
StageColor = reader.ReadU16();
|
||||
BitDepth = reader.ReadU16();
|
||||
reader.Skip (0x18);
|
||||
FrameRate = reader.ReadU16();
|
||||
Platform = reader.ReadI16();
|
||||
Protection = reader.ReadI16();
|
||||
reader.Skip (4);
|
||||
CheckSum = reader.ReadU32();
|
||||
if (Version > 1200)
|
||||
{
|
||||
reader.Position = base_offset + 0x4E;
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.Position = base_offset + 0x46;
|
||||
}
|
||||
DefaultPalette = reader.ReadU16();
|
||||
}
|
||||
}
|
||||
|
||||
internal class KeyTable
|
||||
{
|
||||
public int EntrySize;
|
||||
public int TotalCount;
|
||||
public int UsedCount;
|
||||
public readonly List<KeyTableEntry> Table = new List<KeyTableEntry>();
|
||||
|
||||
public KeyTableEntry this[int index] => Table[index];
|
||||
|
||||
public KeyTableEntry FindByCast (int cast_id, string four_cc)
|
||||
{
|
||||
return Table.Find (e => e.CastId == cast_id && e.FourCC == four_cc);
|
||||
}
|
||||
|
||||
public void Deserialize (SerializationContext context, Reader reader)
|
||||
{
|
||||
EntrySize = reader.ReadU16();
|
||||
reader.Skip(2);
|
||||
TotalCount = reader.ReadI32();
|
||||
UsedCount = reader.ReadI32();
|
||||
|
||||
Table.Clear();
|
||||
Table.Capacity = TotalCount;
|
||||
for (int i = 0; i < TotalCount; ++i)
|
||||
{
|
||||
var entry = new KeyTableEntry();
|
||||
entry.Deserialize (context, reader);
|
||||
Table.Add (entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class KeyTableEntry
|
||||
{
|
||||
public int Id;
|
||||
public int CastId;
|
||||
public string FourCC;
|
||||
|
||||
public void Deserialize (SerializationContext context, Reader input)
|
||||
{
|
||||
Id = input.ReadI32();
|
||||
CastId = input.ReadI32();
|
||||
FourCC = input.ReadFourCC();
|
||||
}
|
||||
}
|
||||
|
||||
internal class MemoryMap
|
||||
{
|
||||
public ushort HeaderLength;
|
||||
public ushort EntryLength;
|
||||
public int ChunkCountMax;
|
||||
public int ChunkCountUsed;
|
||||
public int FreeHead;
|
||||
public readonly List<MemoryMapEntry> Dir = new List<MemoryMapEntry>();
|
||||
|
||||
public MemoryMapEntry this[int index] => Dir[index];
|
||||
|
||||
public MemoryMapEntry Find (string four_cc) => Dir.Find (e => e.FourCC == four_cc);
|
||||
|
||||
public void Deserialize (SerializationContext context, Reader reader)
|
||||
{
|
||||
long header_pos = reader.Position;
|
||||
HeaderLength = reader.ReadU16();
|
||||
if (HeaderLength < 0x18)
|
||||
throw new InvalidFormatException ("Invalid <mmap> header length.");
|
||||
EntryLength = reader.ReadU16();
|
||||
if (EntryLength < 0x14)
|
||||
throw new InvalidFormatException ("Invalid <mmap> entry length.");
|
||||
ChunkCountMax = reader.ReadI32();
|
||||
ChunkCountUsed = reader.ReadI32();
|
||||
reader.Skip (8);
|
||||
FreeHead = reader.ReadI32();
|
||||
|
||||
Dir.Clear();
|
||||
Dir.Capacity = ChunkCountUsed;
|
||||
long entry_pos = header_pos + HeaderLength;
|
||||
for (int i = 0; i < ChunkCountUsed; ++i)
|
||||
{
|
||||
reader.Position = entry_pos;
|
||||
var entry = new MemoryMapEntry (i);
|
||||
entry.Deserialize (context, reader);
|
||||
Dir.Add (entry);
|
||||
entry_pos += EntryLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class MemoryMapEntry : Entry
|
||||
{
|
||||
public int Id;
|
||||
public string FourCC;
|
||||
public ushort Flags;
|
||||
|
||||
public MemoryMapEntry (int id = 0)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public void Deserialize (SerializationContext context, Reader reader)
|
||||
{
|
||||
FourCC = reader.ReadFourCC();
|
||||
Size = reader.ReadU32();
|
||||
Offset = reader.ReadU32() + 8;
|
||||
Flags = reader.ReadU16();
|
||||
int Next = reader.ReadI32();
|
||||
}
|
||||
}
|
||||
|
||||
internal class Reader
|
||||
{
|
||||
Stream m_input;
|
||||
byte[] m_buffer = new byte[4];
|
||||
|
||||
public Reader (Stream input, ByteOrder e = ByteOrder.LittleEndian) : this (input, Encodings.cp932, e)
|
||||
{
|
||||
}
|
||||
|
||||
public Reader (Stream input, Encoding enc, ByteOrder e = ByteOrder.LittleEndian)
|
||||
{
|
||||
m_input = input;
|
||||
Encoding = enc;
|
||||
SetByteOrder (e);
|
||||
}
|
||||
|
||||
public Stream Source { get => m_input; }
|
||||
|
||||
public ByteOrder ByteOrder { get; private set; }
|
||||
|
||||
public Encoding Encoding { get; set; }
|
||||
|
||||
public long Position
|
||||
{
|
||||
get => m_input.Position;
|
||||
set => m_input.Position = value;
|
||||
}
|
||||
|
||||
private Func<ushort> ToU16;
|
||||
private Func<uint> ToU32;
|
||||
|
||||
public void SetByteOrder (ByteOrder e)
|
||||
{
|
||||
this.ByteOrder = e;
|
||||
if (ByteOrder.LittleEndian == e)
|
||||
{
|
||||
ToU16 = () => LittleEndian.ToUInt16 (m_buffer, 0);
|
||||
ToU32 = () => LittleEndian.ToUInt32 (m_buffer, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
ToU16 = () => BigEndian.ToUInt16 (m_buffer, 0);
|
||||
ToU32 = () => BigEndian.ToUInt32 (m_buffer, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static Dictionary<uint, string> KnownFourCC = new Dictionary<uint, string>();
|
||||
|
||||
public string ReadFourCC ()
|
||||
{
|
||||
uint signature = ReadU32();
|
||||
string four_cc;
|
||||
if (KnownFourCC.TryGetValue (signature, out four_cc))
|
||||
return four_cc;
|
||||
BigEndian.Pack (signature, m_buffer, 0);
|
||||
return KnownFourCC[signature] = Encoding.GetString (m_buffer, 0, 4);
|
||||
}
|
||||
|
||||
public void Skip (int amount) => m_input.Seek (amount, SeekOrigin.Current);
|
||||
|
||||
public byte ReadU8 ()
|
||||
{
|
||||
int b = m_input.ReadByte();
|
||||
if (-1 == b)
|
||||
throw new EndOfStreamException();
|
||||
return (byte)b;
|
||||
}
|
||||
|
||||
public sbyte ReadI8 () => (sbyte)ReadU8();
|
||||
|
||||
public ushort ReadU16 ()
|
||||
{
|
||||
if (m_input.Read (m_buffer, 0, 2) < 2)
|
||||
throw new EndOfStreamException();
|
||||
return ToU16();
|
||||
}
|
||||
|
||||
public short ReadI16 () => (short)ReadU16();
|
||||
|
||||
public uint ReadU32 ()
|
||||
{
|
||||
if (m_input.Read (m_buffer, 0, 4) < 4)
|
||||
throw new EndOfStreamException();
|
||||
return ToU32();
|
||||
}
|
||||
|
||||
public int ReadI32 () => (int)ReadU32();
|
||||
|
||||
public byte[] ReadBytes (int length)
|
||||
{
|
||||
if (0 == length)
|
||||
return Array.Empty<byte>();
|
||||
var buffer = new byte[length];
|
||||
if (m_input.Read (buffer, 0, length) < length)
|
||||
throw new EndOfStreamException();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public Reader CloneUnless (ByteOrder order)
|
||||
{
|
||||
if (this.ByteOrder != order)
|
||||
return new Reader (this.Source, this.Encoding, order);
|
||||
else
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,9 +29,10 @@ using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Selen
|
||||
namespace GameRes.Formats.Macromedia
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class BitdFormat : ImageFormat
|
||||
@@ -202,4 +203,134 @@ namespace GameRes.Formats.Selen
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class BitdDecoder : IImageDecoder
|
||||
{
|
||||
Stream m_input;
|
||||
byte[] m_output;
|
||||
int m_width;
|
||||
int m_height;
|
||||
int m_stride;
|
||||
ImageMetaData m_info;
|
||||
ImageData m_image;
|
||||
BitmapPalette m_palette;
|
||||
|
||||
public Stream Source { get => m_input; }
|
||||
public ImageFormat SourceFormat { get => null; }
|
||||
public ImageMetaData Info { get => m_info; }
|
||||
public ImageData Image { get => m_image ?? (m_image = GetImageData()); }
|
||||
public PixelFormat Format { get; private set; }
|
||||
|
||||
public BitdDecoder (Stream input, ImageMetaData info, BitmapPalette palette)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
m_width = info.iWidth;
|
||||
m_height = info.iHeight;
|
||||
m_stride = (m_width * m_info.BPP + 7) / 8;
|
||||
m_stride = (m_stride + 1) & ~1;
|
||||
m_output = new byte[m_stride * m_height];
|
||||
Format = info.BPP == 4 ? PixelFormats.Indexed4
|
||||
: info.BPP == 8 ? PixelFormats.Indexed8
|
||||
: info.BPP == 16 ? PixelFormats.Bgr555
|
||||
: PixelFormats.Bgr32;
|
||||
m_palette = palette;
|
||||
}
|
||||
|
||||
protected ImageData GetImageData ()
|
||||
{
|
||||
if (Info.BPP <= 8)
|
||||
Unpack8bpp();
|
||||
else
|
||||
UnpackChannels (Info.BPP / 8);
|
||||
return ImageData.Create (m_info, Format, m_palette, m_output, m_stride);
|
||||
}
|
||||
|
||||
void Unpack8bpp ()
|
||||
{
|
||||
for (int line = 0; line < m_output.Length; line += m_stride)
|
||||
{
|
||||
int x = 0;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void UnpackChannels (int channels)
|
||||
{
|
||||
var scan_line = new byte[m_stride];
|
||||
for (int line = 0; line < m_output.Length; line += m_stride)
|
||||
{
|
||||
int x = 0;
|
||||
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)
|
||||
scan_line[x++] = (byte)b;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_input.Read (scan_line, x, count);
|
||||
x += count;
|
||||
}
|
||||
}
|
||||
int dst = line;
|
||||
for (int i = 0; i < m_width; ++i)
|
||||
{
|
||||
for (int src = m_width * (channels - 1); src >= 0; src -= m_width)
|
||||
m_output[dst++] = scan_line[i + src];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
bool m_disposed = false;
|
||||
|
||||
public void Dispose ()
|
||||
{
|
||||
if (!m_disposed)
|
||||
{
|
||||
m_input.Dispose();
|
||||
m_disposed = true;
|
||||
}
|
||||
GC.SuppressFinalize (this);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
1327
ArcFormats/Macromedia/Palettes.cs
Normal file
1327
ArcFormats/Macromedia/Palettes.cs
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user