mirror of
https://github.com/crskycode/GARbro.git
synced 2024-11-27 07:34:00 +08:00
Formats update.
(HG3): attempt to recognize swapped Red/Blue channels. Fairytale BDT archives. (XP3, VF): add 'exe' to extensions list. (DXR): consolidated Macromedia Director archives. (SND): support mp3 streams. (QPK): PSP resource archive. (GRC): support encrypted images.
This commit is contained in:
parent
b73bd0fb4d
commit
bfd81f9ec4
@ -103,14 +103,14 @@ namespace GameRes.Formats
|
|||||||
{
|
{
|
||||||
bool ext_is_empty = string.IsNullOrEmpty (ext);
|
bool ext_is_empty = string.IsNullOrEmpty (ext);
|
||||||
if (!ext_is_empty && '.' == ext[0])
|
if (!ext_is_empty && '.' == ext[0])
|
||||||
return filename.EndsWith (ext, StringComparison.InvariantCultureIgnoreCase);
|
return filename.EndsWith (ext, StringComparison.OrdinalIgnoreCase);
|
||||||
int ext_start = GetExtensionIndex (filename);
|
int ext_start = GetExtensionIndex (filename);
|
||||||
// filename extension length
|
// filename extension length
|
||||||
int l_ext_length = filename.Length - ext_start;
|
int l_ext_length = filename.Length - ext_start;
|
||||||
if (ext_is_empty)
|
if (ext_is_empty)
|
||||||
return 0 == l_ext_length;
|
return 0 == l_ext_length;
|
||||||
return (l_ext_length == ext.Length
|
return (l_ext_length == ext.Length
|
||||||
&& filename.EndsWith (ext, StringComparison.InvariantCultureIgnoreCase));
|
&& filename.EndsWith (ext, StringComparison.OrdinalIgnoreCase));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -129,7 +129,7 @@ namespace GameRes.Formats
|
|||||||
}
|
}
|
||||||
else if ('.' == ext[0] || l_ext_length == ext.Length)
|
else if ('.' == ext[0] || l_ext_length == ext.Length)
|
||||||
{
|
{
|
||||||
if (filename.EndsWith (ext, StringComparison.InvariantCultureIgnoreCase))
|
if (filename.EndsWith (ext, StringComparison.OrdinalIgnoreCase))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,6 +106,8 @@
|
|||||||
<Compile Include="AdvSys\ImageGWD.cs" />
|
<Compile Include="AdvSys\ImageGWD.cs" />
|
||||||
<Compile Include="Ail\ArcLNK2.cs" />
|
<Compile Include="Ail\ArcLNK2.cs" />
|
||||||
<Compile Include="AIRNovel\ArcAIR.cs" />
|
<Compile Include="AIRNovel\ArcAIR.cs" />
|
||||||
|
<Compile Include="FC01\ArcBDT.cs" />
|
||||||
|
<Compile Include="FC01\BdtTables.cs" />
|
||||||
<Compile Include="GScripter\ArcDATA.cs" />
|
<Compile Include="GScripter\ArcDATA.cs" />
|
||||||
<Compile Include="Ism\ImagePNG.cs" />
|
<Compile Include="Ism\ImagePNG.cs" />
|
||||||
<Compile Include="Kogado\ArcARC.cs" />
|
<Compile Include="Kogado\ArcARC.cs" />
|
||||||
@ -149,6 +151,7 @@
|
|||||||
<Compile Include="Macromedia\Palettes.cs" />
|
<Compile Include="Macromedia\Palettes.cs" />
|
||||||
<Compile Include="Mugi\ArcBIN.cs" />
|
<Compile Include="Mugi\ArcBIN.cs" />
|
||||||
<Compile Include="NScripter\Script.cs" />
|
<Compile Include="NScripter\Script.cs" />
|
||||||
|
<Compile Include="Psp\ArcQPK.cs" />
|
||||||
<Compile Include="ScrPlayer\ImageIMG.cs" />
|
<Compile Include="ScrPlayer\ImageIMG.cs" />
|
||||||
<Compile Include="Software House Parsley\ArcScn.cs" />
|
<Compile Include="Software House Parsley\ArcScn.cs" />
|
||||||
<Compile Include="TechGian\ArcBIN.cs" />
|
<Compile Include="TechGian\ArcBIN.cs" />
|
||||||
@ -726,7 +729,7 @@
|
|||||||
<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" />
|
||||||
<Compile Include="Macromedia\ArcCCT.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" />
|
||||||
|
@ -30,6 +30,8 @@ using System.Windows.Media.Imaging;
|
|||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using GameRes.Compression;
|
using GameRes.Compression;
|
||||||
using GameRes.Utility;
|
using GameRes.Utility;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace GameRes.Formats.CatSystem
|
namespace GameRes.Formats.CatSystem
|
||||||
{
|
{
|
||||||
@ -285,9 +287,10 @@ namespace GameRes.Formats.CatSystem
|
|||||||
byte[] UnpackJpeg ()
|
byte[] UnpackJpeg ()
|
||||||
{
|
{
|
||||||
Flipped = false;
|
Flipped = false;
|
||||||
m_input.ReadInt32();
|
var toc = ReadSections();
|
||||||
|
|
||||||
|
m_input.Position = toc["img_jpg"] + 12;
|
||||||
var jpeg_size = m_input.ReadInt32();
|
var jpeg_size = m_input.ReadInt32();
|
||||||
long next_section = Source.Position + jpeg_size;
|
|
||||||
BitmapSource frame;
|
BitmapSource frame;
|
||||||
using (var jpeg = new StreamRegion (Source, Source.Position, jpeg_size, true))
|
using (var jpeg = new StreamRegion (Source, Source.Position, jpeg_size, true))
|
||||||
{
|
{
|
||||||
@ -297,40 +300,69 @@ namespace GameRes.Formats.CatSystem
|
|||||||
if (frame.Format.BitsPerPixel < 24)
|
if (frame.Format.BitsPerPixel < 24)
|
||||||
throw new NotSupportedException ("Not supported HG-3 JPEG color depth");
|
throw new NotSupportedException ("Not supported HG-3 JPEG color depth");
|
||||||
int src_pixel_size = frame.Format.BitsPerPixel/8;
|
int src_pixel_size = frame.Format.BitsPerPixel/8;
|
||||||
int stride = (int)m_info.Width * src_pixel_size;
|
int stride = m_info.iWidth * src_pixel_size;
|
||||||
var pixels = new byte[stride*(int)m_info.Height];
|
var pixels = new byte[stride * m_info.iHeight];
|
||||||
frame.CopyPixels (pixels, stride, 0);
|
frame.CopyPixels (pixels, stride, 0);
|
||||||
var output = new byte[m_info.Width*m_info.Height*4];
|
|
||||||
uint total = m_info.Width * m_info.Height;
|
int total = m_info.iWidth * m_info.iHeight;
|
||||||
|
byte[] alpha = null;
|
||||||
|
if (toc.ContainsKey ("img_al"))
|
||||||
|
alpha = ReadAlpha (toc["img_al"]);
|
||||||
|
else
|
||||||
|
alpha = Enumerable.Repeat<byte> (0xFF, total).ToArray();
|
||||||
|
|
||||||
|
bool swap_rgb = toc.ContainsKey ("imgmode"); // XXX ???
|
||||||
|
|
||||||
|
var output = new byte[total * 4];
|
||||||
int src = 0;
|
int src = 0;
|
||||||
int dst = 0;
|
int dst = 0;
|
||||||
|
int src_A = 0;
|
||||||
|
int src_R = 2, src_G = 1, src_B = 0;
|
||||||
|
if (swap_rgb)
|
||||||
|
{
|
||||||
|
src_R = 0;
|
||||||
|
src_B = 2;
|
||||||
|
}
|
||||||
for (uint i = 0; i < total; ++i)
|
for (uint i = 0; i < total; ++i)
|
||||||
{
|
{
|
||||||
output[dst++] = pixels[src+2];
|
output[dst++] = pixels[src+src_B];
|
||||||
output[dst++] = pixels[src+1];
|
output[dst++] = pixels[src+src_G];
|
||||||
output[dst++] = pixels[src];
|
output[dst++] = pixels[src+src_R];
|
||||||
output[dst++] = 0xFF;
|
output[dst++] = alpha[src_A++];
|
||||||
src += src_pixel_size;
|
src += src_pixel_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_input.Position = next_section;
|
|
||||||
var section_header = m_input.ReadBytes (8);
|
|
||||||
if (!Binary.AsciiEqual (section_header, "img_al\0\0"))
|
|
||||||
return output;
|
return output;
|
||||||
m_input.Seek (8, SeekOrigin.Current);
|
}
|
||||||
|
|
||||||
|
byte[] ReadAlpha (long start_pos)
|
||||||
|
{
|
||||||
|
m_input.Position = start_pos + 0x10;
|
||||||
|
int packed_size = m_input.ReadInt32();
|
||||||
int alpha_size = m_input.ReadInt32();
|
int alpha_size = m_input.ReadInt32();
|
||||||
using (var alpha_in = new StreamRegion (Source, Source.Position+4, alpha_size, true))
|
using (var alpha_in = new StreamRegion (Source, Source.Position, packed_size, true))
|
||||||
using (var alpha = new ZLibStream (alpha_in, CompressionMode.Decompress))
|
using (var alpha = new ZLibStream (alpha_in, CompressionMode.Decompress))
|
||||||
{
|
{
|
||||||
for (int i = 3; i < output.Length; i += 4)
|
var alpha_data = new byte[alpha_size];
|
||||||
|
alpha.Read (alpha_data, 0, alpha_size);
|
||||||
|
return alpha_data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<string, long> ReadSections ()
|
||||||
{
|
{
|
||||||
int b = alpha.ReadByte();
|
long next_offset = m_info.HeaderSize;
|
||||||
if (-1 == b)
|
var toc = new Dictionary<string, long>();
|
||||||
throw new EndOfStreamException();
|
uint section_size;
|
||||||
output[i] = (byte)b;
|
do
|
||||||
}
|
{
|
||||||
return output;
|
m_input.Position = next_offset;
|
||||||
|
var section_name = m_input.ReadCString (8);
|
||||||
|
section_size = m_input.ReadUInt32();
|
||||||
|
toc[section_name] = next_offset;
|
||||||
|
next_offset += section_size;
|
||||||
}
|
}
|
||||||
|
while (section_size != 0);
|
||||||
|
return toc;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] UnpackWebp ()
|
byte[] UnpackWebp ()
|
||||||
|
@ -29,11 +29,11 @@ namespace GameRes.Formats.??????
|
|||||||
[Export(typeof(ArchiveFormat))]
|
[Export(typeof(ArchiveFormat))]
|
||||||
public class PakOpener : ArchiveFormat
|
public class PakOpener : ArchiveFormat
|
||||||
{
|
{
|
||||||
public override string Tag { get => "ARC"; }
|
public override string Tag => "ARC";
|
||||||
public override string Description { get => "?????? engine resource archive"; }
|
public override string Description => "?????? engine resource archive";
|
||||||
public override uint Signature { get => 0; }
|
public override uint Signature => 0;
|
||||||
public override bool IsHierarchic { get => false; }
|
public override bool IsHierarchic => false;
|
||||||
public override bool CanWrite { get => false; }
|
public override bool CanWrite => false;
|
||||||
|
|
||||||
public override ArcFile TryOpen (ArcView file)
|
public override ArcFile TryOpen (ArcView file)
|
||||||
{
|
{
|
||||||
@ -51,5 +51,10 @@ namespace GameRes.Formats.??????
|
|||||||
}
|
}
|
||||||
return new ArcFile (file, this, dir);
|
return new ArcFile (file, this, dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||||
|
{
|
||||||
|
return base.OpenEntry (arc, entry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,10 +26,10 @@ namespace GameRes.Formats.??????
|
|||||||
[Export(typeof(AudioFormat))]
|
[Export(typeof(AudioFormat))]
|
||||||
public class xxxAudio : AudioFormat
|
public class xxxAudio : AudioFormat
|
||||||
{
|
{
|
||||||
public override string Tag { get => "xxx"; }
|
public override string Tag => "xxx";
|
||||||
public override string Description { get => "?????? audio resource"; }
|
public override string Description => "?????? audio resource";
|
||||||
public override uint Signature { get => 0; }
|
public override uint Signature => 0;
|
||||||
public override bool CanWrite { get => false; }
|
public override bool CanWrite => false;
|
||||||
|
|
||||||
public override SoundInput TryOpen (IBinaryStream file)
|
public override SoundInput TryOpen (IBinaryStream file)
|
||||||
{
|
{
|
||||||
|
@ -28,9 +28,9 @@ namespace GameRes.Formats.??????
|
|||||||
[Export(typeof(ImageFormat))]
|
[Export(typeof(ImageFormat))]
|
||||||
public class xxxFormat : ImageFormat
|
public class xxxFormat : ImageFormat
|
||||||
{
|
{
|
||||||
public override string Tag { get => "xxx"; }
|
public override string Tag => "xxx";
|
||||||
public override string Description { get => "?????? image format"; }
|
public override string Description => "?????? image format";
|
||||||
public override uint Signature { get => 0; }
|
public override uint Signature => 0;
|
||||||
|
|
||||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||||
{
|
{
|
||||||
|
@ -64,7 +64,7 @@ namespace GameRes.Formats.Emote
|
|||||||
|
|
||||||
public PsbOpener ()
|
public PsbOpener ()
|
||||||
{
|
{
|
||||||
Extensions = new string[] { "psb", "pimg", "dpak", "psbz" };
|
Extensions = new string[] { "psb", "pimg", "dpak", "psbz", "psp" };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ArcFile TryOpen (ArcView file)
|
public override ArcFile TryOpen (ArcView file)
|
||||||
|
151
ArcFormats/FC01/ArcBDT.cs
Normal file
151
ArcFormats/FC01/ArcBDT.cs
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
//! \file ArcBDT.cs
|
||||||
|
//! \date 2023 Sep 10
|
||||||
|
//! \brief RPA resource archive.
|
||||||
|
//
|
||||||
|
// 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 System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.Composition;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
// [021228][Fairytale Kagetsu Gumi] Tsuki Jong
|
||||||
|
|
||||||
|
namespace GameRes.Formats.FC01
|
||||||
|
{
|
||||||
|
[Export(typeof(ArchiveFormat))]
|
||||||
|
public partial class BdtOpener : PakOpener
|
||||||
|
{
|
||||||
|
public override string Tag => "BDT";
|
||||||
|
public override string Description => "Fairytale resource archive";
|
||||||
|
public override uint Signature => 0x4B434150; // 'PACK'
|
||||||
|
public override bool IsHierarchic => false;
|
||||||
|
public override bool CanWrite => false;
|
||||||
|
|
||||||
|
public BdtOpener ()
|
||||||
|
{
|
||||||
|
Signatures = new[] { 0x4B434150u, 0u };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ArcFile TryOpen (ArcView file)
|
||||||
|
{
|
||||||
|
uint signature = file.View.ReadUInt32 (0);
|
||||||
|
if (signature != this.Signature)
|
||||||
|
return BogusMediaArchive (file, signature);
|
||||||
|
var arc_name = Path.GetFileNameWithoutExtension (file.Name).ToLowerInvariant();
|
||||||
|
if (!arc_name.StartsWith ("dt0"))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
uint index_pos = 12;
|
||||||
|
int count = file.View.ReadInt32 (4);
|
||||||
|
if (!IsSaneCount (count) || file.MaxOffset <= index_pos)
|
||||||
|
return null;
|
||||||
|
int entry_size = file.View.ReadInt32 (8);
|
||||||
|
|
||||||
|
string arc_num_str = arc_name.Substring (3);
|
||||||
|
int arc_num = int.Parse (arc_num_str, NumberStyles.HexNumber);
|
||||||
|
var dir = ReadVomIndex (file, arc_num, count, entry_size);
|
||||||
|
if (null == dir)
|
||||||
|
return null;
|
||||||
|
var arc_key = GetKey (arc_num);
|
||||||
|
return new AgsiArchive (file, this, dir, arc_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Entry> ReadVomIndex (ArcView file, int arc_num, int count, int record_size)
|
||||||
|
{
|
||||||
|
if (4 == arc_num) // dt004.bdt is an archive of indexes
|
||||||
|
{
|
||||||
|
var reader = new IndexReader (file, count, record_size);
|
||||||
|
return reader.ReadIndex();
|
||||||
|
}
|
||||||
|
var dt4name = VFS.ChangeFileName (file.Name, "dt004.bdt");
|
||||||
|
using (var dt4file = VFS.OpenView (dt4name))
|
||||||
|
{
|
||||||
|
var dt4arc = TryOpen (dt4file);
|
||||||
|
if (null == dt4arc)
|
||||||
|
return null;
|
||||||
|
using (dt4arc)
|
||||||
|
{
|
||||||
|
int vom_idx = arc_num;
|
||||||
|
if (arc_num > 4)
|
||||||
|
--vom_idx;
|
||||||
|
var voms = string.Format ("vom{0:D3}.dat", vom_idx);
|
||||||
|
var vom_entry = dt4arc.Dir.First (e => e.Name == voms);
|
||||||
|
using (var input = dt4arc.OpenEntry (vom_entry))
|
||||||
|
using (var index = BinaryStream.FromStream (input, vom_entry.Name))
|
||||||
|
{
|
||||||
|
var reader = new IndexReader (file, count, record_size);
|
||||||
|
return reader.ReadIndex (index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArcFile BogusMediaArchive (ArcView file, uint signature)
|
||||||
|
{
|
||||||
|
if (!file.Name.HasExtension (".bdt"))
|
||||||
|
return null;
|
||||||
|
string ext = null;
|
||||||
|
string type = "";
|
||||||
|
if (OggAudio.Instance.Signature == signature)
|
||||||
|
{
|
||||||
|
ext = ".ogg";
|
||||||
|
type = "audio";
|
||||||
|
}
|
||||||
|
else if (AudioFormat.Wav.Signature == signature && file.View.AsciiEqual (8, "AVI "))
|
||||||
|
{
|
||||||
|
ext = ".avi";
|
||||||
|
}
|
||||||
|
if (null == ext)
|
||||||
|
return null;
|
||||||
|
var name = Path.GetFileNameWithoutExtension (file.Name);
|
||||||
|
var dir = new List<Entry> {
|
||||||
|
new Entry {
|
||||||
|
Name = name + ext,
|
||||||
|
Type = type,
|
||||||
|
Offset = 0,
|
||||||
|
Size = (uint)file.MaxOffset
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return new ArcFile (file, this, dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] GetKey (int index)
|
||||||
|
{
|
||||||
|
int src = index * 8;
|
||||||
|
if (src + 8 > KeyOffsetTable.Length)
|
||||||
|
throw new ArgumentException ("Invalid AGSI key index.");
|
||||||
|
var key = new byte[8];
|
||||||
|
key[0] = KeySource[KeyOffsetTable[src++]];
|
||||||
|
key[1] = KeySource[KeyOffsetTable[src++]];
|
||||||
|
key[2] = KeySource[KeyOffsetTable[src++]];
|
||||||
|
key[3] = KeySource[KeyOffsetTable[src++] + 480];
|
||||||
|
key[4] = KeySource[KeyOffsetTable[src++] + 480];
|
||||||
|
key[5] = KeySource[KeyOffsetTable[src++] + 480];
|
||||||
|
key[6] = KeySource[KeyOffsetTable[src++] + 480];
|
||||||
|
key[7] = KeySource[KeyOffsetTable[src++] + 480];
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -102,8 +102,10 @@ namespace GameRes.Formats.FC01
|
|||||||
|
|
||||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||||
{
|
{
|
||||||
var aent = (AgsiEntry)entry;
|
var aent = entry as AgsiEntry;
|
||||||
var aarc = arc as AgsiArchive;
|
var aarc = arc as AgsiArchive;
|
||||||
|
if (null == aent)
|
||||||
|
return base.OpenEntry (arc, entry);
|
||||||
Stream input;
|
Stream input;
|
||||||
if (!aent.IsEncrypted)
|
if (!aent.IsEncrypted)
|
||||||
input = arc.File.CreateStream (entry.Offset, entry.Size);
|
input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||||
@ -202,16 +204,16 @@ namespace GameRes.Formats.FC01
|
|||||||
ArcView m_file;
|
ArcView m_file;
|
||||||
int m_count;
|
int m_count;
|
||||||
int m_record_size;
|
int m_record_size;
|
||||||
uint m_data_offset;
|
|
||||||
|
|
||||||
public bool IsEncrypted { get; set; }
|
public bool IsEncrypted { get; set; }
|
||||||
|
public uint DataOffset { get; set; }
|
||||||
|
|
||||||
public IndexReader (ArcView file, int count, int record_size, bool is_encrypted)
|
public IndexReader (ArcView file, int count, int record_size, bool is_encrypted = false)
|
||||||
{
|
{
|
||||||
m_file = file;
|
m_file = file;
|
||||||
m_count = count;
|
m_count = count;
|
||||||
m_record_size = record_size;
|
m_record_size = record_size;
|
||||||
m_data_offset = (uint)(0xC + m_count * m_record_size);
|
DataOffset = (uint)(0xC + m_count * m_record_size);
|
||||||
IsEncrypted = is_encrypted;
|
IsEncrypted = is_encrypted;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,7 +241,7 @@ namespace GameRes.Formats.FC01
|
|||||||
if (!ArchiveFormat.IsSaneCount (count) || record_size <= 0x10 || record_size > 0x100)
|
if (!ArchiveFormat.IsSaneCount (count) || record_size <= 0x10 || record_size > 0x100)
|
||||||
return null;
|
return null;
|
||||||
var reader = new IndexReader (file, count, record_size, is_encrypted);
|
var reader = new IndexReader (file, count, record_size, is_encrypted);
|
||||||
if (reader.m_data_offset >= file.MaxOffset)
|
if (reader.DataOffset >= file.MaxOffset)
|
||||||
return null;
|
return null;
|
||||||
return reader;
|
return reader;
|
||||||
}
|
}
|
||||||
@ -247,6 +249,10 @@ namespace GameRes.Formats.FC01
|
|||||||
public List<Entry> ReadIndex ()
|
public List<Entry> ReadIndex ()
|
||||||
{
|
{
|
||||||
using (var index = OpenIndex())
|
using (var index = OpenIndex())
|
||||||
|
return ReadIndex (index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Entry> ReadIndex (IBinaryStream index)
|
||||||
{
|
{
|
||||||
int name_size = m_record_size - 0x10;
|
int name_size = m_record_size - 0x10;
|
||||||
var dir = new List<Entry> (m_count);
|
var dir = new List<Entry> (m_count);
|
||||||
@ -256,7 +262,7 @@ namespace GameRes.Formats.FC01
|
|||||||
entry.UnpackedSize = index.ReadUInt32();
|
entry.UnpackedSize = index.ReadUInt32();
|
||||||
entry.Size = index.ReadUInt32();
|
entry.Size = index.ReadUInt32();
|
||||||
entry.Method = index.ReadInt32();
|
entry.Method = index.ReadInt32();
|
||||||
entry.Offset = index.ReadUInt32() + m_data_offset;
|
entry.Offset = index.ReadUInt32() + DataOffset;
|
||||||
if (!entry.CheckPlacement (m_file.MaxOffset))
|
if (!entry.CheckPlacement (m_file.MaxOffset))
|
||||||
return null;
|
return null;
|
||||||
var name = index.ReadCString (name_size);
|
var name = index.ReadCString (name_size);
|
||||||
@ -270,7 +276,6 @@ namespace GameRes.Formats.FC01
|
|||||||
}
|
}
|
||||||
return dir;
|
return dir;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
IBinaryStream OpenIndex ()
|
IBinaryStream OpenIndex ()
|
||||||
{
|
{
|
||||||
|
159
ArcFormats/FC01/BdtTables.cs
Normal file
159
ArcFormats/FC01/BdtTables.cs
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
//! \file BdtTables.cs
|
||||||
|
//! \date 2023 Sep 10
|
||||||
|
//! \brief BDT archives key tables.
|
||||||
|
//
|
||||||
|
|
||||||
|
namespace GameRes.Formats.FC01
|
||||||
|
{
|
||||||
|
public partial class BdtOpener
|
||||||
|
{
|
||||||
|
internal static readonly ushort[] KeyOffsetTable = new ushort[] { // dword_4C7D80
|
||||||
|
0x029, 0x0E3, 0x05E, 0x104, 0x261, 0x16C, 0x256, 0x22E, 0x08A, 0x023, 0x1B4, 0x01D, 0x115, 0x0D5,
|
||||||
|
0x178, 0x1EB, 0x07F, 0x1D6, 0x02F, 0x14C, 0x238, 0x04A, 0x099, 0x124, 0x1C9, 0x0F9, 0x071, 0x260,
|
||||||
|
0x094, 0x15F, 0x044, 0x078, 0x072, 0x1B9, 0x18C, 0x0E3, 0x0DF, 0x1F3, 0x162, 0x053, 0x03D, 0x1BF,
|
||||||
|
0x198, 0x210, 0x0DB, 0x218, 0x05B, 0x139, 0x0E8, 0x1A9, 0x098, 0x148, 0x1A7, 0x17F, 0x0B3, 0x1E1,
|
||||||
|
0x119, 0x05F, 0x131, 0x121, 0x1F7, 0x023, 0x240, 0x120, 0x172, 0x048, 0x1BE, 0x130, 0x10E, 0x1A5,
|
||||||
|
0x05A, 0x199, 0x15D, 0x116, 0x195, 0x0B6, 0x19F, 0x189, 0x003, 0x06A, 0x0D6, 0x1AA, 0x05A, 0x22C,
|
||||||
|
0x1BE, 0x047, 0x1DE, 0x234, 0x04E, 0x0EE, 0x043, 0x1F9, 0x098, 0x0C3, 0x11D, 0x0BB, 0x099, 0x1AB,
|
||||||
|
0x0AE, 0x060, 0x1FE, 0x0A1, 0x1A6, 0x111, 0x1B5, 0x15F, 0x102, 0x24E, 0x106, 0x000, 0x0BA, 0x1BB,
|
||||||
|
0x0C3, 0x0CA, 0x0DC, 0x090, 0x18D, 0x1E2, 0x1BD, 0x161, 0x0E5, 0x137, 0x17C, 0x20D, 0x042, 0x1DA,
|
||||||
|
0x0D8, 0x131, 0x013, 0x01F, 0x177, 0x069, 0x1AF, 0x129, 0x18F, 0x251, 0x070, 0x106, 0x091, 0x209,
|
||||||
|
0x0C0, 0x01A, 0x05E, 0x23A, 0x0F3, 0x03E, 0x1B6, 0x0F7, 0x066, 0x19B, 0x268, 0x08B, 0x0FD, 0x156,
|
||||||
|
0x01D, 0x10F, 0x13B, 0x1F1, 0x083, 0x163, 0x010, 0x1B8, 0x1C8, 0x1CD, 0x103, 0x1C0, 0x0DD, 0x18A,
|
||||||
|
0x035, 0x0A3, 0x0FF, 0x0B0, 0x1D3, 0x00E, 0x025, 0x1FB, 0x193, 0x083, 0x0CD, 0x235, 0x0F4, 0x05F,
|
||||||
|
0x0C8, 0x18B, 0x055, 0x0DE, 0x017, 0x24A, 0x046, 0x036, 0x207, 0x123, 0x0E9, 0x18D, 0x146, 0x073,
|
||||||
|
0x09D, 0x00D, 0x0AF, 0x153, 0x0C2, 0x0A8, 0x0AF, 0x21F, 0x1D4, 0x074, 0x026, 0x20C, 0x170, 0x13B,
|
||||||
|
0x173, 0x0D2, 0x122, 0x076, 0x00C, 0x200, 0x0AA, 0x046, 0x0D4, 0x25A, 0x03D, 0x0C7, 0x0CF, 0x1DD,
|
||||||
|
0x17E, 0x176, 0x13C, 0x1D7, 0x084, 0x002, 0x02E, 0x203, 0x014, 0x067, 0x12A, 0x00F, 0x0C2, 0x1F0,
|
||||||
|
0x0E2, 0x222, 0x0C9, 0x079, 0x12B, 0x1A0, 0x1BA, 0x257, 0x156, 0x0A0, 0x109, 0x17A, 0x15E, 0x048,
|
||||||
|
0x02D, 0x01E, 0x24D, 0x082, 0x1AD, 0x0D0, 0x042, 0x029, 0x068, 0x214, 0x14A, 0x0B9, 0x0CB, 0x134,
|
||||||
|
0x027, 0x223, 0x0A3, 0x0B4, 0x0EC, 0x197, 0x107, 0x136, 0x0B9, 0x12F, 0x20B, 0x220, 0x145, 0x07F,
|
||||||
|
0x052, 0x11E, 0x02C, 0x0C1, 0x0CD, 0x110, 0x1C3, 0x1C8, 0x044, 0x162, 0x09D, 0x087, 0x15E, 0x0B7,
|
||||||
|
0x055, 0x17D, 0x076, 0x01A, 0x0B0, 0x017, 0x05C, 0x012, 0x247, 0x081, 0x088, 0x0A6, 0x14C, 0x075,
|
||||||
|
0x248, 0x151, 0x0E8, 0x004, 0x032, 0x111, 0x1A4, 0x1A9, 0x0BF, 0x137, 0x21A, 0x0D7, 0x189, 0x04A,
|
||||||
|
0x12E, 0x170, 0x0DA, 0x019, 0x04D, 0x0D6, 0x16A, 0x168, 0x19C, 0x19C, 0x188, 0x1C4, 0x204, 0x12B,
|
||||||
|
0x125, 0x128, 0x00B, 0x114, 0x0E7, 0x070, 0x06C, 0x1FD, 0x0C6, 0x1B0, 0x003, 0x15D, 0x085, 0x07A,
|
||||||
|
0x1C2, 0x237, 0x041, 0x07D, 0x183, 0x08E, 0x216, 0x126, 0x224, 0x010, 0x0E4, 0x059, 0x019, 0x0EB,
|
||||||
|
0x1E8, 0x04C, 0x1F6, 0x091, 0x18E, 0x08C, 0x0F0, 0x13F, 0x0F0, 0x065, 0x157, 0x1B8, 0x08E, 0x142,
|
||||||
|
0x033, 0x20F, 0x03B, 0x242, 0x1AC, 0x0E0, 0x005, 0x1A3, 0x17B, 0x03C, 0x175, 0x22D, 0x239, 0x10A,
|
||||||
|
0x022, 0x006, 0x07B, 0x16D, 0x014, 0x052, 0x241, 0x1B1, 0x12C, 0x074, 0x0F1, 0x1FF, 0x0B1, 0x118,
|
||||||
|
0x0AE, 0x062, 0x139, 0x0DD, 0x103, 0x193, 0x11A, 0x02B, 0x06E, 0x077, 0x0A7, 0x158, 0x15C, 0x21D,
|
||||||
|
0x230, 0x009, 0x194, 0x215, 0x171, 0x09F, 0x0DB, 0x174, 0x04F, 0x17E, 0x022, 0x1FC, 0x196, 0x06C,
|
||||||
|
0x04F, 0x16A, 0x1C6, 0x142, 0x1D1, 0x1A1, 0x045, 0x024, 0x092, 0x16F, 0x0A7, 0x1B0, 0x231, 0x1D6,
|
||||||
|
0x129, 0x181, 0x145, 0x0AC, 0x050, 0x0E6, 0x024, 0x154, 0x002, 0x152, 0x016, 0x23B, 0x1B2, 0x14F,
|
||||||
|
0x132, 0x17C, 0x11C, 0x00D, 0x085, 0x208, 0x229, 0x1D9, 0x158, 0x13E, 0x135, 0x018, 0x0E2, 0x12C,
|
||||||
|
0x1D5, 0x1C5, 0x168, 0x054, 0x108, 0x17F, 0x09C, 0x1ED, 0x021, 0x127, 0x160, 0x03A, 0x0BF, 0x0B3,
|
||||||
|
0x10B, 0x140, 0x008, 0x13A, 0x232, 0x186, 0x051, 0x061, 0x056, 0x09B, 0x059, 0x19A, 0x0B5, 0x169,
|
||||||
|
0x066, 0x16E, 0x07C, 0x072, 0x0A4, 0x144, 0x196, 0x133, 0x0F7, 0x16B, 0x118, 0x1E0, 0x16E, 0x17B,
|
||||||
|
0x034, 0x11B, 0x06F, 0x08F, 0x0C0, 0x0EE, 0x213, 0x03F, 0x181, 0x141, 0x0A5, 0x09E, 0x120, 0x0F9,
|
||||||
|
0x0F8, 0x030, 0x187, 0x155, 0x050, 0x069, 0x151, 0x03E, 0x164, 0x032, 0x0E9, 0x0D4, 0x000, 0x0AD,
|
||||||
|
0x178, 0x19E, 0x1C1, 0x21C, 0x10C, 0x21E, 0x0AB, 0x0FC, 0x026, 0x1AA, 0x01C, 0x177, 0x058, 0x04B,
|
||||||
|
0x10D, 0x0DF, 0x081, 0x079, 0x08C, 0x1D2, 0x21B, 0x0B2, 0x049, 0x025, 0x14D, 0x18C, 0x018, 0x19D,
|
||||||
|
0x202, 0x125, 0x073, 0x0D9, 0x191, 0x198, 0x1AB, 0x07B, 0x005, 0x1C9, 0x114, 0x065, 0x0F6, 0x02A,
|
||||||
|
0x0C4, 0x0DC, 0x056, 0x108, 0x009, 0x07E, 0x180, 0x07E, 0x0A5, 0x1A8, 0x09F, 0x1C7, 0x094, 0x0BD,
|
||||||
|
0x028, 0x11E, 0x107, 0x0AB, 0x0EF, 0x05D, 0x0C8, 0x096, 0x037, 0x1BF, 0x1EE, 0x06F, 0x201, 0x219,
|
||||||
|
0x147, 0x0FA, 0x060, 0x0D9, 0x185, 0x09C, 0x1CB, 0x1DF, 0x087, 0x021, 0x0B1, 0x15A, 0x0E5, 0x040,
|
||||||
|
0x063, 0x167, 0x04B, 0x10C, 0x0EA, 0x04E, 0x028, 0x1F4, 0x016, 0x0CA, 0x10F, 0x127, 0x04C, 0x166,
|
||||||
|
0x0CE, 0x190, 0x1E9, 0x15B, 0x14B, 0x159, 0x11B, 0x1B9, 0x013, 0x0DE, 0x0F1, 0x0F6, 0x00A, 0x06D,
|
||||||
|
0x140, 0x0BD, 0x226, 0x1EA, 0x088, 0x0ED, 0x16F, 0x153, 0x0A2, 0x1EF, 0x0CC, 0x020, 0x00B, 0x1DB,
|
||||||
|
0x06E, 0x0CF, 0x090, 0x031, 0x1B5, 0x0FC, 0x080, 0x001, 0x14F, 0x01E, 0x113, 0x0A2, 0x221, 0x22A,
|
||||||
|
0x1CA, 0x0AA, 0x110, 0x0B6, 0x0D5, 0x1D0, 0x228, 0x07C, 0x18E, 0x1B6, 0x064, 0x063, 0x05C, 0x011,
|
||||||
|
0x1E7, 0x105, 0x1E4, 0x146, 0x12F, 0x0BC, 0x011, 0x195, 0x183, 0x1CC, 0x0FF, 0x17A, 0x114, 0x065,
|
||||||
|
0x0F6, 0x02A, 0x0C4, 0x0DC, 0x056, 0x108, 0x009, 0x07E, 0x180, 0x07E, 0x0A5, 0x1A8, 0x09F, 0x1C7,
|
||||||
|
0x094, 0x0BD, 0x028, 0x11E, 0x107, 0x0AB, 0x0EF, 0x05D, 0x0C8, 0x096, 0x037, 0x1BF, 0x1EE, 0x06F,
|
||||||
|
0x201, 0x219, 0x147, 0x0FA, 0x060, 0x0D9, 0x185, 0x09C, 0x1CB, 0x1DF, 0x087, 0x021, 0x0B1, 0x15A,
|
||||||
|
0x0E5, 0x040, 0x063, 0x167, 0x04B, 0x10C, 0x0EA, 0x04E, 0x028, 0x1F4, 0x016, 0x0CA, 0x114, 0x065,
|
||||||
|
0x0F6, 0x02A, 0x0C4, 0x0DC, 0x056, 0x108, 0x009, 0x07E, 0x180, 0x07E, 0x0A5, 0x1A8, 0x09F, 0x1C7,
|
||||||
|
0x094, 0x0BD, 0x028, 0x11E, 0x107, 0x0AB, 0x0EF, 0x05D, 0x0C8, 0x096, 0x037, 0x1BF, 0x1EE, 0x06F,
|
||||||
|
0x201, 0x219, 0x147, 0x0FA, 0x060, 0x0D9, 0x185, 0x09C, 0x1CB, 0x1DF, 0x087, 0x021, 0x0B1, 0x15A,
|
||||||
|
0x0E5, 0x040, 0x063, 0x167, 0x04B, 0x10C, 0x0EA, 0x04E, 0x028, 0x1F4, 0x016, 0x0CA, 0x002, 0x152,
|
||||||
|
0x016, 0x23B, 0x1B2, 0x14F, 0x132, 0x17C, 0x11C, 0x00D, 0x085, 0x208, 0x229, 0x1D9, 0x158, 0x13E,
|
||||||
|
0x135, 0x018, 0x0E2, 0x12C, 0x1D5, 0x1C5, 0x168, 0x054, 0x108, 0x17F, 0x09C, 0x1ED, 0x021, 0x127,
|
||||||
|
0x160, 0x03A, 0x0BF, 0x0B3, 0x10B, 0x140, 0x008, 0x13A, 0x232, 0x186, 0x051, 0x061, 0x056, 0x09B,
|
||||||
|
0x059, 0x19A, 0x0B5, 0x169, 0x066, 0x16E, 0x07C, 0x072, 0x0A4, 0x144, 0x196, 0x133, 0x0F7, 0x16B,
|
||||||
|
0x118, 0x1E0, 0x16E, 0x17B, 0x034, 0x11B, 0x06F, 0x08F, 0x0C0, 0x0EE, 0x213, 0x03F, 0x181, 0x141,
|
||||||
|
0x0A5, 0x09E, 0x120, 0x0F9, 0x0F8, 0x030, 0x187, 0x155, 0x050, 0x069, 0x151, 0x03E, 0x164, 0x032,
|
||||||
|
0x0E9, 0x0D4, 0x000, 0x0AD, 0x178, 0x19E, 0x1C1, 0x21C, 0x10C, 0x21E, 0x0AB, 0x0FC, 0x026, 0x1AA,
|
||||||
|
0x01C, 0x177, 0x058, 0x04B, 0x10D, 0x0DF, 0x081, 0x079, 0x08C, 0x1D2, 0x21B, 0x0B2, 0x049, 0x025,
|
||||||
|
0x14D, 0x18C, 0x018, 0x19D, 0x202, 0x125, 0x073, 0x0D9, 0x191, 0x198, 0x1AB, 0x07B, 0x005, 0x1C9,
|
||||||
|
0x114, 0x065, 0x0F6, 0x02A, 0x0C4, 0x0DC, 0x056, 0x108, 0x009, 0x07E, 0x180, 0x07E, 0x0A5, 0x1A8,
|
||||||
|
0x09F, 0x1C7, 0x094, 0x0BD, 0x028, 0x11E, 0x107, 0x0AB, 0x0EF, 0x05D, 0x0C8, 0x096, 0x037, 0x1BF,
|
||||||
|
0x1EE, 0x06F, 0x201, 0x219, 0x147, 0x0FA, 0x060, 0x0D9, 0x185, 0x09C, 0x1CB, 0x1DF, 0x087, 0x021,
|
||||||
|
0x0B1, 0x15A, 0x0E5, 0x040, 0x063, 0x167, 0x04B, 0x10C, 0x0EA, 0x04E, 0x028, 0x1F4, 0x016, 0x0CA,
|
||||||
|
0x10F, 0x127, 0x04C, 0x166, 0x0CE, 0x190, 0x1E9, 0x15B, 0x14B, 0x159, 0x11B, 0x1B9, 0x013, 0x0DE,
|
||||||
|
0x0F1, 0x0F6,
|
||||||
|
};
|
||||||
|
internal static readonly byte[] KeySource = new byte[] { // byte_4C7920
|
||||||
|
0x5A, 0x4F, 0xC6, 0xCD, 0x58, 0xD3, 0xA1, 0x3F, 0x74, 0xC5, 0xA2, 0xD8, 0x47, 0x6A, 0xEF, 0x42,
|
||||||
|
0x4A, 0x65, 0x54, 0x75, 0xD9, 0x2E, 0xB1, 0x76, 0x48, 0x41, 0x30, 0xDF, 0xBE, 0xD9, 0x5F, 0xD9,
|
||||||
|
0x39, 0xA8, 0xAD, 0xB3, 0xBD, 0xAC, 0xB0, 0xCB, 0xC6, 0x29, 0xB6, 0xE6, 0xD0, 0x0C, 0x67, 0xDE,
|
||||||
|
0x1C, 0x1B, 0x72, 0xAF, 0x74, 0x24, 0x5C, 0xA9, 0xFE, 0x77, 0x9D, 0x6B, 0x6E, 0x45, 0x64, 0x93,
|
||||||
|
0x48, 0xB0, 0x39, 0xDC, 0x4D, 0x79, 0xBF, 0xA7, 0x3B, 0xD2, 0x53, 0x2B, 0xB0, 0x44, 0x7E, 0x6D,
|
||||||
|
0x76, 0x4E, 0xBE, 0xB3, 0x3E, 0x27, 0x3C, 0x02, 0xB8, 0xAD, 0x53, 0x04, 0x42, 0x87, 0xBE, 0x40,
|
||||||
|
0x5D, 0x60, 0xB6, 0x6A, 0x59, 0x5D, 0xBB, 0x71, 0x30, 0x4B, 0x22, 0xDD, 0x6C, 0x37, 0x57, 0x2C,
|
||||||
|
0x50, 0x7D, 0xBE, 0x33, 0x32, 0xE4, 0x2B, 0x56, 0xDD, 0xA9, 0x61, 0xBE, 0x2F, 0x5A, 0x3A, 0xB7,
|
||||||
|
0x7B, 0x5C, 0x6E, 0xD8, 0x3D, 0x50, 0x8C, 0xD7, 0x68, 0x5C, 0xBB, 0xE7, 0x4C, 0xC4, 0x40, 0x2E,
|
||||||
|
0x6C, 0x61, 0xB8, 0x67, 0xD5, 0xBC, 0x21, 0x2F, 0x32, 0x61, 0xF2, 0xFF, 0xCF, 0xBE, 0xAF, 0x74,
|
||||||
|
0xC9, 0xCC, 0x26, 0x79, 0x0F, 0x74, 0xCA, 0x54, 0x3B, 0x13, 0x54, 0xAF, 0x90, 0x72, 0xD1, 0x6A,
|
||||||
|
0x70, 0x59, 0xA9, 0x27, 0x24, 0x11, 0xC8, 0x1E, 0xAD, 0xC5, 0x4B, 0x41, 0xB2, 0xA4, 0xE5, 0xBD,
|
||||||
|
0xDC, 0x92, 0xB1, 0xD5, 0x28, 0x3F, 0x7C, 0x62, 0x2A, 0x46, 0xD4, 0x28, 0xF5, 0x28, 0xB8, 0xB6,
|
||||||
|
0xBF, 0xAE, 0xFD, 0xA9, 0xD9, 0xBC, 0xC9, 0x49, 0x2B, 0xB9, 0x31, 0x4F, 0xD4, 0xA6, 0x59, 0x5B,
|
||||||
|
0x51, 0xCA, 0x31, 0x23, 0xB1, 0x32, 0x80, 0xEC, 0x36, 0x68, 0xB8, 0x2E, 0x45, 0x1A, 0x2D, 0x39,
|
||||||
|
0xD1, 0x34, 0x35, 0xDA, 0xAA, 0xBE, 0x5A, 0xD3, 0x1C, 0x5A, 0xCF, 0x65, 0xBF, 0x6A, 0xCE, 0x52,
|
||||||
|
0x77, 0xBD, 0x2C, 0xD8, 0x57, 0xB3, 0xD4, 0x61, 0x43, 0xB1, 0xD3, 0x28, 0x37, 0xDA, 0xC9, 0x3C,
|
||||||
|
0x61, 0x7B, 0x17, 0x40, 0x32, 0x0A, 0x72, 0x4E, 0x50, 0x44, 0x16, 0x32, 0x2F, 0x32, 0x71, 0xF6,
|
||||||
|
0xB5, 0x0F, 0x86, 0x10, 0x13, 0xCA, 0xD2, 0x36, 0x27, 0x62, 0xA5, 0x79, 0x69, 0x2D, 0x33, 0x30,
|
||||||
|
0x84, 0x66, 0xB7, 0x26, 0xA1, 0xA1, 0xC9, 0x22, 0xB6, 0xC8, 0xA2, 0xC1, 0x6C, 0x78, 0xD8, 0x02,
|
||||||
|
0x53, 0xD1, 0x43, 0xEE, 0xAF, 0x5C, 0x49, 0xCA, 0x13, 0x21, 0xBA, 0xA3, 0xBE, 0xA9, 0x59, 0x3A,
|
||||||
|
0x29, 0x6B, 0xC3, 0xCC, 0xDE, 0xC6, 0x73, 0xA6, 0xD0, 0xB9, 0x2A, 0x65, 0x71, 0xB9, 0xDF, 0x39,
|
||||||
|
0xCF, 0xF4, 0xD0, 0x47, 0x36, 0x21, 0xEB, 0xDD, 0xB4, 0x4B, 0x73, 0xBE, 0xA2, 0x6A, 0xB8, 0x46,
|
||||||
|
0x2F, 0xDB, 0x32, 0xC0, 0x99, 0xA8, 0xD9, 0x2A, 0x71, 0x12, 0x26, 0xCE, 0xCC, 0xC0, 0xD1, 0xCD,
|
||||||
|
0x6C, 0x27, 0xE0, 0x4E, 0x34, 0x91, 0x20, 0x6E, 0xC7, 0x66, 0x62, 0x4F, 0x2F, 0xD4, 0x26, 0x3D,
|
||||||
|
0x84, 0x35, 0xEE, 0xD3, 0xA0, 0x49, 0xDF, 0xA8, 0xDB, 0x69, 0x0C, 0x22, 0x31, 0xD0, 0x24, 0x7F,
|
||||||
|
0x18, 0x3F, 0x28, 0xA1, 0xCD, 0xC0, 0xA9, 0xCB, 0x01, 0x49, 0xCC, 0xCD, 0xFF, 0x34, 0xD9, 0xBD,
|
||||||
|
0xC1, 0xE4, 0x64, 0xB8, 0xA6, 0xD7, 0x54, 0x33, 0xD0, 0xCE, 0x8B, 0x9A, 0xD9, 0xC3, 0xA1, 0x3B,
|
||||||
|
0x40, 0x8E, 0x06, 0x61, 0x37, 0x74, 0x32, 0x83, 0x57, 0xA6, 0x20, 0x0C, 0xAF, 0x83, 0xFD, 0x23,
|
||||||
|
0xA2, 0x44, 0x31, 0x5C, 0xB3, 0x18, 0x47, 0xDA, 0x30, 0x09, 0xE1, 0x61, 0x34, 0x75, 0x77, 0x50,
|
||||||
|
0xC9, 0xDF, 0x5E, 0xD5, 0x68, 0xD5, 0x9A, 0xED, 0x5F, 0x29, 0x4A, 0x55, 0xC2, 0x25, 0x43, 0xB4,
|
||||||
|
0xDE, 0xC4, 0x32, 0x2D, 0xA6, 0x63, 0x4E, 0xA7, 0x23, 0x5E, 0x3B, 0xFB, 0x75, 0xDB, 0x6D, 0x3D,
|
||||||
|
0xD8, 0xC7, 0xB0, 0xC4, 0x61, 0x3C, 0xD6, 0x49, 0x7A, 0xA6, 0xDC, 0x6B, 0xE1, 0x29, 0x4E, 0x54,
|
||||||
|
0x5E, 0x6E, 0x21, 0xF9, 0x21, 0x02, 0xC2, 0x2C, 0x4E, 0xB6, 0xBF, 0xCA, 0x5A, 0xD1, 0x36, 0x3D,
|
||||||
|
0x55, 0x0A, 0xD3, 0x97, 0xD4, 0xC2, 0xD0, 0xD6, 0xDA, 0x79, 0xC8, 0x46, 0xA5, 0x4C, 0xD5, 0xB8,
|
||||||
|
0x3B, 0x9F, 0xB4, 0x61, 0xA3, 0x46, 0xB5, 0x79, 0x58, 0x42, 0xB0, 0x40, 0x54, 0xD6, 0x33, 0x60,
|
||||||
|
0x72, 0x7C, 0xDB, 0x4E, 0x70, 0xB3, 0xB1, 0x8C, 0x3F, 0xC0, 0xB2, 0x80, 0x59, 0x3F, 0x68, 0xBB,
|
||||||
|
0x27, 0xE0, 0x73, 0x6A, 0xC1, 0x7D, 0xA7, 0x62, 0xCB, 0xCB, 0xB8, 0x2A, 0x4A, 0x4D, 0x57, 0xA8,
|
||||||
|
0x3A, 0x5B, 0x62, 0xD4, 0x66, 0x64, 0x47, 0xBE, 0x37, 0x0A, 0x5F, 0x46, 0x36, 0x74, 0x2D, 0x2A,
|
||||||
|
0xA4, 0x42, 0x9A, 0x43, 0x25, 0x4B, 0x59, 0x30, 0x57, 0x43, 0x5A, 0xDE, 0x45, 0x79, 0xBB, 0xB2,
|
||||||
|
0x63, 0x4F, 0xDD, 0x3F, 0x51, 0xDF, 0x2B, 0x6E, 0x94, 0xDC, 0xA6, 0x5F, 0x4A, 0x6C, 0xC6, 0x61,
|
||||||
|
0xDB, 0x4D, 0x55, 0xAD, 0x41, 0x2B, 0x2C, 0xC2, 0xB5, 0x6C, 0x33, 0x69, 0x5A, 0xB5, 0x9D, 0x44,
|
||||||
|
0x7B, 0x3B, 0x47, 0x49, 0x77, 0x2E, 0x9C, 0x55, 0x59, 0x64, 0x21, 0x87, 0x7A, 0x75, 0xBF, 0x38,
|
||||||
|
0x98, 0x27, 0x7E, 0xA8, 0x7D, 0x3E, 0x4C, 0xA6, 0x48, 0xD6, 0xA1, 0x32, 0x5D, 0xAD, 0x2C, 0xDB,
|
||||||
|
0xCF, 0x48, 0x38, 0x68, 0xEE, 0x26, 0x32, 0x79, 0x4F, 0xDB, 0x98, 0x67, 0x65, 0x72, 0xBC, 0xC3,
|
||||||
|
0x2F, 0x74, 0x33, 0x48, 0xC5, 0x48, 0x43, 0xCE, 0xD5, 0xA4, 0xBD, 0x6E, 0xB0, 0xDE, 0xF1, 0x3D,
|
||||||
|
0xC2, 0xA1, 0xC3, 0x76, 0x6C, 0xBE, 0xCB, 0x77, 0x69, 0x0E, 0x2A, 0x49, 0xD9, 0xB5, 0x22, 0x2F,
|
||||||
|
0x7E, 0x77, 0xC7, 0x80, 0x4D, 0x3C, 0xBC, 0x3E, 0x56, 0xC2, 0x61, 0x2B, 0x0B, 0x61, 0xBC, 0xE2,
|
||||||
|
0xB7, 0xD0, 0xA8, 0xCD, 0xBB, 0xA3, 0xB1, 0x50, 0xB9, 0x78, 0xCE, 0xB0, 0x6C, 0xB7, 0xB1, 0x2C,
|
||||||
|
0x22, 0xD3, 0x75, 0xD8, 0x11, 0xB3, 0x76, 0x3A, 0x64, 0x78, 0xB9, 0x67, 0x69, 0xA3, 0x47, 0x21,
|
||||||
|
0x5D, 0x31, 0x43, 0x08, 0x7E, 0xDC, 0xDB, 0xCD, 0x7D, 0x15, 0x6D, 0x28, 0xB3, 0x1C, 0xEB, 0x4A,
|
||||||
|
0xE9, 0xB8, 0x5A, 0x40, 0x40, 0x40, 0xC2, 0x73, 0x28, 0xB8, 0xCC, 0x66, 0xBF, 0x23, 0xDB, 0x5D,
|
||||||
|
0x59, 0xCF, 0x2D, 0x4A, 0xB1, 0xB7, 0x7D, 0xD9, 0xDE, 0xB6, 0x74, 0x55, 0xAE, 0x65, 0xD6, 0x60,
|
||||||
|
0xCB, 0xDA, 0x5C, 0x13, 0x37, 0xBE, 0xEA, 0xDC, 0x24, 0x60, 0x4A, 0x43, 0x6F, 0x41, 0xB5, 0xDC,
|
||||||
|
0x3E, 0x6D, 0x59, 0x51, 0x95, 0x2F, 0x34, 0x6A, 0xAD, 0xC5, 0xAE, 0x3D, 0x26, 0x59, 0x77, 0x44,
|
||||||
|
0xB1, 0x25, 0xDC, 0x38, 0x29, 0x22, 0x7D, 0x28, 0x68, 0xCA, 0xB2, 0x3E, 0xAA, 0x40, 0xA8, 0x7E,
|
||||||
|
0x7A, 0xA5, 0xF2, 0xAF, 0x6D, 0xDA, 0x4A, 0x49, 0x68, 0xD3, 0x74, 0xAD, 0xD5, 0x80, 0xD2, 0x2B,
|
||||||
|
0x3C, 0xC0, 0x30, 0xE5, 0x83, 0x3F, 0xC3, 0x57, 0x53, 0xB9, 0x76, 0x74, 0x45, 0x35, 0x67, 0xD4,
|
||||||
|
0xBB, 0xB8, 0xA6, 0x78, 0xCF, 0xA4, 0xD9, 0xA2, 0x73, 0xB2, 0xC0, 0x53, 0x3C, 0x68, 0xE9, 0xA0,
|
||||||
|
0x78, 0x65, 0x49, 0x25, 0x28, 0xCD, 0x5B, 0xA1, 0xC4, 0x29, 0x2D, 0xCA, 0xEC, 0x28, 0xBF, 0x29,
|
||||||
|
0x6B, 0x4F, 0x7E, 0x67, 0xC3, 0x0F, 0xFA, 0x71, 0x41, 0xC6, 0x3E, 0x5E, 0x02, 0x57, 0xAE, 0x7E,
|
||||||
|
0x65, 0x68, 0x3B, 0xCE, 0x5C, 0x8F, 0xD5, 0x6B, 0xCA, 0x66, 0xE4, 0x45, 0x57, 0x42, 0x72, 0x2C,
|
||||||
|
0xB5, 0x73, 0x5B, 0x30, 0xD5, 0xE1, 0xF9, 0xC9, 0xC8, 0xBE, 0xB9, 0x46, 0xA9, 0xCF, 0xD1, 0x7E,
|
||||||
|
0xAE, 0x2D, 0x22, 0xCA, 0x5E, 0x72, 0x3D, 0x56, 0x50, 0x60, 0xB4, 0x74, 0x5E, 0x48, 0x4E, 0xA5,
|
||||||
|
0x36, 0xBA, 0x2A, 0xC2, 0x60, 0xEE, 0x37, 0x3C, 0x2B, 0x43, 0xB9, 0x03, 0xBF, 0x65, 0x49, 0xCB,
|
||||||
|
0x78, 0x7B, 0x49, 0x8D, 0xD6, 0xCE, 0xAA, 0x4C, 0x4D, 0x27, 0x70, 0xA7, 0x17, 0xB1, 0xAE, 0x05,
|
||||||
|
0x30, 0xC9, 0x6F, 0x05, 0x29, 0xC6, 0x82, 0xAA, 0x41, 0x7F, 0x2D, 0x28, 0xC0, 0x3E, 0x53, 0xEF,
|
||||||
|
0x6A, 0x5F, 0x12, 0x42, 0xE9, 0x3F, 0x52, 0x78, 0x8B, 0x31, 0x23, 0x4F, 0xB1, 0x8A, 0x77, 0xF7,
|
||||||
|
0x38, 0xD6, 0x90, 0xAE, 0x04, 0x9F, 0xED, 0xD6, 0x69, 0x12, 0x26, 0x7F, 0xEC, 0xAE, 0xFC, 0x45,
|
||||||
|
0x01, 0x74, 0xD7, 0x6D, 0x9F, 0x9A, 0xA7, 0x75, 0x5A, 0x30, 0xCD, 0x90, 0xA9, 0xA5, 0x87, 0x4B,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -129,6 +129,8 @@ namespace GameRes.Formats.KAAS
|
|||||||
var pent = (PdImageEntry)entry;
|
var pent = (PdImageEntry)entry;
|
||||||
byte[] baseline = null;
|
byte[] baseline = null;
|
||||||
var dir = arc.Dir as List<Entry>;
|
var dir = arc.Dir as List<Entry>;
|
||||||
|
// actual baseline image index is hard-coded in game script
|
||||||
|
// we just look back at the first non-incremental image
|
||||||
for (int i = pent.Number-1; i >= 0; --i)
|
for (int i = pent.Number-1; i >= 0; --i)
|
||||||
{
|
{
|
||||||
var base_entry = dir[i];
|
var base_entry = dir[i];
|
||||||
|
@ -89,6 +89,7 @@ namespace GameRes.Formats.KiriKiri
|
|||||||
public Xp3Opener ()
|
public Xp3Opener ()
|
||||||
{
|
{
|
||||||
Signatures = new uint[] { 0x0d335058, 0 };
|
Signatures = new uint[] { 0x0d335058, 0 };
|
||||||
|
Extensions = new[] { "XP3", "EXE" };
|
||||||
ContainedFormats = new[] { "TLG", "BMP", "PNG", "JPEG", "OGG", "WAV", "TXT" };
|
ContainedFormats = new[] { "TLG", "BMP", "PNG", "JPEG", "OGG", "WAV", "TXT" };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ namespace GameRes.Formats.LiveMaker
|
|||||||
|
|
||||||
public VffOpener ()
|
public VffOpener ()
|
||||||
{
|
{
|
||||||
Extensions = new string[] { "dat" };
|
Extensions = new string[] { "dat", "exe" };
|
||||||
Signatures = new uint[] { 0x666676, 0 };
|
Signatures = new uint[] { 0x666676, 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,10 +198,10 @@ namespace GameRes.Formats.Macromedia
|
|||||||
if (t >= 0)
|
if (t >= 0)
|
||||||
{
|
{
|
||||||
string ext = new string (type_buf, 0, t+1);
|
string ext = new string (type_buf, 0, t+1);
|
||||||
return string.Format ("{0:X8}.{1}", id, ext.ToLowerInvariant());
|
return string.Format ("{0:D8}.{1}", id, ext.ToLowerInvariant());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return id.ToString ("X8");
|
return id.ToString ("D8");
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] ZlibUnpack (long offset, uint size, out int actual_size, int unpacked_size_hint = 0)
|
byte[] ZlibUnpack (long offset, uint size, out int actual_size, int unpacked_size_hint = 0)
|
||||||
|
@ -23,13 +23,13 @@
|
|||||||
// IN THE SOFTWARE.
|
// IN THE SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
using GameRes.Compression;
|
||||||
using GameRes.Utility;
|
using GameRes.Utility;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.Composition;
|
using System.ComponentModel.Composition;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
|
|
||||||
@ -46,12 +46,12 @@ namespace GameRes.Formats.Macromedia
|
|||||||
|
|
||||||
public DxrOpener ()
|
public DxrOpener ()
|
||||||
{
|
{
|
||||||
Extensions = new[] { "dxr", "cxt" };
|
Extensions = new[] { "dxr", "cxt", "cct", "dcr" };
|
||||||
Signatures = new[] { 0x52494658u, 0x58464952u };
|
Signatures = new[] { 0x52494658u, 0x58464952u };
|
||||||
}
|
}
|
||||||
|
|
||||||
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", //"snd "
|
||||||
};
|
};
|
||||||
|
|
||||||
internal bool ConvertText = true;
|
internal bool ConvertText = true;
|
||||||
@ -71,9 +71,9 @@ namespace GameRes.Formats.Macromedia
|
|||||||
|
|
||||||
var dir = new List<Entry> ();
|
var dir = new List<Entry> ();
|
||||||
ImportMedia (dir_file, dir);
|
ImportMedia (dir_file, dir);
|
||||||
foreach (MemoryMapEntry entry in dir_file.MMap.Dir)
|
foreach (DirectorEntry entry in dir_file.Directory)
|
||||||
{
|
{
|
||||||
if (entry.Size != 0 && 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 ("snd " == entry.FourCC)
|
||||||
@ -90,19 +90,30 @@ namespace GameRes.Formats.Macromedia
|
|||||||
var snd = entry as SoundEntry;
|
var snd = entry as SoundEntry;
|
||||||
if (snd != null)
|
if (snd != null)
|
||||||
return OpenSound (arc, snd);
|
return OpenSound (arc, snd);
|
||||||
var ment = entry as MemoryMapEntry;
|
var pent = entry as PackedEntry;
|
||||||
if (!ConvertText || null == ment || ment.FourCC != "STXT")
|
if (null == pent)
|
||||||
return base.OpenEntry (arc, entry);
|
return base.OpenEntry (arc, entry);
|
||||||
uint offset = Binary.BigEndian (arc.File.View.ReadUInt32 (entry.Offset));
|
var input = OpenChunkStream (arc.File, pent);
|
||||||
uint length = Binary.BigEndian (arc.File.View.ReadUInt32 (entry.Offset + 4));
|
var ment = entry as DirectorEntry;
|
||||||
return arc.File.CreateStream (entry.Offset + offset, length);
|
if (null == ment || !ConvertText || ment.FourCC != "STXT")
|
||||||
|
return input.AsStream;
|
||||||
|
using (input)
|
||||||
|
{
|
||||||
|
uint offset = Binary.BigEndian (input.ReadUInt32());
|
||||||
|
uint length = Binary.BigEndian (input.ReadUInt32());
|
||||||
|
input.Position = offset;
|
||||||
|
var text = input.ReadBytes ((int)length);
|
||||||
|
return new BinMemoryStream (text, entry.Name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Stream OpenSound (ArcFile arc, SoundEntry entry)
|
internal Stream OpenSound (ArcFile arc, SoundEntry entry)
|
||||||
{
|
{
|
||||||
if (null == entry.Header)
|
if (null == entry.Header)
|
||||||
return base.OpenEntry (arc, entry);
|
return base.OpenEntry (arc, entry);
|
||||||
var header = arc.File.View.ReadBytes (entry.Header.Offset, entry.Header.Size);
|
var header = new byte[entry.Header.UnpackedSize];
|
||||||
|
using (var input = OpenChunkStream (arc.File, entry.Header))
|
||||||
|
input.Read (header, 0, header.Length);
|
||||||
var format = entry.DeserializeHeader (header);
|
var format = entry.DeserializeHeader (header);
|
||||||
var riff = new MemoryStream (0x2C);
|
var riff = new MemoryStream (0x2C);
|
||||||
WaveAudio.WriteRiffHeader (riff, format, entry.Size);
|
WaveAudio.WriteRiffHeader (riff, format, entry.Size);
|
||||||
@ -110,12 +121,14 @@ namespace GameRes.Formats.Macromedia
|
|||||||
{
|
{
|
||||||
using (riff)
|
using (riff)
|
||||||
{
|
{
|
||||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
var input = OpenChunkStream (arc.File, entry).AsStream;
|
||||||
return new PrefixStream (riff.ToArray(), input);
|
return new PrefixStream (riff.ToArray(), input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// samples are stored in big-endian format
|
// samples are stored in big-endian format
|
||||||
var samples = arc.File.View.ReadBytes (entry.Offset, entry.Size);
|
var samples = new byte[entry.UnpackedSize];
|
||||||
|
using (var input = OpenChunkStream (arc.File, entry))
|
||||||
|
input.Read (samples, 0, samples.Length);
|
||||||
for (int i = 1; i < samples.Length; i += 2)
|
for (int i = 1; i < samples.Length; i += 2)
|
||||||
{
|
{
|
||||||
byte s = samples[i-1];
|
byte s = samples[i-1];
|
||||||
@ -130,7 +143,6 @@ namespace GameRes.Formats.Macromedia
|
|||||||
void ImportMedia (DirectorFile dir_file, List<Entry> dir)
|
void ImportMedia (DirectorFile dir_file, List<Entry> dir)
|
||||||
{
|
{
|
||||||
var seen_ids = new HashSet<int>();
|
var seen_ids = new HashSet<int>();
|
||||||
var mmap = dir_file.MMap;
|
|
||||||
foreach (var cast in dir_file.Casts)
|
foreach (var cast in dir_file.Casts)
|
||||||
{
|
{
|
||||||
foreach (var piece in cast.Members.Values)
|
foreach (var piece in cast.Members.Values)
|
||||||
@ -157,17 +169,35 @@ namespace GameRes.Formats.Macromedia
|
|||||||
{
|
{
|
||||||
if ("ediM" == elem.FourCC)
|
if ("ediM" == elem.FourCC)
|
||||||
{
|
{
|
||||||
var ediM = dir_file.MMap[elem.Id];
|
var ediM = dir_file.Index[elem.Id];
|
||||||
if (string.IsNullOrEmpty (name))
|
name = SanitizeName(name, ediM.Id);
|
||||||
name = ediM.Id.ToString ("D6");
|
return new PackedEntry
|
||||||
return new Entry
|
|
||||||
{
|
{
|
||||||
Name = name + ".mp3",
|
Name = name + ".ediM",
|
||||||
Type = "audio",
|
Type = "audio",
|
||||||
Offset = ediM.Offset,
|
Offset = ediM.Offset,
|
||||||
Size = ediM.Size,
|
Size = ediM.Size,
|
||||||
|
UnpackedSize = ediM.UnpackedSize,
|
||||||
|
IsPacked = ediM.IsPacked
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
else if ("snd " == elem.FourCC)
|
||||||
|
{
|
||||||
|
var snd = dir_file.Index[elem.Id];
|
||||||
|
if (snd.Size != 0)
|
||||||
|
{
|
||||||
|
name = SanitizeName (name, snd.Id);
|
||||||
|
return new PackedEntry
|
||||||
|
{
|
||||||
|
Name = name + ".snd",
|
||||||
|
Type = "audio",
|
||||||
|
Offset = snd.Offset,
|
||||||
|
Size = snd.Size,
|
||||||
|
UnpackedSize = snd.Size,
|
||||||
|
IsPacked = false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
if (null == sndHrec && "sndH" == elem.FourCC)
|
if (null == sndHrec && "sndH" == elem.FourCC)
|
||||||
sndHrec = elem;
|
sndHrec = elem;
|
||||||
else if (null == sndSrec && "sndS" == elem.FourCC)
|
else if (null == sndSrec && "sndS" == elem.FourCC)
|
||||||
@ -175,66 +205,100 @@ namespace GameRes.Formats.Macromedia
|
|||||||
}
|
}
|
||||||
if (sndHrec == null || sndSrec == null)
|
if (sndHrec == null || sndSrec == null)
|
||||||
return null;
|
return null;
|
||||||
var sndH = dir_file.MMap[sndHrec.Id];
|
var sndH = dir_file.Index[sndHrec.Id];
|
||||||
var sndS = dir_file.MMap[sndSrec.Id];
|
var sndS = dir_file.Index[sndSrec.Id];
|
||||||
if (string.IsNullOrEmpty (name))
|
name = SanitizeName (name, sndSrec.Id);
|
||||||
name = sndSrec.Id.ToString ("D6");
|
|
||||||
return new SoundEntry
|
return new SoundEntry
|
||||||
{
|
{
|
||||||
Name = name + ".snd",
|
Name = name + ".snd",
|
||||||
Type = "audio",
|
Type = "audio",
|
||||||
Offset = sndS.Offset,
|
Offset = sndS.Offset,
|
||||||
Size = sndS.Size,
|
Size = sndS.Size,
|
||||||
|
UnpackedSize = sndS.UnpackedSize,
|
||||||
|
IsPacked = sndS.IsPacked,
|
||||||
Header = sndH,
|
Header = sndH,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry ImportBitmap (CastMember bitmap, DirectorFile dir_file, Cast cast)
|
Entry ImportBitmap (CastMember bitmap, DirectorFile dir_file, Cast cast)
|
||||||
{
|
{
|
||||||
// var bitd = dir_file.KeyTable.FindByCast (bitmap.Id, "BITD");
|
KeyTableEntry bitd = null, edim = null, alfa = null;
|
||||||
KeyTableEntry bitd = null, alfa = null;
|
|
||||||
foreach (var elem in dir_file.KeyTable.Table.Where (e => e.CastId == bitmap.Id))
|
foreach (var elem in dir_file.KeyTable.Table.Where (e => e.CastId == bitmap.Id))
|
||||||
{
|
{
|
||||||
if (null == bitd && "BITD" == elem.FourCC)
|
if (null == bitd && "BITD" == elem.FourCC)
|
||||||
bitd = elem;
|
bitd = elem;
|
||||||
|
else if (null == edim && "ediM" == elem.FourCC)
|
||||||
|
edim = elem;
|
||||||
else if (null == alfa && "ALFA" == elem.FourCC)
|
else if (null == alfa && "ALFA" == elem.FourCC)
|
||||||
alfa = elem;
|
alfa = elem;
|
||||||
}
|
}
|
||||||
if (bitd == null)
|
if (bitd == null && edim == null)
|
||||||
return null;
|
return null;
|
||||||
var entry = new BitmapEntry();
|
var entry = new BitmapEntry();
|
||||||
|
if (bitd != null)
|
||||||
|
{
|
||||||
entry.DeserializeHeader (bitmap.SpecificData);
|
entry.DeserializeHeader (bitmap.SpecificData);
|
||||||
var name = bitmap.Info.Name;
|
var name = SanitizeName (bitmap.Info.Name, bitd.Id);
|
||||||
if (string.IsNullOrEmpty (name))
|
var chunk = dir_file.Index[bitd.Id];
|
||||||
name = bitd.Id.ToString ("D6");
|
|
||||||
var chunk = dir_file.MMap[bitd.Id];
|
|
||||||
entry.Name = name + ".BITD";
|
entry.Name = name + ".BITD";
|
||||||
entry.Type = "image";
|
entry.Type = "image";
|
||||||
entry.Offset = chunk.Offset;
|
entry.Offset = chunk.Offset;
|
||||||
entry.Size = chunk.Size;
|
entry.Size = chunk.Size;
|
||||||
|
entry.IsPacked = chunk.IsPacked;
|
||||||
|
entry.UnpackedSize = chunk.UnpackedSize;
|
||||||
if (entry.Palette > 0)
|
if (entry.Palette > 0)
|
||||||
{
|
{
|
||||||
var cast_id = cast.Index[entry.Palette-1];
|
var cast_id = cast.Index[entry.Palette-1];
|
||||||
var clut = dir_file.KeyTable.FindByCast (cast_id, "CLUT");
|
var clut = dir_file.KeyTable.FindByCast (cast_id, "CLUT");
|
||||||
if (clut != null)
|
if (clut != null)
|
||||||
entry.PaletteRef = dir_file.MMap[clut.Id];
|
entry.PaletteRef = dir_file.Index[clut.Id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // if (edim != null)
|
||||||
|
{
|
||||||
|
var name = SanitizeName (bitmap.Info.Name, edim.Id);
|
||||||
|
var chunk = dir_file.Index[edim.Id];
|
||||||
|
entry.Name = name + ".jpg";
|
||||||
|
entry.Type = "image";
|
||||||
|
entry.Offset = chunk.Offset;
|
||||||
|
entry.Size = chunk.Size;
|
||||||
|
entry.IsPacked = false;
|
||||||
|
entry.UnpackedSize = entry.Size;
|
||||||
}
|
}
|
||||||
if (alfa != null)
|
if (alfa != null)
|
||||||
entry.AlphaRef = dir_file.MMap[alfa.Id];
|
entry.AlphaRef = dir_file.Index[alfa.Id];
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static readonly Regex ForbiddenCharsRe = new Regex (@"[:?*<>/\\]");
|
||||||
|
|
||||||
|
string SanitizeName (string name, int id)
|
||||||
|
{
|
||||||
|
name = name?.Trim();
|
||||||
|
if (string.IsNullOrEmpty (name))
|
||||||
|
name = id.ToString ("D6");
|
||||||
|
else
|
||||||
|
name = ForbiddenCharsRe.Replace (name, "_");
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
|
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
|
||||||
{
|
{
|
||||||
var bent = entry as BitmapEntry;
|
var bent = entry as BitmapEntry;
|
||||||
if (null == bent)
|
if (null == bent)
|
||||||
return base.OpenImage(arc, entry);
|
return base.OpenImage(arc, entry);
|
||||||
|
if (entry.Name.HasExtension (".jpg"))
|
||||||
|
return OpenJpeg (arc, bent);
|
||||||
|
|
||||||
BitmapPalette palette = null;
|
BitmapPalette palette = null;
|
||||||
if (bent.PaletteRef != null)
|
if (bent.PaletteRef != null)
|
||||||
{
|
{
|
||||||
var pal_bytes = arc.File.View.ReadBytes (bent.PaletteRef.Offset, bent.PaletteRef.Size);
|
using (var pal = OpenChunkStream (arc.File, bent.PaletteRef))
|
||||||
|
{
|
||||||
|
var pal_bytes = pal.ReadBytes ((int)bent.PaletteRef.UnpackedSize);
|
||||||
palette = ReadPalette (pal_bytes);
|
palette = ReadPalette (pal_bytes);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (bent.BitDepth <= 8)
|
else if (bent.BitDepth <= 8)
|
||||||
{
|
{
|
||||||
switch (bent.Palette)
|
switch (bent.Palette)
|
||||||
@ -247,28 +311,54 @@ namespace GameRes.Formats.Macromedia
|
|||||||
case -101: palette = Palettes.SystemWindows; break;
|
case -101: palette = Palettes.SystemWindows; break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var info = new ImageMetaData {
|
var info = new BitdMetaData {
|
||||||
Width = (uint)(bent.Right - bent.Left),
|
Width = (uint)(bent.Right - bent.Left),
|
||||||
Height = (uint)(bent.Bottom - bent.Top),
|
Height = (uint)(bent.Bottom - bent.Top),
|
||||||
BPP = bent.BitDepth
|
BPP = bent.BitDepth,
|
||||||
|
DepthType = bent.DepthType,
|
||||||
};
|
};
|
||||||
byte[] alpha_channel = null;
|
byte[] alpha_channel = null;
|
||||||
if (bent.AlphaRef != null)
|
if (bent.AlphaRef != null)
|
||||||
|
alpha_channel = ReadAlphaChannel (arc.File, bent.AlphaRef, info);
|
||||||
|
var input = OpenChunkStream (arc.File, bent).AsStream;
|
||||||
|
return new BitdDecoder (input, info, palette) { AlphaChannel = alpha_channel };
|
||||||
|
}
|
||||||
|
|
||||||
|
IImageDecoder OpenJpeg (ArcFile arc, BitmapEntry entry)
|
||||||
{
|
{
|
||||||
using (var alpha = arc.File.CreateStream (bent.AlphaRef.Offset, bent.AlphaRef.Size))
|
if (null == entry.AlphaRef)
|
||||||
|
return base.OpenImage (arc, entry);
|
||||||
|
// jpeg with alpha-channel
|
||||||
|
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var alpha_info = new ImageMetaData {
|
var info = ImageFormat.Jpeg.ReadMetaData (input);
|
||||||
|
if (null == info)
|
||||||
|
throw new InvalidFormatException ("Invalid 'ediM' chunk.");
|
||||||
|
var alpha_channel = ReadAlphaChannel (arc.File, entry.AlphaRef, info);
|
||||||
|
return BitdDecoder.FromJpeg (input, info, alpha_channel);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
input.Dispose();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] ReadAlphaChannel (ArcView file, DirectorEntry entry, ImageMetaData info)
|
||||||
|
{
|
||||||
|
using (var alpha = OpenChunkStream (file, entry))
|
||||||
|
{
|
||||||
|
var alpha_info = new BitdMetaData {
|
||||||
Width = info.Width,
|
Width = info.Width,
|
||||||
Height = info.Height,
|
Height = info.Height,
|
||||||
BPP = 8,
|
BPP = 8,
|
||||||
|
DepthType = 0x80,
|
||||||
};
|
};
|
||||||
var decoder = new BitdDecoder (alpha, alpha_info, null);
|
var decoder = new BitdDecoder (alpha.AsStream, alpha_info, null);
|
||||||
alpha_channel = decoder.Unpack8bpp();
|
return decoder.Unpack8bpp();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
|
||||||
return new BitdDecoder (input.AsStream, info, palette) { AlphaChannel = alpha_channel };
|
|
||||||
}
|
|
||||||
|
|
||||||
BitmapPalette ReadPalette (byte[] data)
|
BitmapPalette ReadPalette (byte[] data)
|
||||||
{
|
{
|
||||||
@ -280,9 +370,20 @@ namespace GameRes.Formats.Macromedia
|
|||||||
}
|
}
|
||||||
return new BitmapPalette (colors);
|
return new BitmapPalette (colors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IBinaryStream OpenChunkStream (ArcView file, PackedEntry entry)
|
||||||
|
{
|
||||||
|
var input = file.CreateStream (entry.Offset, entry.Size);
|
||||||
|
if (!entry.IsPacked)
|
||||||
|
return input;
|
||||||
|
var data = new byte[entry.UnpackedSize];
|
||||||
|
using (var zstream = new ZLibStream (input, CompressionMode.Decompress))
|
||||||
|
zstream.Read (data, 0, data.Length);
|
||||||
|
return new BinMemoryStream (data, entry.Name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class BitmapEntry : Entry
|
internal class BitmapEntry : PackedEntry
|
||||||
{
|
{
|
||||||
public byte Flags;
|
public byte Flags;
|
||||||
public byte DepthType;
|
public byte DepthType;
|
||||||
@ -292,8 +393,8 @@ namespace GameRes.Formats.Macromedia
|
|||||||
public int Right;
|
public int Right;
|
||||||
public int BitDepth;
|
public int BitDepth;
|
||||||
public int Palette;
|
public int Palette;
|
||||||
public Entry PaletteRef;
|
public DirectorEntry PaletteRef;
|
||||||
public Entry AlphaRef;
|
public DirectorEntry AlphaRef;
|
||||||
|
|
||||||
public void DeserializeHeader (byte[] data)
|
public void DeserializeHeader (byte[] data)
|
||||||
{
|
{
|
||||||
@ -314,9 +415,9 @@ namespace GameRes.Formats.Macromedia
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class SoundEntry : Entry
|
internal class SoundEntry : PackedEntry
|
||||||
{
|
{
|
||||||
public Entry Header;
|
public DirectorEntry Header;
|
||||||
|
|
||||||
public WaveFormat DeserializeHeader (byte[] header)
|
public WaveFormat DeserializeHeader (byte[] header)
|
||||||
{
|
{
|
||||||
|
@ -31,10 +31,12 @@ namespace GameRes.Formats.Macromedia
|
|||||||
[Export(typeof(AudioFormat))]
|
[Export(typeof(AudioFormat))]
|
||||||
public class SndAudio : AudioFormat
|
public class SndAudio : AudioFormat
|
||||||
{
|
{
|
||||||
public override string Tag { get => "SND"; }
|
public override string Tag => "SND";
|
||||||
public override string Description { get => "Macromedia Director audio resource"; }
|
public override string Description => "Macromedia Director audio resource";
|
||||||
public override uint Signature { get => 0; }
|
public override uint Signature => 0;
|
||||||
public override bool CanWrite { get => false; }
|
public override bool CanWrite => false;
|
||||||
|
|
||||||
|
static readonly ResourceInstance<AudioFormat> Mp3 = new ResourceInstance<AudioFormat> ("MP3");
|
||||||
|
|
||||||
public override SoundInput TryOpen (IBinaryStream file)
|
public override SoundInput TryOpen (IBinaryStream file)
|
||||||
{
|
{
|
||||||
@ -83,6 +85,13 @@ namespace GameRes.Formats.Macromedia
|
|||||||
if (bps != 16 && bps != 8)
|
if (bps != 16 && bps != 8)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
// try mp3
|
||||||
|
var samples_stream = new StreamRegion (reader.Source, reader.Position);
|
||||||
|
var mp3_input = new BinaryStream (samples_stream, file.Name);
|
||||||
|
var mp3 = Mp3.Value.TryOpen (mp3_input);
|
||||||
|
if (mp3 != null)
|
||||||
|
return mp3;
|
||||||
|
|
||||||
var format = new WaveFormat {
|
var format = new WaveFormat {
|
||||||
FormatTag = 1,
|
FormatTag = 1,
|
||||||
Channels = channels,
|
Channels = channels,
|
||||||
@ -93,8 +102,7 @@ namespace GameRes.Formats.Macromedia
|
|||||||
format.SetBPS();
|
format.SetBPS();
|
||||||
if (8 == bps)
|
if (8 == bps)
|
||||||
{
|
{
|
||||||
var data = new StreamRegion (file.AsStream, file.Position);
|
return new RawPcmInput (samples_stream, format);
|
||||||
return new RawPcmInput (data, format);
|
|
||||||
}
|
}
|
||||||
int sample_count = frames_count * channels;
|
int sample_count = frames_count * channels;
|
||||||
var samples = file.ReadBytes (sample_count);
|
var samples = file.ReadBytes (sample_count);
|
||||||
|
@ -23,9 +23,11 @@
|
|||||||
// IN THE SOFTWARE.
|
// IN THE SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
using GameRes.Compression;
|
||||||
using GameRes.Utility;
|
using GameRes.Utility;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
@ -66,24 +68,53 @@ namespace GameRes.Formats.Macromedia
|
|||||||
|
|
||||||
internal class DirectorFile
|
internal class DirectorFile
|
||||||
{
|
{
|
||||||
|
List<DirectorEntry> m_dir;
|
||||||
|
Dictionary<int, DirectorEntry> m_index = new Dictionary<int, DirectorEntry>();
|
||||||
MemoryMap m_mmap = new MemoryMap();
|
MemoryMap m_mmap = new MemoryMap();
|
||||||
KeyTable m_keyTable = new KeyTable();
|
KeyTable m_keyTable = new KeyTable();
|
||||||
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[]>();
|
||||||
|
|
||||||
|
public bool IsAfterBurned { get; private set; }
|
||||||
|
|
||||||
public MemoryMap MMap => m_mmap;
|
public MemoryMap MMap => m_mmap;
|
||||||
public KeyTable KeyTable => m_keyTable;
|
public KeyTable KeyTable => m_keyTable;
|
||||||
public DirectorConfig Config => m_config;
|
public DirectorConfig Config => m_config;
|
||||||
public List<Cast> Casts => m_casts;
|
public List<Cast> Casts => m_casts;
|
||||||
|
public List<DirectorEntry> Directory => m_dir;
|
||||||
|
public Dictionary<int, DirectorEntry> Index => m_index;
|
||||||
|
|
||||||
|
public DirectorEntry Find (string four_cc) => Directory.Find (e => e.FourCC == four_cc);
|
||||||
|
|
||||||
|
public DirectorEntry FindById (int id)
|
||||||
|
{
|
||||||
|
DirectorEntry entry;
|
||||||
|
m_index.TryGetValue (id, out entry);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
public bool Deserialize (SerializationContext context, Reader reader)
|
public bool Deserialize (SerializationContext context, Reader reader)
|
||||||
{
|
{
|
||||||
reader.Position = 8;
|
reader.Position = 8;
|
||||||
string codec = reader.ReadFourCC();
|
string codec = reader.ReadFourCC();
|
||||||
if (codec != "MV93" && codec != "MC95")
|
if (codec == "MV93" || codec == "MC95")
|
||||||
|
{
|
||||||
|
if (!ReadMMap (context, reader))
|
||||||
return false;
|
return false;
|
||||||
return ReadMMap (context, reader)
|
}
|
||||||
&& ReadKeyTable (context, reader)
|
else if (codec == "FGDC" || codec == "FGDM")
|
||||||
|
{
|
||||||
|
IsAfterBurned = true;
|
||||||
|
if (!ReadAfterBurner (context, reader))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Trace.WriteLine (string.Format ("Unknown codec '{0}'", codec), "DXR");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return ReadKeyTable (context, reader)
|
||||||
&& ReadConfig (context, reader)
|
&& ReadConfig (context, reader)
|
||||||
&& ReadCasts (context, reader);
|
&& ReadCasts (context, reader);
|
||||||
}
|
}
|
||||||
@ -99,25 +130,129 @@ namespace GameRes.Formats.Macromedia
|
|||||||
return false;
|
return false;
|
||||||
reader.Position = mmap_pos + 8;
|
reader.Position = mmap_pos + 8;
|
||||||
MMap.Deserialize (context, reader);
|
MMap.Deserialize (context, reader);
|
||||||
|
m_dir = MMap.Dir;
|
||||||
|
for (int i = 0; i < m_dir.Count; ++i)
|
||||||
|
{
|
||||||
|
m_index[i] = m_dir[i];
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ReadAfterBurner (SerializationContext context, Reader reader)
|
||||||
|
{
|
||||||
|
if (reader.ReadFourCC() != "Fver")
|
||||||
|
return false;
|
||||||
|
int length = reader.ReadVarInt();
|
||||||
|
long next_pos = reader.Position + length;
|
||||||
|
int version = reader.ReadVarInt();
|
||||||
|
if (version > 0x400)
|
||||||
|
{
|
||||||
|
reader.ReadVarInt(); // imap version
|
||||||
|
reader.ReadVarInt(); // director version
|
||||||
|
}
|
||||||
|
if (version > 0x500)
|
||||||
|
{
|
||||||
|
int str_len = reader.ReadU8();
|
||||||
|
reader.Skip (str_len); // version string
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.Position = next_pos;
|
||||||
|
if (reader.ReadFourCC() != "Fcdr")
|
||||||
|
return false;
|
||||||
|
// skip compression table, assume everything is zlib-compressed
|
||||||
|
length = reader.ReadVarInt();
|
||||||
|
|
||||||
|
reader.Position += length;
|
||||||
|
if (reader.ReadFourCC() != "ABMP")
|
||||||
|
return false;
|
||||||
|
length = reader.ReadVarInt();
|
||||||
|
next_pos = reader.Position + length;
|
||||||
|
reader.ReadVarInt(); // compression type, index within 'Fcdr' compression table
|
||||||
|
int unpacked_size = reader.ReadVarInt();
|
||||||
|
using (var abmp = new ZLibStream (reader.Source, CompressionMode.Decompress, true))
|
||||||
|
{
|
||||||
|
var abmp_reader = new Reader (abmp, reader.ByteOrder);
|
||||||
|
if (!ReadABMap (context, abmp_reader))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.Position = next_pos;
|
||||||
|
if (reader.ReadFourCC() != "FGEI")
|
||||||
|
return false;
|
||||||
|
reader.ReadVarInt();
|
||||||
|
long base_offset = reader.Position;
|
||||||
|
foreach (var entry in m_dir)
|
||||||
|
{
|
||||||
|
m_index[entry.Id] = entry;
|
||||||
|
if (entry.Offset >= 0)
|
||||||
|
entry.Offset += base_offset;
|
||||||
|
}
|
||||||
|
var ils_chunk = FindById (2);
|
||||||
|
if (null == ils_chunk)
|
||||||
|
return false;
|
||||||
|
using (var ils = new ZLibStream (reader.Source, CompressionMode.Decompress, true))
|
||||||
|
{
|
||||||
|
uint pos = 0;
|
||||||
|
var ils_reader = new Reader (ils, reader.ByteOrder);
|
||||||
|
while (pos < ils_chunk.UnpackedSize)
|
||||||
|
{
|
||||||
|
int id = ils_reader.ReadVarInt();
|
||||||
|
var chunk = m_index[id];
|
||||||
|
m_ilsMap[id] = ils_reader.ReadBytes ((int)chunk.Size);
|
||||||
|
pos += ils_reader.GetVarIntLength ((uint)id) + chunk.Size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadABMap (SerializationContext context, Reader reader)
|
||||||
|
{
|
||||||
|
reader.ReadVarInt();
|
||||||
|
reader.ReadVarInt();
|
||||||
|
int count = reader.ReadVarInt();
|
||||||
|
m_dir = new List<DirectorEntry> (count);
|
||||||
|
for (int i = 0; i < count; ++ i)
|
||||||
|
{
|
||||||
|
var entry = new AfterBurnerEntry();
|
||||||
|
entry.Deserialize (context, reader);
|
||||||
|
m_dir.Add (entry);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Reader GetChunkReader (DirectorEntry chunk, Reader reader)
|
||||||
|
{
|
||||||
|
if (-1 == chunk.Offset)
|
||||||
|
{
|
||||||
|
byte[] chunk_data;
|
||||||
|
if (!m_ilsMap.TryGetValue (chunk.Id, out chunk_data))
|
||||||
|
throw new InvalidFormatException (string.Format ("Can't find chunk {0} in ILS", chunk.FourCC));
|
||||||
|
var input = new BinMemoryStream (chunk_data, null);
|
||||||
|
reader = new Reader (input, reader.ByteOrder);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reader.Position = chunk.Offset;
|
||||||
|
}
|
||||||
|
return reader;
|
||||||
|
}
|
||||||
|
|
||||||
bool ReadKeyTable (SerializationContext context, Reader reader)
|
bool ReadKeyTable (SerializationContext context, Reader reader)
|
||||||
{
|
{
|
||||||
var key_chunk = MMap.Find ("KEY*");
|
var key_chunk = Find ("KEY*");
|
||||||
if (null == key_chunk)
|
if (null == key_chunk)
|
||||||
return false;
|
return false;
|
||||||
reader.Position = key_chunk.Offset;
|
reader = GetChunkReader (key_chunk, reader);
|
||||||
KeyTable.Deserialize (context, reader);
|
KeyTable.Deserialize (context, reader);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ReadConfig (SerializationContext context, Reader reader)
|
bool ReadConfig (SerializationContext context, Reader reader)
|
||||||
{
|
{
|
||||||
var config_chunk = MMap.Find ("VWCF") ?? MMap.Find ("DRCF");
|
var config_chunk = Find ("VWCF") ?? Find ("DRCF");
|
||||||
if (null == config_chunk)
|
if (null == config_chunk)
|
||||||
return false;
|
return false;
|
||||||
reader.Position = config_chunk.Offset;
|
reader = GetChunkReader (config_chunk, reader);
|
||||||
Config.Deserialize (context, reader);
|
Config.Deserialize (context, reader);
|
||||||
context.Version = Config.Version;
|
context.Version = Config.Version;
|
||||||
return true;
|
return true;
|
||||||
@ -125,21 +260,23 @@ namespace GameRes.Formats.Macromedia
|
|||||||
|
|
||||||
bool ReadCasts (SerializationContext context, Reader reader)
|
bool ReadCasts (SerializationContext context, Reader reader)
|
||||||
{
|
{
|
||||||
|
Reader cas_reader;
|
||||||
if (context.Version > 1200)
|
if (context.Version > 1200)
|
||||||
{
|
{
|
||||||
var mcsl = MMap.Find ("MCsL");
|
var mcsl = Find ("MCsL");
|
||||||
if (mcsl != null)
|
if (mcsl != null)
|
||||||
{
|
{
|
||||||
reader.Position = mcsl.Offset;
|
var mcsl_reader = GetChunkReader (mcsl, reader);
|
||||||
var cast_list = new CastList();
|
var cast_list = new CastList();
|
||||||
cast_list.Deserialize (context, reader);
|
cast_list.Deserialize (context, mcsl_reader);
|
||||||
foreach (var entry in cast_list.Entries)
|
foreach (var entry in cast_list.Entries)
|
||||||
{
|
{
|
||||||
var key_entry = KeyTable.FindByCast (entry.Id, "CAS*");
|
var key_entry = KeyTable.FindByCast (entry.Id, "CAS*");
|
||||||
if (key_entry != null)
|
if (key_entry != null)
|
||||||
{
|
{
|
||||||
var mmap_entry = MMap[key_entry.Id];
|
var cas_entry = Index[key_entry.Id];
|
||||||
var cast = new Cast (context, reader, mmap_entry);
|
cas_reader = GetChunkReader (cas_entry, reader);
|
||||||
|
var cast = new Cast (context, cas_reader, cas_entry);
|
||||||
if (!PopulateCast (cast, context, reader, entry))
|
if (!PopulateCast (cast, context, reader, entry))
|
||||||
return false;
|
return false;
|
||||||
Casts.Add (cast);
|
Casts.Add (cast);
|
||||||
@ -148,11 +285,12 @@ namespace GameRes.Formats.Macromedia
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var cas_chunk = MMap.Find ("CAS*");
|
var cas_chunk = Find ("CAS*");
|
||||||
if (null == cas_chunk)
|
if (null == cas_chunk)
|
||||||
return false;
|
return false;
|
||||||
var new_entry = new CastListEntry { Name = "internal", Id = 0x400, MinMember = Config.MinMember };
|
var new_entry = new CastListEntry { Name = "internal", Id = 0x400, MinMember = Config.MinMember };
|
||||||
var new_cast = new Cast (context, reader, cas_chunk);
|
cas_reader = GetChunkReader (cas_chunk, reader);
|
||||||
|
var new_cast = new Cast (context, cas_reader, cas_chunk);
|
||||||
if (!PopulateCast (new_cast, context, reader, new_entry))
|
if (!PopulateCast (new_cast, context, reader, new_entry))
|
||||||
return false;
|
return false;
|
||||||
Casts.Add (new_cast);
|
Casts.Add (new_cast);
|
||||||
@ -162,30 +300,16 @@ namespace GameRes.Formats.Macromedia
|
|||||||
public bool PopulateCast (Cast cast, SerializationContext context, Reader reader, CastListEntry entry)
|
public bool PopulateCast (Cast cast, SerializationContext context, Reader reader, CastListEntry entry)
|
||||||
{
|
{
|
||||||
cast.Name = entry.Name;
|
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)
|
for (int i = 0; i < cast.Index.Length; ++i)
|
||||||
{
|
{
|
||||||
int chunk_id = cast.Index[i];
|
int chunk_id = cast.Index[i];
|
||||||
if (chunk_id > 0)
|
if (chunk_id > 0)
|
||||||
{
|
{
|
||||||
var chunk = MMap[chunk_id];
|
var chunk = this.Index[chunk_id];
|
||||||
var member = new CastMember();
|
var member = new CastMember();
|
||||||
member.Id = chunk_id;
|
member.Id = chunk_id;
|
||||||
reader.Position = chunk.Offset;
|
var cast_reader = GetChunkReader (chunk, reader);
|
||||||
member.Deserialize (context, reader);
|
member.Deserialize (context, cast_reader);
|
||||||
cast.Members[member.Id] = member;
|
cast.Members[member.Id] = member;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -293,11 +417,10 @@ namespace GameRes.Formats.Macromedia
|
|||||||
public string Name;
|
public string Name;
|
||||||
public Dictionary<int, CastMember> Members = new Dictionary<int, CastMember>();
|
public Dictionary<int, CastMember> Members = new Dictionary<int, CastMember>();
|
||||||
|
|
||||||
public Cast (SerializationContext context, Reader reader, MemoryMapEntry entry)
|
public Cast (SerializationContext context, Reader reader, DirectorEntry entry)
|
||||||
{
|
{
|
||||||
int count = (int)(entry.Size / 4);
|
int count = (int)(entry.Size / 4);
|
||||||
Index = new int[count];
|
Index = new int[count];
|
||||||
reader.Position = entry.Offset;
|
|
||||||
Deserialize (context, reader);
|
Deserialize (context, reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,54 +514,6 @@ namespace GameRes.Formats.Macromedia
|
|||||||
public int Id;
|
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
|
internal class DirectorConfig
|
||||||
{
|
{
|
||||||
public short Length;
|
public short Length;
|
||||||
@ -548,11 +623,9 @@ namespace GameRes.Formats.Macromedia
|
|||||||
public int ChunkCountMax;
|
public int ChunkCountMax;
|
||||||
public int ChunkCountUsed;
|
public int ChunkCountUsed;
|
||||||
public int FreeHead;
|
public int FreeHead;
|
||||||
public readonly List<MemoryMapEntry> Dir = new List<MemoryMapEntry>();
|
public readonly List<DirectorEntry> Dir = new List<DirectorEntry>();
|
||||||
|
|
||||||
public MemoryMapEntry this[int index] => Dir[index];
|
public DirectorEntry 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)
|
public void Deserialize (SerializationContext context, Reader reader)
|
||||||
{
|
{
|
||||||
@ -582,10 +655,14 @@ namespace GameRes.Formats.Macromedia
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class MemoryMapEntry : Entry
|
internal class DirectorEntry : PackedEntry
|
||||||
{
|
{
|
||||||
public int Id;
|
public int Id;
|
||||||
public string FourCC;
|
public string FourCC;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class MemoryMapEntry : DirectorEntry
|
||||||
|
{
|
||||||
public ushort Flags;
|
public ushort Flags;
|
||||||
|
|
||||||
public MemoryMapEntry (int id = 0)
|
public MemoryMapEntry (int id = 0)
|
||||||
@ -599,7 +676,25 @@ namespace GameRes.Formats.Macromedia
|
|||||||
Size = reader.ReadU32();
|
Size = reader.ReadU32();
|
||||||
Offset = reader.ReadU32() + 8;
|
Offset = reader.ReadU32() + 8;
|
||||||
Flags = reader.ReadU16();
|
Flags = reader.ReadU16();
|
||||||
int Next = reader.ReadI32();
|
reader.ReadI32(); // next
|
||||||
|
UnpackedSize = Size;
|
||||||
|
IsPacked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class AfterBurnerEntry : DirectorEntry
|
||||||
|
{
|
||||||
|
public int CompMethod;
|
||||||
|
|
||||||
|
public void Deserialize (SerializationContext context, Reader reader)
|
||||||
|
{
|
||||||
|
Id = reader.ReadVarInt();
|
||||||
|
Offset = reader.ReadVarInt();
|
||||||
|
Size = (uint)reader.ReadVarInt();
|
||||||
|
UnpackedSize = (uint)reader.ReadVarInt();
|
||||||
|
CompMethod = reader.ReadVarInt(); // assume zlib
|
||||||
|
FourCC = reader.ReadFourCC();
|
||||||
|
IsPacked = Size != UnpackedSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -619,7 +714,7 @@ namespace GameRes.Formats.Macromedia
|
|||||||
SetByteOrder (e);
|
SetByteOrder (e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Stream Source { get => m_input; }
|
public Stream Source => m_input;
|
||||||
|
|
||||||
public ByteOrder ByteOrder { get; private set; }
|
public ByteOrder ByteOrder { get; private set; }
|
||||||
|
|
||||||
@ -701,6 +796,32 @@ namespace GameRes.Formats.Macromedia
|
|||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int ReadVarInt ()
|
||||||
|
{
|
||||||
|
int n = 0;
|
||||||
|
for (int i = 0; i < 5; ++i)
|
||||||
|
{
|
||||||
|
int bits = m_input.ReadByte();
|
||||||
|
if (-1 == bits)
|
||||||
|
throw new EndOfStreamException();
|
||||||
|
n = n << 7 | bits & 0x7F;
|
||||||
|
if (0 == (bits & 0x80))
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
throw new InvalidFormatException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint GetVarIntLength (uint i)
|
||||||
|
{
|
||||||
|
uint n = 1;
|
||||||
|
while (i > 0x7F)
|
||||||
|
{
|
||||||
|
i >>= 7;
|
||||||
|
++n;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
public Reader CloneUnless (ByteOrder order)
|
public Reader CloneUnless (ByteOrder order)
|
||||||
{
|
{
|
||||||
if (this.ByteOrder != order)
|
if (this.ByteOrder != order)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
//! \file ImageBITD.cs
|
//! \file ImageBITD.cs
|
||||||
//! \date Fri Jun 26 07:45:01 2015
|
//! \date Fri Jun 26 07:45:01 2015
|
||||||
//! \brief Selen image format.
|
//! \brief Macromedia Director image format.
|
||||||
//
|
//
|
||||||
// Copyright (C) 2015 by morkt
|
// Copyright (C) 2015-2023 by morkt
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to
|
// of this software and associated documentation files (the "Software"), to
|
||||||
@ -34,174 +34,9 @@ using GameRes.Utility;
|
|||||||
|
|
||||||
namespace GameRes.Formats.Macromedia
|
namespace GameRes.Formats.Macromedia
|
||||||
{
|
{
|
||||||
[Export(typeof(ImageFormat))]
|
internal class BitdMetaData : ImageMetaData
|
||||||
public class BitdFormat : ImageFormat
|
|
||||||
{
|
{
|
||||||
public override string Tag { get { return "BITD"; } }
|
public byte DepthType;
|
||||||
public override string Description { get { return "Selen RLE-compressed bitmap"; } }
|
|
||||||
public override uint Signature { get { return 0; } }
|
|
||||||
|
|
||||||
public override ImageMetaData ReadMetaData (IBinaryStream stream)
|
|
||||||
{
|
|
||||||
if (stream.Length > 0xffffff)
|
|
||||||
return null;
|
|
||||||
var scanner = new BitdScanner (stream.AsStream);
|
|
||||||
return scanner.GetInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override ImageData Read (IBinaryStream stream, ImageMetaData info)
|
|
||||||
{
|
|
||||||
var reader = new BitdReader (stream.AsStream, info);
|
|
||||||
reader.Unpack();
|
|
||||||
return ImageData.Create (info, reader.Format, null, reader.Data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write (Stream file, ImageData image)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException ("BitdFormat.Write not implemented");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class BitdScanner
|
|
||||||
{
|
|
||||||
Stream m_input;
|
|
||||||
|
|
||||||
protected Stream Input { get { return m_input; } }
|
|
||||||
|
|
||||||
public BitdScanner (Stream input)
|
|
||||||
{
|
|
||||||
m_input = input;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int MaxScanLine = 2048;
|
|
||||||
|
|
||||||
public ImageMetaData GetInfo ()
|
|
||||||
{
|
|
||||||
int total = 0;
|
|
||||||
var scan_lines = new Dictionary<int, int>();
|
|
||||||
var key_lines = new List<int>();
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
int b = m_input.ReadByte();
|
|
||||||
if (-1 == b)
|
|
||||||
break;
|
|
||||||
int count = b;
|
|
||||||
if (b > 0x7f)
|
|
||||||
count = (byte)-(sbyte)b;
|
|
||||||
++count;
|
|
||||||
if (count > 0x7f)
|
|
||||||
return null;
|
|
||||||
if (b > 0x7f)
|
|
||||||
{
|
|
||||||
if (-1 == m_input.ReadByte())
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_input.Seek (count, SeekOrigin.Current);
|
|
||||||
|
|
||||||
key_lines.Clear();
|
|
||||||
key_lines.AddRange (scan_lines.Keys);
|
|
||||||
foreach (var line in key_lines)
|
|
||||||
{
|
|
||||||
int width = scan_lines[line];
|
|
||||||
if (width < count)
|
|
||||||
scan_lines.Remove (line);
|
|
||||||
else if (width == count)
|
|
||||||
scan_lines[line] = line;
|
|
||||||
else
|
|
||||||
scan_lines[line] = width - count;
|
|
||||||
}
|
|
||||||
|
|
||||||
total += count;
|
|
||||||
if (total <= MaxScanLine && total >= 8)
|
|
||||||
scan_lines[total] = total;
|
|
||||||
if (total > MaxScanLine && !scan_lines.Any())
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
int rem;
|
|
||||||
total = Math.DivRem (total, 4, out rem);
|
|
||||||
if (rem != 0)
|
|
||||||
return null;
|
|
||||||
var valid_lines = from line in scan_lines where line.Key == line.Value
|
|
||||||
orderby line.Key
|
|
||||||
select line.Key;
|
|
||||||
bool is_eof = -1 == m_input.ReadByte();
|
|
||||||
foreach (var width in valid_lines)
|
|
||||||
{
|
|
||||||
int height = Math.DivRem (total, width, out rem);
|
|
||||||
if (0 == rem)
|
|
||||||
{
|
|
||||||
return new ImageMetaData
|
|
||||||
{
|
|
||||||
Width = (uint)width,
|
|
||||||
Height = (uint)height,
|
|
||||||
BPP = 32,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class BitdReader : BitdScanner
|
|
||||||
{
|
|
||||||
byte[] m_output;
|
|
||||||
int m_width;
|
|
||||||
int m_height;
|
|
||||||
|
|
||||||
public byte[] Data { get { return m_output; } }
|
|
||||||
public PixelFormat Format { get; private set; }
|
|
||||||
|
|
||||||
public BitdReader (Stream input, ImageMetaData info) : base (input)
|
|
||||||
{
|
|
||||||
m_width = (int)info.Width;
|
|
||||||
m_height = (int)info.Height;
|
|
||||||
m_output = new byte[m_width * m_height * 4];
|
|
||||||
Format = PixelFormats.Bgra32;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Unpack ()
|
|
||||||
{
|
|
||||||
int stride = m_width * 4;
|
|
||||||
var scan_line = new byte[stride];
|
|
||||||
for (int line = 0; line < m_output.Length; line += stride)
|
|
||||||
{
|
|
||||||
int dst = 0;
|
|
||||||
while (dst < stride)
|
|
||||||
{
|
|
||||||
int b = Input.ReadByte();
|
|
||||||
if (-1 == b)
|
|
||||||
throw new InvalidFormatException ("Unexpected end of file");
|
|
||||||
int count = b;
|
|
||||||
if (b > 0x7f)
|
|
||||||
count = (byte)-(sbyte)b;
|
|
||||||
++count;
|
|
||||||
if (dst + count > stride)
|
|
||||||
throw new InvalidFormatException();
|
|
||||||
if (b > 0x7f)
|
|
||||||
{
|
|
||||||
b = Input.ReadByte();
|
|
||||||
if (-1 == b)
|
|
||||||
throw new InvalidFormatException ("Unexpected end of file");
|
|
||||||
for (int i = 0; i < count; ++i)
|
|
||||||
scan_line[dst++] = (byte)b;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Input.Read (scan_line, dst, count);
|
|
||||||
dst += count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dst = line;
|
|
||||||
for (int x = 0; x < m_width; ++x)
|
|
||||||
{
|
|
||||||
m_output[dst++] = scan_line[x+m_width*3];
|
|
||||||
m_output[dst++] = scan_line[x+m_width*2];
|
|
||||||
m_output[dst++] = scan_line[x+m_width];
|
|
||||||
m_output[dst++] = scan_line[x];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class BitdDecoder : IImageDecoder
|
internal class BitdDecoder : IImageDecoder
|
||||||
@ -216,13 +51,13 @@ namespace GameRes.Formats.Macromedia
|
|||||||
BitmapPalette m_palette;
|
BitmapPalette m_palette;
|
||||||
|
|
||||||
public Stream Source => m_input;
|
public Stream Source => m_input;
|
||||||
public ImageFormat SourceFormat => null;
|
public ImageFormat SourceFormat { get; private set; }
|
||||||
public ImageMetaData Info => m_info;
|
public ImageMetaData Info => m_info;
|
||||||
public ImageData Image => m_image ?? (m_image = GetImageData());
|
public ImageData Image => m_image ?? (m_image = GetImageData());
|
||||||
public PixelFormat Format { get; private set; }
|
public PixelFormat Format { get; private set; }
|
||||||
public byte[] AlphaChannel { get; set; }
|
public byte[] AlphaChannel { get; set; }
|
||||||
|
|
||||||
public BitdDecoder (Stream input, ImageMetaData info, BitmapPalette palette)
|
public BitdDecoder (Stream input, BitdMetaData info, BitmapPalette palette)
|
||||||
{
|
{
|
||||||
m_input = input;
|
m_input = input;
|
||||||
m_info = info;
|
m_info = info;
|
||||||
@ -235,22 +70,54 @@ namespace GameRes.Formats.Macromedia
|
|||||||
: info.BPP == 4 ? PixelFormats.Indexed4
|
: info.BPP == 4 ? PixelFormats.Indexed4
|
||||||
: info.BPP == 8 ? PixelFormats.Indexed8
|
: info.BPP == 8 ? PixelFormats.Indexed8
|
||||||
: info.BPP == 16 ? PixelFormats.Bgr555
|
: info.BPP == 16 ? PixelFormats.Bgr555
|
||||||
: PixelFormats.Bgr32;
|
: info.DepthType == 0x87 // i have no clue what this is
|
||||||
|
|| info.DepthType == 0x8A ? PixelFormats.Bgra32 // depth type 0x87/0x8A
|
||||||
|
: PixelFormats.Bgra32; // depth type 0x82/84/85/86/8C
|
||||||
m_palette = palette;
|
m_palette = palette;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BitdDecoder (Stream input, ImageMetaData info, byte[] alpha_channel)
|
||||||
|
{
|
||||||
|
m_input = input;
|
||||||
|
m_info = info;
|
||||||
|
m_width = info.iWidth;
|
||||||
|
m_height = info.iHeight;
|
||||||
|
m_stride = (m_width * m_info.BPP + 7) / 8;
|
||||||
|
Format = PixelFormats.Bgra32;
|
||||||
|
AlphaChannel = alpha_channel;
|
||||||
|
SourceFormat = ImageFormat.Jpeg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IImageDecoder FromJpeg (Stream input, ImageMetaData info, byte[] alpha_channel)
|
||||||
|
{
|
||||||
|
return new BitdDecoder (input, info, alpha_channel);
|
||||||
|
}
|
||||||
|
|
||||||
protected ImageData GetImageData ()
|
protected ImageData GetImageData ()
|
||||||
{
|
{
|
||||||
|
BitmapSource bitmap = null;
|
||||||
m_input.Position = 0;
|
m_input.Position = 0;
|
||||||
if (Info.BPP <= 8)
|
if (SourceFormat == ImageFormat.Jpeg)
|
||||||
|
{
|
||||||
|
var decoder = new JpegBitmapDecoder (m_input, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||||
|
bitmap = decoder.Frames[0];
|
||||||
|
if (null == AlphaChannel)
|
||||||
|
return new ImageData (bitmap, m_info);
|
||||||
|
}
|
||||||
|
else if (Info.BPP > 8)
|
||||||
|
UnpackChannels (Info.BPP / 8);
|
||||||
|
else if (m_output.Length != m_input.Length)
|
||||||
Unpack8bpp();
|
Unpack8bpp();
|
||||||
else
|
else
|
||||||
UnpackChannels (Info.BPP / 8);
|
m_input.Read (m_output, 0, m_output.Length);
|
||||||
|
|
||||||
if (AlphaChannel != null)
|
if (AlphaChannel != null)
|
||||||
{
|
{
|
||||||
if (Info.BPP != 32)
|
if (Info.BPP != 32 || bitmap != null)
|
||||||
{
|
{
|
||||||
BitmapSource bitmap = BitmapSource.Create (Info.iWidth, Info.iHeight, ImageData.DefaultDpiX, ImageData.DefaultDpiY, Format, m_palette, m_output, m_stride);
|
if (bitmap == null)
|
||||||
|
bitmap = BitmapSource.Create (m_width, m_height, ImageData.DefaultDpiX, ImageData.DefaultDpiY, Format, m_palette, m_output, m_stride);
|
||||||
|
if (Info.BPP != 32)
|
||||||
bitmap = new FormatConvertedBitmap (bitmap, PixelFormats.Bgr32, null, 0);
|
bitmap = new FormatConvertedBitmap (bitmap, PixelFormats.Bgr32, null, 0);
|
||||||
m_stride = bitmap.PixelWidth * 4;
|
m_stride = bitmap.PixelWidth * 4;
|
||||||
m_output = new byte[bitmap.PixelHeight * m_stride];
|
m_output = new byte[bitmap.PixelHeight * m_stride];
|
||||||
@ -324,7 +191,8 @@ namespace GameRes.Formats.Macromedia
|
|||||||
{
|
{
|
||||||
int b = m_input.ReadByte();
|
int b = m_input.ReadByte();
|
||||||
if (-1 == b)
|
if (-1 == b)
|
||||||
throw new InvalidFormatException ("Unexpected end of file");
|
break; // one in 5000 images somehow stumbles here
|
||||||
|
// throw new InvalidFormatException ("Unexpected end of file");
|
||||||
int count = b;
|
int count = b;
|
||||||
if (b > 0x7f)
|
if (b > 0x7f)
|
||||||
count = (byte)-(sbyte)b;
|
count = (byte)-(sbyte)b;
|
||||||
|
@ -32,7 +32,7 @@ using GameRes.Utility;
|
|||||||
namespace GameRes.Formats.Mebius
|
namespace GameRes.Formats.Mebius
|
||||||
{
|
{
|
||||||
[Export(typeof(AudioFormat))]
|
[Export(typeof(AudioFormat))]
|
||||||
[ExportMetadata("Priority", -1)]
|
[ExportMetadata("Priority", 1)]
|
||||||
public class KoeAudio : AudioFormat
|
public class KoeAudio : AudioFormat
|
||||||
{
|
{
|
||||||
public override string Tag { get { return "KOE/MEBIUS"; } }
|
public override string Tag { get { return "KOE/MEBIUS"; } }
|
||||||
@ -65,7 +65,67 @@ namespace GameRes.Formats.Mebius
|
|||||||
return new WaveInput (data);
|
return new WaveInput (data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static readonly Dictionary<string, byte[]> DefaultKeys = new Dictionary<string, byte[]> {
|
Dictionary<string, byte[]> DefaultKeys => SchemeMap["Mebinya!"];
|
||||||
|
|
||||||
|
static readonly Dictionary<string, Dictionary<string, byte[]>> SchemeMap =
|
||||||
|
new Dictionary<string, Dictionary<string, byte[]>> {
|
||||||
|
{ "Mebinya!", new Dictionary<string, byte[]> {
|
||||||
|
{ "koe", new byte[] {
|
||||||
|
0xCE, 0xC5, 0x94, 0xE8, 0xD5, 0x7F, 0xEB, 0xF4, 0x96, 0xCA, 0xAA, 0x80, 0xAC, 0x45, 0x60, 0x58,
|
||||||
|
0x71, 0x50, 0xDD, 0x72, 0x20, 0x39, 0x08, 0x73, 0xFE, 0x46, 0x07, 0xC5, 0x78, 0x77, 0xC0, 0x23,
|
||||||
|
0x49, 0x9F, 0xFC, 0xD1, 0x9A, 0x0F, 0x99, 0x7F, 0x3E, 0x7B, 0xAE, 0xF4, 0x66, 0xEE, 0x14, 0x94,
|
||||||
|
0x75, 0xD0, 0x0E, 0xD8, 0x64, 0x60, 0xB4, 0x3B, 0x40, 0x33, 0xC3, 0x4E, 0x40, 0x0E, 0xE4, 0x6C,
|
||||||
|
0x8D, 0x26, 0xBA, 0xB0, 0x17, 0xA5, 0x40, 0xB7, 0x27, 0x80, 0x79, 0x58, 0x92, 0xF8, 0x79, 0x3E,
|
||||||
|
0x2A, 0xDA, 0xC8, 0x29, 0xD3, 0x43, 0x66, 0xC0, 0xE5, 0x16, 0xAB, 0x25, 0x35, 0x68, 0x60, 0xC1,
|
||||||
|
0x77, 0x6E, 0x2B, 0x0E, 0x50, 0x58, 0xDC, 0xAE, 0xC5, 0x97, 0xE9, 0x27, 0xE1, 0xF3, 0x03, 0xA2,
|
||||||
|
0x43, 0x77, 0x13, 0xF0, 0xEC, 0x8C, 0x40, 0xB4, 0x7F, 0x62, 0x8B, 0x84, 0x40, 0x68, 0xAF, 0xD2,
|
||||||
|
0x10, 0xF2, 0xFE, 0x79, 0x3D, 0x63, 0x3D, 0xB4, 0x43, 0x65, 0xB8, 0x5F, 0x77, 0x13, 0x32, 0x56,
|
||||||
|
0xA4, 0x93, 0xC9, 0x3D, 0x9F, 0x89, 0xFE, 0x0B, 0xD0, 0x6C, 0x81, 0x2D, 0x3F, 0x94, 0xDD, 0x16,
|
||||||
|
0x1A, 0x12, 0x3A, 0x83, 0xC7, 0x26, 0xC3, 0xE0, 0xFE, 0xF1, 0xEC, 0x82, 0x6C, 0xAF, 0xA0, 0x30,
|
||||||
|
0xEB, 0xFD, 0x1A, 0xA1, 0xD0, 0xA9, 0xEC, 0x7A, 0x52, 0x6D, 0x83, 0xE4, 0x84, 0x97, 0x8F, 0x44,
|
||||||
|
0x89, 0x0E, 0xB7, 0xC1, 0x4F, 0xA1, 0x89, 0x8C, 0x09, 0xA6, 0xE5, 0x98, 0x4C, 0xC3, 0x7A, 0xCA,
|
||||||
|
0xE6, 0x6D, 0x06, 0xB7, 0x5B, 0x82, 0x6C, 0x02, 0x2E, 0x03, 0x57, 0xF3, 0xD6, 0x3D, 0x79, 0x5B,
|
||||||
|
0x87, 0x0E, 0xA2, 0x4E, 0xA6, 0xFE, 0xB8, 0x56, 0xA6, 0x55, 0xD3, 0x2B, 0x17, 0x6F, 0x7F, 0x84,
|
||||||
|
0x16, 0xF7, 0xE6, 0x99, 0x8A, 0x4E, 0x73, 0xDE, 0x45, 0x2E, 0x1A, 0xA6, 0xEF, 0x78, 0x67, 0x1A,
|
||||||
|
} },
|
||||||
|
{ "mse", new byte[] {
|
||||||
|
0x40, 0xBA, 0x96, 0x7E, 0x07, 0xE1, 0x92, 0x95, 0x7E, 0x95, 0x17, 0x47, 0x3D, 0x1C, 0x08, 0x94,
|
||||||
|
0x02, 0xA5, 0x39, 0x7D, 0x95, 0xCB, 0xD8, 0x57, 0x09, 0x52, 0x67, 0xFD, 0x86, 0x57, 0xFD, 0x81,
|
||||||
|
0x04, 0xB9, 0x70, 0x54, 0x14, 0xC7, 0x8E, 0xA5, 0xA0, 0x11, 0xF5, 0xE2, 0xC5, 0x6E, 0xDB, 0x01,
|
||||||
|
0xA8, 0x8C, 0xA9, 0x25, 0xEB, 0x98, 0xD6, 0xBA, 0xAD, 0xD9, 0x62, 0x00, 0xAE, 0x50, 0xCA, 0x3E,
|
||||||
|
0x04, 0xAA, 0xF7, 0x98, 0xF9, 0x2C, 0xAE, 0xA4, 0x11, 0xCE, 0xF8, 0xCC, 0xAD, 0xB8, 0x07, 0xA5,
|
||||||
|
0xE8, 0xDF, 0x28, 0x2A, 0xA1, 0xE4, 0x81, 0x1F, 0x35, 0x7B, 0x4C, 0x7F, 0xFA, 0x04, 0x75, 0x31,
|
||||||
|
0x77, 0x0D, 0xD1, 0x79, 0xD3, 0x68, 0x2C, 0xDB, 0x16, 0x27, 0xBB, 0xD5, 0x2A, 0xFB, 0x2C, 0xBC,
|
||||||
|
0xB1, 0x70, 0xE2, 0x1C, 0xA8, 0xF6, 0x1E, 0x53, 0xDA, 0xA0, 0x89, 0xED, 0xB9, 0x25, 0x0A, 0x55,
|
||||||
|
0x08, 0x01, 0x37, 0xE7, 0x6B, 0xB4, 0xDB, 0x18, 0xE2, 0x13, 0x6B, 0x8E, 0x25, 0x98, 0x40, 0x05,
|
||||||
|
0xE7, 0x32, 0x1F, 0x4B, 0xA9, 0x7C, 0xC8, 0x24, 0x51, 0x54, 0x16, 0xFD, 0x6F, 0xC8, 0x67, 0x2B,
|
||||||
|
0xD2, 0xCD, 0x78, 0x18, 0xC2, 0xB0, 0xB6, 0xAA, 0x25, 0xB2, 0x4E, 0xCD, 0x3A, 0xD7, 0x0D, 0x43,
|
||||||
|
0x64, 0xBD, 0x35, 0x52, 0xFC, 0x07, 0x70, 0x67, 0xBE, 0x48, 0xFB, 0xA9, 0xD2, 0x67, 0xC3, 0xB8,
|
||||||
|
0x6A, 0xDC, 0x76, 0x04, 0x0E, 0xDD, 0xD3, 0xEB, 0x7A, 0x49, 0x39, 0xAC, 0xBD, 0xE5, 0x31, 0xBB,
|
||||||
|
0x71, 0xCC, 0x91, 0x8A, 0xB1, 0x09, 0x57, 0xF3, 0x39, 0xD2, 0x5E, 0xAB, 0x4F, 0x5F, 0x24, 0x86,
|
||||||
|
0xD5, 0x3D, 0xA8, 0xE7, 0x36, 0x23, 0x21, 0x32, 0x76, 0x3C, 0x98, 0x0A, 0x34, 0x51, 0x1E, 0xB8,
|
||||||
|
0x51, 0x40, 0x34, 0x93, 0x0B, 0x5C, 0x94, 0x24, 0x50, 0x6A, 0x72, 0x85, 0x04, 0xF1, 0xE5, 0x20,
|
||||||
|
} },
|
||||||
|
{ "bgm", new byte[] {
|
||||||
|
0x16, 0x83, 0x0A, 0x4D, 0x6E, 0x39, 0xBF, 0xD8, 0x9C, 0x2B, 0x9E, 0x9F, 0xAE, 0x13, 0x8C, 0x63,
|
||||||
|
0xBE, 0x53, 0x95, 0x2E, 0x61, 0xB3, 0xFC, 0x26, 0x1C, 0xA5, 0xBF, 0x99, 0x69, 0x29, 0x3C, 0x99,
|
||||||
|
0xD7, 0x1E, 0x8B, 0xFD, 0xBD, 0x98, 0xC9, 0x12, 0x0E, 0x93, 0x5F, 0x59, 0x4E, 0x89, 0x7B, 0x26,
|
||||||
|
0xA7, 0x53, 0x50, 0xF1, 0xB6, 0x52, 0x5A, 0xA6, 0x6D, 0xCD, 0x20, 0xD9, 0xC3, 0x82, 0xCB, 0x21,
|
||||||
|
0xFD, 0x4D, 0x8B, 0xFA, 0x49, 0xEA, 0xC3, 0x7C, 0x81, 0x42, 0xEE, 0x38, 0xC3, 0xAB, 0xE0, 0x1A,
|
||||||
|
0xBD, 0x9F, 0xB4, 0x98, 0x4F, 0x59, 0x60, 0x8D, 0xEE, 0x41, 0x92, 0x87, 0xEB, 0x30, 0x2A, 0x66,
|
||||||
|
0xF4, 0x69, 0xA2, 0xA4, 0x0F, 0x53, 0xB6, 0x04, 0x4E, 0x4A, 0xB8, 0x9E, 0x8B, 0x23, 0xE0, 0xF8,
|
||||||
|
0xE6, 0xA2, 0x1F, 0xA4, 0x46, 0x9B, 0x34, 0x09, 0x33, 0xE3, 0x0B, 0x66, 0xB7, 0xCC, 0x1F, 0xA9,
|
||||||
|
0x1F, 0xEE, 0xF6, 0x1D, 0x42, 0x55, 0xE6, 0x19, 0x44, 0x61, 0xBA, 0xAE, 0x57, 0xFC, 0x6D, 0x08,
|
||||||
|
0xFE, 0x6B, 0x84, 0x5C, 0x69, 0x50, 0xD0, 0xCC, 0xC3, 0xBC, 0x92, 0x7C, 0x33, 0x59, 0x4D, 0x2D,
|
||||||
|
0x50, 0x00, 0x47, 0xCE, 0x4C, 0xDB, 0x7A, 0xB0, 0x25, 0x61, 0x07, 0x55, 0x8A, 0xAD, 0x50, 0x0B,
|
||||||
|
0xD3, 0x2D, 0x6C, 0xC9, 0x39, 0x94, 0x82, 0x0F, 0x9B, 0xF9, 0x45, 0x95, 0x1C, 0xBA, 0xA5, 0xB9,
|
||||||
|
0xD2, 0x60, 0xE3, 0xE3, 0xC7, 0x34, 0xAA, 0x43, 0x27, 0xC7, 0xC2, 0x3D, 0xBD, 0x8A, 0xA6, 0x4B,
|
||||||
|
0xA9, 0x3F, 0xEF, 0xBB, 0x6B, 0xE4, 0x6B, 0x89, 0x2A, 0xE9, 0xD1, 0xC0, 0xE5, 0x3A, 0xED, 0x1A,
|
||||||
|
0x61, 0xF9, 0xB3, 0xCC, 0x03, 0x0F, 0x82, 0xCD, 0x74, 0x36, 0x2A, 0xD8, 0x3E, 0x4E, 0xE0, 0x17,
|
||||||
|
0x37, 0x1B, 0x41, 0xC2, 0xE8, 0xA7, 0x81, 0x7C, 0xD3, 0x02, 0xFD, 0x51, 0xB4, 0x02, 0x43, 0x9E,
|
||||||
|
} },
|
||||||
|
} },
|
||||||
|
{ "Tomodachi Ijou Koibito Miman", new Dictionary<string, byte[]> {
|
||||||
{ "koe", new byte[] {
|
{ "koe", new byte[] {
|
||||||
0x15, 0xEE, 0x1F, 0x83, 0x32, 0x20, 0xF8, 0x17, 0x53, 0xE3, 0x7B, 0xC0, 0x6A, 0x75, 0x93, 0xA5,
|
0x15, 0xEE, 0x1F, 0x83, 0x32, 0x20, 0xF8, 0x17, 0x53, 0xE3, 0x7B, 0xC0, 0x6A, 0x75, 0x93, 0xA5,
|
||||||
0x79, 0x32, 0x36, 0x7A, 0x76, 0xC5, 0xF4, 0x06, 0xC5, 0x08, 0xF5, 0x1E, 0xE4, 0xD5, 0xED, 0x72,
|
0x79, 0x32, 0x36, 0x7A, 0x76, 0xC5, 0xF4, 0x06, 0xC5, 0x08, 0xF5, 0x1E, 0xE4, 0xD5, 0xED, 0x72,
|
||||||
@ -120,6 +180,7 @@ namespace GameRes.Formats.Mebius
|
|||||||
0x4C, 0x2C, 0x58, 0x3B, 0xA9, 0x7A, 0x51, 0x5C, 0xFD, 0xA5, 0xCF, 0x67, 0xB8, 0x34, 0x85, 0x3D,
|
0x4C, 0x2C, 0x58, 0x3B, 0xA9, 0x7A, 0x51, 0x5C, 0xFD, 0xA5, 0xCF, 0x67, 0xB8, 0x34, 0x85, 0x3D,
|
||||||
0x7D, 0x93, 0xE9, 0x7E, 0x9E, 0x6E, 0xC3, 0xB2, 0xB1, 0xD0, 0x5C, 0x83, 0x61, 0x6F, 0x27, 0x18,
|
0x7D, 0x93, 0xE9, 0x7E, 0x9E, 0x6E, 0xC3, 0xB2, 0xB1, 0xD0, 0x5C, 0x83, 0x61, 0x6F, 0x27, 0x18,
|
||||||
} },
|
} },
|
||||||
|
} },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ namespace GameRes.Formats.Neko
|
|||||||
static string[] s_known_dir_names = {
|
static string[] s_known_dir_names = {
|
||||||
"image/actor", "image/back", "image/mask", "image/visual", "image/actor/big",
|
"image/actor", "image/back", "image/mask", "image/visual", "image/actor/big",
|
||||||
"image/face", "image/actor/b", "image/actor/bb", "image/actor/s", "image/actor/ss",
|
"image/face", "image/actor/b", "image/actor/bb", "image/actor/s", "image/actor/ss",
|
||||||
"sound/bgm", "sound/env", "sound/se", "voice", "script", "system", "count",
|
"sound/bgm", "sound/env", "sound/se", "sound/bgv", "voice", "script", "system", "count",
|
||||||
};
|
};
|
||||||
|
|
||||||
static Lazy<string[]> s_known_file_names = new Lazy<string[]> (ReadNekoPackLst);
|
static Lazy<string[]> s_known_file_names = new Lazy<string[]> (ReadNekoPackLst);
|
||||||
|
103
ArcFormats/Psp/ArcQPK.cs
Normal file
103
ArcFormats/Psp/ArcQPK.cs
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
//! \file ArcQPK.cs
|
||||||
|
//! \date 2023 Sep 13
|
||||||
|
//! \brief PSP resource archive.
|
||||||
|
//
|
||||||
|
// 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.Compression;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.Composition;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace GameRes.Formats.Psp
|
||||||
|
{
|
||||||
|
[Export(typeof(ArchiveFormat))]
|
||||||
|
public class PakOpener : ArchiveFormat
|
||||||
|
{
|
||||||
|
public override string Tag => "QPK";
|
||||||
|
public override string Description => "PSP resource archive";
|
||||||
|
public override uint Signature => 0x4B5051; // 'QPK'
|
||||||
|
public override bool IsHierarchic => false;
|
||||||
|
public override bool CanWrite => false;
|
||||||
|
|
||||||
|
public override ArcFile TryOpen (ArcView file)
|
||||||
|
{
|
||||||
|
var index_name = Path.ChangeExtension (file.Name, "QPI");
|
||||||
|
List<Entry> dir;
|
||||||
|
using (var index = VFS.OpenView (index_name))
|
||||||
|
{
|
||||||
|
if (!index.View.AsciiEqual (0, "QPI\0"))
|
||||||
|
return null;
|
||||||
|
int count = index.View.ReadInt32 (4);
|
||||||
|
if (!IsSaneCount (count))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var base_name = Path.GetFileNameWithoutExtension (file.Name);
|
||||||
|
string ext = "";
|
||||||
|
string type = "";
|
||||||
|
if ("TGA" == base_name)
|
||||||
|
{
|
||||||
|
ext = ".tga";
|
||||||
|
type = "image";
|
||||||
|
}
|
||||||
|
dir = new List<Entry> (count);
|
||||||
|
uint index_pos = 0x1C;
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
uint offset = index.View.ReadUInt32 (index_pos);
|
||||||
|
uint size = index.View.ReadUInt32 (index_pos+4);
|
||||||
|
if (offset > file.MaxOffset)
|
||||||
|
return null;
|
||||||
|
index_pos += 8;
|
||||||
|
if ((size & 0x80000000) != 0 || size == 0)
|
||||||
|
continue;
|
||||||
|
var entry = new PackedEntry {
|
||||||
|
Name = string.Format ("{0}#{1:D5}{2}", base_name, i, ext),
|
||||||
|
Type = type,
|
||||||
|
Offset = offset,
|
||||||
|
UnpackedSize = size & 0x3FFFFFFF,
|
||||||
|
IsPacked = (size & 0x40000000) != 0,
|
||||||
|
};
|
||||||
|
dir.Add (entry);
|
||||||
|
}
|
||||||
|
long last_offset = file.MaxOffset;
|
||||||
|
for (int i = dir.Count - 1; i >= 0; --i)
|
||||||
|
{
|
||||||
|
dir[i].Size = (uint)(last_offset - dir[i].Offset);
|
||||||
|
last_offset = dir[i].Offset;
|
||||||
|
}
|
||||||
|
return new ArcFile (file, this, dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||||
|
{
|
||||||
|
var pent = (PackedEntry)entry;
|
||||||
|
if (!pent.IsPacked || !arc.File.View.AsciiEqual (pent.Offset, "CZL\0"))
|
||||||
|
return base.OpenEntry (arc, pent);
|
||||||
|
uint size = arc.File.View.ReadUInt32 (pent.Offset+4);
|
||||||
|
var input = arc.File.CreateStream (pent.Offset+12, size);
|
||||||
|
return new ZLibStream (input, CompressionMode.Decompress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -133,7 +133,7 @@ namespace GameRes.Formats.Jikkenshitsu
|
|||||||
return new GUI.WidgetSJDAT (DefaultScheme.KnownSchemes.Keys);
|
return new GUI.WidgetSJDAT (DefaultScheme.KnownSchemes.Keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] QueryKey (string filename)
|
internal byte[] QueryKey (string filename)
|
||||||
{
|
{
|
||||||
var options = Query<SjOptions> (arcStrings.ArcImageEncrypted);
|
var options = Query<SjOptions> (arcStrings.ArcImageEncrypted);
|
||||||
return options.Key;
|
return options.Key;
|
||||||
|
@ -28,6 +28,8 @@ using System.IO;
|
|||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
|
|
||||||
|
// [020412][Ciel] Maid Hunter Zero One ~Nora Maid~
|
||||||
|
|
||||||
namespace GameRes.Formats.Jikkenshitsu
|
namespace GameRes.Formats.Jikkenshitsu
|
||||||
{
|
{
|
||||||
internal class GrcMetaData : ImageMetaData
|
internal class GrcMetaData : ImageMetaData
|
||||||
@ -38,6 +40,8 @@ namespace GameRes.Formats.Jikkenshitsu
|
|||||||
public int DataLength;
|
public int DataLength;
|
||||||
public int AlphaOffset;
|
public int AlphaOffset;
|
||||||
public int AlphaLength;
|
public int AlphaLength;
|
||||||
|
public bool IsEncrypted;
|
||||||
|
public byte[] Key;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Export(typeof(ImageFormat))]
|
[Export(typeof(ImageFormat))]
|
||||||
@ -47,15 +51,24 @@ namespace GameRes.Formats.Jikkenshitsu
|
|||||||
public override string Description { get { return "Studio Jikkenshitsu image format"; } }
|
public override string Description { get { return "Studio Jikkenshitsu image format"; } }
|
||||||
public override uint Signature { get { return 0x08; } }
|
public override uint Signature { get { return 0x08; } }
|
||||||
|
|
||||||
|
public GrcFormat ()
|
||||||
|
{
|
||||||
|
Signatures = new[] { 0x08u, 0x8008u };
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly ResourceInstance<SpDatFormat> SpeedFormat = new ResourceInstance<SpDatFormat> ("DAT/SPEED");
|
||||||
|
|
||||||
|
byte[] DefaultKey = null;
|
||||||
|
|
||||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||||
{
|
{
|
||||||
if (!file.Name.HasExtension (".grc"))
|
if (!file.Name.HasExtension (".grc"))
|
||||||
return null;
|
return null;
|
||||||
var header = file.ReadHeader (0x20);
|
var header = file.ReadHeader (0x20);
|
||||||
int bpp = header.ToInt32 (0);
|
int bpp = header[0];
|
||||||
if (bpp != 8)
|
if (bpp != 8)
|
||||||
return null;
|
return null;
|
||||||
return new GrcMetaData {
|
var info = new GrcMetaData {
|
||||||
Width = header.ToUInt16 (4),
|
Width = header.ToUInt16 (4),
|
||||||
Height = header.ToUInt16 (6),
|
Height = header.ToUInt16 (6),
|
||||||
BPP = bpp,
|
BPP = bpp,
|
||||||
@ -65,7 +78,16 @@ namespace GameRes.Formats.Jikkenshitsu
|
|||||||
DataLength = header.ToInt32 (20),
|
DataLength = header.ToInt32 (20),
|
||||||
AlphaOffset = header.ToInt32 (24),
|
AlphaOffset = header.ToInt32 (24),
|
||||||
AlphaLength = header.ToInt32 (28),
|
AlphaLength = header.ToInt32 (28),
|
||||||
|
IsEncrypted = (header[1] & 0x80) != 0,
|
||||||
};
|
};
|
||||||
|
if (info.IsEncrypted)
|
||||||
|
{
|
||||||
|
DefaultKey = DefaultKey ?? SpeedFormat.Value.QueryKey (file.Name);
|
||||||
|
if (null == DefaultKey)
|
||||||
|
return null;
|
||||||
|
info.Key = DefaultKey;
|
||||||
|
}
|
||||||
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||||
@ -96,12 +118,25 @@ namespace GameRes.Formats.Jikkenshitsu
|
|||||||
m_info = info;
|
m_info = info;
|
||||||
m_stride = m_info.iWidth * m_info.BPP / 8;
|
m_stride = m_info.iWidth * m_info.BPP / 8;
|
||||||
m_output = new byte[m_stride * m_info.iHeight];
|
m_output = new byte[m_stride * m_info.iHeight];
|
||||||
|
Format = PixelFormats.Indexed8;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImageData Unpack ()
|
public ImageData Unpack ()
|
||||||
{
|
{
|
||||||
|
if (m_info.IsEncrypted)
|
||||||
|
{
|
||||||
|
int packed_size = (int)(m_input.Length - 0x20);
|
||||||
|
m_input.Position = 0x20;
|
||||||
|
using (var enc = new InputProxyStream (m_input.AsStream, true))
|
||||||
|
using (var dec = new InputCryptoStream (enc, new SjTransform (m_info.Key)))
|
||||||
|
{
|
||||||
|
var data = new byte[m_input.Length];
|
||||||
|
dec.Read (data, 0x20, packed_size);
|
||||||
|
// memory stream is not disposed, not a big deal
|
||||||
|
m_input = new BinMemoryStream (data, m_input.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
m_input.Position = 0x20;
|
m_input.Position = 0x20;
|
||||||
Format = PixelFormats.Indexed8;
|
|
||||||
|
|
||||||
if (8 == m_info.BPP)
|
if (8 == m_info.BPP)
|
||||||
Palette = ImageFormat.ReadPalette (m_input.AsStream);
|
Palette = ImageFormat.ReadPalette (m_input.AsStream);
|
||||||
|
Loading…
Reference in New Issue
Block a user