mirror of
https://github.com/crskycode/GARbro.git
synced 2024-11-23 05:35:34 +08:00
bunch of stuff.
This commit is contained in:
parent
ea096c52ef
commit
77fde27d26
255
ArcFormats/AIRNovel/ArcAIR.cs
Normal file
255
ArcFormats/AIRNovel/ArcAIR.cs
Normal file
@ -0,0 +1,255 @@
|
||||
//! \file ArcAIR.cs
|
||||
//! \date 2022 Jun 10
|
||||
//! \brief AIRNovel engine encrypted resources.
|
||||
//
|
||||
// Copyright (C) 2022 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;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using GameRes.Cryptography;
|
||||
using GameRes.Formats.PkWare;
|
||||
|
||||
using SharpZip = ICSharpCode.SharpZipLib.Zip;
|
||||
|
||||
namespace GameRes.Formats.AirNovel
|
||||
{
|
||||
internal class AirEntry : ZipEntry
|
||||
{
|
||||
public bool IsEncrypted { get; set; }
|
||||
|
||||
public AirEntry (SharpZip.ZipEntry zip_entry) : base (zip_entry)
|
||||
{
|
||||
IsEncrypted = Name.EndsWith ("_");
|
||||
if (IsEncrypted)
|
||||
{
|
||||
Name = Name.Substring (0, Name.Length-1);
|
||||
Type = FormatCatalog.Instance.GetTypeFromName (Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class AirArchive : PkZipArchive
|
||||
{
|
||||
public byte[] Key;
|
||||
public uint CoderLength;
|
||||
|
||||
public AirArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, SharpZip.ZipFile native, byte[] key) : base (arc, impl, dir, native)
|
||||
{
|
||||
Key = key;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class AirNovelScheme : ResourceScheme
|
||||
{
|
||||
public IDictionary<string, string> KnownKeys;
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class AirOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "AIR"; } }
|
||||
public override string Description { get { return "Adobe AIR resource archive"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override bool IsHierarchic { get { return true; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
static readonly ResourceInstance<ArchiveFormat> Zip = new ResourceInstance<ArchiveFormat> ("ZIP");
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.Name.HasExtension (".air"))
|
||||
return null;
|
||||
var input = file.CreateStream();
|
||||
SharpZip.ZipFile zip = null;
|
||||
try
|
||||
{
|
||||
SharpZip.ZipStrings.CodePage = Encoding.UTF8.CodePage;
|
||||
zip = new SharpZip.ZipFile (input);
|
||||
var files = zip.Cast<SharpZip.ZipEntry>().Where (z => !z.IsDirectory);
|
||||
bool has_encrypted = false;
|
||||
var dir = new List<Entry>();
|
||||
foreach (var f in files)
|
||||
{
|
||||
var entry = new AirEntry (f);
|
||||
has_encrypted |= entry.IsEncrypted;
|
||||
dir.Add (entry);
|
||||
}
|
||||
if (has_encrypted)
|
||||
{
|
||||
uint coder_length;
|
||||
if (FindAirNovelCoderLength (zip, out coder_length))
|
||||
{
|
||||
var key = QueryEncryptionKey (file);
|
||||
if (!string.IsNullOrEmpty (key))
|
||||
{
|
||||
var rc4_key = AirRc4Crypt.GenerateKey (key);
|
||||
return new AirArchive (file, this, dir, zip, rc4_key) { CoderLength = coder_length };
|
||||
}
|
||||
}
|
||||
}
|
||||
return new PkZipArchive (file, Zip.Value, dir, zip);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (zip != null)
|
||||
zip.Close();
|
||||
input.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var zarc = (AirArchive)arc;
|
||||
var zent = (AirEntry)entry;
|
||||
var input = zarc.Native.GetInputStream (zent.NativeEntry);
|
||||
if (!zent.IsEncrypted)
|
||||
return input;
|
||||
var data = new byte[zent.UnpackedSize];
|
||||
using (input)
|
||||
input.Read (data, 0, data.Length);
|
||||
int enc_len;
|
||||
if (zent.Name.HasExtension (".an"))
|
||||
enc_len = data.Length;
|
||||
else
|
||||
enc_len = Math.Min (data.Length, (int)zarc.CoderLength);
|
||||
var rc4 = new AirRc4Crypt (zarc.Key);
|
||||
rc4.Decrypt (data, 0, enc_len);
|
||||
return new BinMemoryStream (data, entry.Name);
|
||||
}
|
||||
|
||||
bool FindAirNovelCoderLength (SharpZip.ZipFile zip, out uint coder_length)
|
||||
{
|
||||
coder_length = 0;
|
||||
var config = zip.GetEntry ("config.anprj");
|
||||
if (null == config)
|
||||
return false;
|
||||
using (var input = zip.GetInputStream (config))
|
||||
{
|
||||
var coder = FindConfigNode (input, "/config/coder[@len]");
|
||||
if (null == coder)
|
||||
return false;
|
||||
var lenAttr = coder.Attributes["len"].Value;
|
||||
var styles = NumberStyles.Integer;
|
||||
if (lenAttr.StartsWith ("0x"))
|
||||
{
|
||||
lenAttr = lenAttr.Substring (2, lenAttr.Length-2);
|
||||
styles = NumberStyles.HexNumber;
|
||||
}
|
||||
return UInt32.TryParse (lenAttr, styles, CultureInfo.InvariantCulture, out coder_length);
|
||||
}
|
||||
}
|
||||
|
||||
XmlNode FindConfigNode (Stream input, string xpath)
|
||||
{
|
||||
using (var reader = new StreamReader (input))
|
||||
{
|
||||
var xml = new XmlDocument();
|
||||
xml.Load (reader);
|
||||
return xml.DocumentElement.SelectSingleNode (xpath);
|
||||
}
|
||||
}
|
||||
|
||||
AirNovelScheme DefaultScheme = new AirNovelScheme { KnownKeys = new Dictionary<string, string>() };
|
||||
|
||||
internal IDictionary<string, string> KnownKeys { get { return DefaultScheme.KnownKeys; } }
|
||||
|
||||
public override ResourceScheme Scheme
|
||||
{
|
||||
get { return DefaultScheme; }
|
||||
set { DefaultScheme = (AirNovelScheme)value; }
|
||||
}
|
||||
|
||||
string QueryEncryptionKey (ArcView file)
|
||||
{
|
||||
var title = FormatCatalog.Instance.LookupGame (file.Name);
|
||||
if (string.IsNullOrEmpty (title))
|
||||
return null;
|
||||
string key;
|
||||
if (!KnownKeys.TryGetValue (title, out key))
|
||||
return null;
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
internal class AirRc4Crypt
|
||||
{
|
||||
const int KeyLength = 0xFF;
|
||||
|
||||
byte[] KeyState = new byte[KeyLength+1];
|
||||
|
||||
public AirRc4Crypt (byte[] key)
|
||||
{
|
||||
for (int i = 0; i <= KeyLength; ++i)
|
||||
{
|
||||
KeyState[i] = (byte)i;
|
||||
}
|
||||
int j = 0;
|
||||
for (int i = 0; i <= KeyLength; ++i)
|
||||
{
|
||||
j = (j + KeyState[i] + key[i]) & KeyLength;
|
||||
KeyState[i] ^= KeyState[j];
|
||||
KeyState[j] ^= KeyState[i];
|
||||
KeyState[i] ^= KeyState[j];
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] GenerateKey (string passPhrase)
|
||||
{
|
||||
if (string.IsNullOrEmpty (passPhrase))
|
||||
throw new ArgumentException ("passPhrase");
|
||||
var key = new byte[KeyLength+1];
|
||||
for (int i = 0; i < key.Length; ++i)
|
||||
{
|
||||
key[i] = (byte)passPhrase[i % passPhrase.Length];
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
public void Decrypt (byte[] data, int pos, int length)
|
||||
{
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
var keyCopy = KeyState.Clone() as byte[];
|
||||
int last = Math.Min (pos + length, data.Length);
|
||||
while (pos < last)
|
||||
{
|
||||
i = (i + 1) & KeyLength;
|
||||
j = (j + keyCopy[i]) & KeyLength;
|
||||
// i was wondering why standard RC4 encryption class doesn't work here
|
||||
// well, this swap fails when i == j lol
|
||||
keyCopy[i] ^= keyCopy[j];
|
||||
keyCopy[j] ^= keyCopy[i];
|
||||
keyCopy[i] ^= keyCopy[j];
|
||||
int k = (keyCopy[i] + keyCopy[j]) & KeyLength;
|
||||
data[pos++] ^= keyCopy[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -78,4 +78,9 @@ namespace GameRes.Formats.Abel
|
||||
[ExportMetadata("Extension", "ALP")]
|
||||
[ExportMetadata("Target", "DAT/GENERIC")]
|
||||
public class AlpFormat : ResourceAlias { }
|
||||
|
||||
[Export(typeof(ResourceAlias))]
|
||||
[ExportMetadata("Extension", "JCP")]
|
||||
[ExportMetadata("Target", "WAV")]
|
||||
public class JcpFormat : ResourceAlias { }
|
||||
}
|
||||
|
@ -24,7 +24,6 @@
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using GameRes.Compression;
|
||||
@ -50,14 +49,16 @@ namespace GameRes.Formats.Abel
|
||||
|
||||
public GpsFormat ()
|
||||
{
|
||||
Extensions = new string[] { "gps", "cmp" };
|
||||
Extensions = new[] { "gps", "gp2", "cmp" };
|
||||
Signatures = new[] { 0x535047u, 0x325047u }; // 'GPS', 'GP2'
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x29);
|
||||
bool is_gp2 = header.AsciiEqual ("GP2");
|
||||
var gps = new GpsMetaData();
|
||||
if (header.ToUInt32 (4) == 0xCCCCCCCC)
|
||||
if (!is_gp2 && header.ToUInt32 (4) == 0xCCCCCCCC)
|
||||
{
|
||||
gps.HeaderSize = 0x19;
|
||||
gps.Compression = 2;
|
||||
@ -69,6 +70,8 @@ namespace GameRes.Formats.Abel
|
||||
gps.HeaderSize = 0x29;
|
||||
gps.Compression = header[0x10];
|
||||
gps.UnpackedSize = header.ToInt32 (0x11);
|
||||
if (is_gp2)
|
||||
gps.UnpackedSize = - 1 - gps.UnpackedSize;
|
||||
gps.PackedSize = header.ToInt32 (0x15);
|
||||
gps.Width = header.ToUInt32 (0x19);
|
||||
gps.Height = header.ToUInt32 (0x1D);
|
||||
|
@ -53,6 +53,7 @@ namespace GameRes.Formats.Abogado
|
||||
{ "PCM1", "ADP" },
|
||||
{ "PCM2", "ADP" },
|
||||
{ "PCM", "ADP" },
|
||||
{ "ADPCM", "ADP" },
|
||||
{ "GRAPHIC", "KG" },
|
||||
{ "GRPFILE", "KG" },
|
||||
{ "EFCFILE", "ADP" },
|
||||
|
118
ArcFormats/Adobe/ArcAIR.cs
Normal file
118
ArcFormats/Adobe/ArcAIR.cs
Normal file
@ -0,0 +1,118 @@
|
||||
//! \file ArcAIR.cs
|
||||
//! \date 2023 Aug 22
|
||||
//! \brief Adobe AIR 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.Utility;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
|
||||
namespace GameRes.Formats.Adobe
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class DatOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get => "DAT/AIR"; }
|
||||
public override string Description { get => "Adobe AIR resource archive"; }
|
||||
public override uint Signature { get => 0; }
|
||||
public override bool IsHierarchic { get => false; }
|
||||
public override bool CanWrite { get => false; }
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
uint index_pos = Binary.BigEndian (file.View.ReadUInt32 (0));
|
||||
if (index_pos >= file.MaxOffset || 0 == index_pos || file.MaxOffset > 0x40000000)
|
||||
return null;
|
||||
uint index_size = (uint)(file.MaxOffset - index_pos);
|
||||
if (index_size > 0x100000) // arbitrary max size for compressed index
|
||||
return null;
|
||||
using (var input = file.CreateStream (index_pos, index_size))
|
||||
using (var unpacked = new DeflateStream (input, CompressionMode.Decompress))
|
||||
using (var index = new BinaryStream (unpacked, file.Name))
|
||||
{
|
||||
if (0x0A != index.ReadUInt8() ||
|
||||
0x0B != index.ReadUInt8() ||
|
||||
0x01 != index.ReadUInt8())
|
||||
return null;
|
||||
var name_buffer = new byte[0x80];
|
||||
var dir = new List<Entry>();
|
||||
while (index.PeekByte() != -1)
|
||||
{
|
||||
int length = index.ReadUInt8();
|
||||
if (0 == (length & 1))
|
||||
return null;
|
||||
length >>= 1;
|
||||
if (0 == length)
|
||||
break;
|
||||
index.Read (name_buffer, 0, length);
|
||||
var name = Encoding.UTF8.GetString (name_buffer, 0, length);
|
||||
if (0x09 != index.ReadUInt8() ||
|
||||
0x05 != index.ReadUInt8() ||
|
||||
0x01 != index.ReadUInt8())
|
||||
return null;
|
||||
if (0x04 != index.ReadUInt8()) // invalid number signature
|
||||
return null;
|
||||
uint offset = ReadInteger (index);
|
||||
if (0x04 != index.ReadUInt8())
|
||||
return null;
|
||||
uint size = ReadInteger (index);
|
||||
var entry = Create<PackedEntry> (name);
|
||||
entry.Offset = offset;
|
||||
entry.Size = size;
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
if (0 == entry.Size)
|
||||
return Stream.Null;
|
||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
return new DeflateStream (input, CompressionMode.Decompress);
|
||||
}
|
||||
|
||||
internal static uint ReadInteger (IBinaryStream input)
|
||||
{
|
||||
uint u = input.ReadUInt8();
|
||||
if (u < 0x80)
|
||||
return u;
|
||||
u = (u & 0x7F) << 7;
|
||||
uint b = input.ReadUInt8();
|
||||
if (b < 0x80)
|
||||
return u | b;
|
||||
u = (u | b & 0x7F) << 7;
|
||||
b = input.ReadUInt8();
|
||||
if (b < 0x80)
|
||||
return u | b;
|
||||
u = (u | b & 0x7F) << 8;
|
||||
return u | input.ReadUInt8();
|
||||
}
|
||||
}
|
||||
}
|
@ -27,6 +27,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using GameRes.Compression;
|
||||
|
||||
namespace GameRes.Formats.AliceSoft
|
||||
@ -40,6 +41,16 @@ namespace GameRes.Formats.AliceSoft
|
||||
public override bool IsHierarchic { get { return true; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public AfaOpener ()
|
||||
{
|
||||
ContainedFormats = new[] { "QNT", "AJP", "DCF", "OGG" };
|
||||
Settings = new[] { AfaEncoding };
|
||||
}
|
||||
|
||||
internal readonly EncodingSetting AfaEncoding = new EncodingSetting ("AFAEncodingCP", "DefaultEncoding");
|
||||
|
||||
internal Encoding NameEncoding { get { return AfaEncoding.Get<Encoding>(); } }
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.View.AsciiEqual (8, "AlicArch"))
|
||||
@ -54,6 +65,7 @@ namespace GameRes.Formats.AliceSoft
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
|
||||
var default_enc = NameEncoding;
|
||||
var dir = new List<Entry> (count);
|
||||
var name_buf = new byte[0x40];
|
||||
using (var input = file.CreateStream (0x2C, packed_size))
|
||||
@ -70,7 +82,7 @@ namespace GameRes.Formats.AliceSoft
|
||||
name_buf = new byte[index_step];
|
||||
if (index_step != index.Read (name_buf, 0, index_step))
|
||||
return null;
|
||||
var name = Encodings.cp932.GetString (name_buf, 0, name_length);
|
||||
var name = default_enc.GetString (name_buf, 0, name_length);
|
||||
var entry = FormatCatalog.Instance.Create<Entry> (name);
|
||||
index.ReadInt32();
|
||||
index.ReadInt32();
|
||||
|
@ -2,7 +2,7 @@
|
||||
//! \date Fri Jul 29 14:07:15 2016
|
||||
//! \brief AliceSoft incremental image format.
|
||||
//
|
||||
// Copyright (C) 2016-2018 by morkt
|
||||
// Copyright (C) 2016-2022 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
|
||||
@ -27,6 +27,7 @@ using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Windows.Media;
|
||||
using GameRes.Compression;
|
||||
using GameRes.Utility;
|
||||
@ -37,6 +38,15 @@ namespace GameRes.Formats.AliceSoft
|
||||
{
|
||||
public string BaseName;
|
||||
public long DataOffset;
|
||||
public bool IsPcf;
|
||||
}
|
||||
|
||||
internal interface IBaseImageReader
|
||||
{
|
||||
int BPP { get; }
|
||||
byte[] Data { get; }
|
||||
|
||||
void Unpack ();
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
@ -46,17 +56,25 @@ namespace GameRes.Formats.AliceSoft
|
||||
public override string Description { get { return "AliceSoft System incremental image"; } }
|
||||
public override uint Signature { get { return 0x20666364; } } // 'dcf '
|
||||
|
||||
public DcfFormat ()
|
||||
{
|
||||
Extensions = new[] { "dcf", "pcf" };
|
||||
Signatures = new[] { 0x20666364u, 0x20666370u };
|
||||
}
|
||||
|
||||
static readonly ResourceInstance<AfaOpener> Afa = new ResourceInstance<AfaOpener> ("AFA");
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream stream)
|
||||
{
|
||||
stream.Seek (4, SeekOrigin.Current);
|
||||
uint header_size = stream.ReadUInt32();
|
||||
long data_pos = stream.Position + header_size;
|
||||
if (stream.ReadInt32() != 1)
|
||||
var header = stream.ReadHeader (0x1C);
|
||||
uint header_size = header.ToUInt32 (4);
|
||||
long data_pos = 8 + header_size;
|
||||
if (header.ToInt32 (8) != 1)
|
||||
return null;
|
||||
uint width = stream.ReadUInt32();
|
||||
uint height = stream.ReadUInt32();
|
||||
int bpp = stream.ReadInt32();
|
||||
int name_length = stream.ReadInt32();
|
||||
uint width = header.ToUInt32 (0x0C);
|
||||
uint height = header.ToUInt32 (0x10);
|
||||
int bpp = header.ToInt32 (0x14);
|
||||
int name_length = header.ToInt32 (0x18);
|
||||
if (name_length <= 0)
|
||||
return null;
|
||||
int shift = (name_length % 7) + 1;
|
||||
@ -70,18 +88,17 @@ namespace GameRes.Formats.AliceSoft
|
||||
Width = width,
|
||||
Height = height,
|
||||
BPP = bpp,
|
||||
BaseName = Encodings.cp932.GetString (name_bits),
|
||||
BaseName = Afa.Value.NameEncoding.GetString (name_bits),
|
||||
DataOffset = data_pos,
|
||||
IsPcf = stream.Signature == 0x20666370u,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream stream, ImageMetaData info)
|
||||
{
|
||||
using (var reader = new DcfReader (stream, (DcfMetaData)info))
|
||||
{
|
||||
reader.Unpack();
|
||||
return ImageData.Create (info, reader.Format, null, reader.Data, reader.Stride);
|
||||
}
|
||||
var reader = new DcfReader (stream, (DcfMetaData)info);
|
||||
reader.Unpack();
|
||||
return ImageData.Create (reader.Info, reader.Format, null, reader.Data, reader.Stride);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
@ -90,7 +107,7 @@ namespace GameRes.Formats.AliceSoft
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class DcfReader : IDisposable
|
||||
internal sealed class DcfReader : IBaseImageReader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
DcfMetaData m_info;
|
||||
@ -100,10 +117,14 @@ namespace GameRes.Formats.AliceSoft
|
||||
int m_overlay_bpp;
|
||||
int m_base_bpp;
|
||||
|
||||
static readonly Lazy<ImageFormat> s_QntFormat = new Lazy<ImageFormat> (() => ImageFormat.FindByTag ("QNT"));
|
||||
static readonly ResourceInstance<ImageFormat> s_QntFormat = new ResourceInstance<ImageFormat> ("QNT");
|
||||
static readonly ResourceInstance<ImageFormat> s_DcfFormat = new ResourceInstance<ImageFormat> ("DCF");
|
||||
|
||||
internal ImageFormat Qnt { get { return s_QntFormat.Value; } }
|
||||
internal ImageFormat Dcf { get { return s_DcfFormat.Value; } }
|
||||
|
||||
public int BPP { get { return m_base_bpp; } }
|
||||
public ImageMetaData Info { get; private set; }
|
||||
public byte[] Data { get { return m_output; } }
|
||||
public PixelFormat Format { get; private set; }
|
||||
public int Stride { get; private set; }
|
||||
@ -112,10 +133,13 @@ namespace GameRes.Formats.AliceSoft
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
Info = info;
|
||||
}
|
||||
|
||||
public void Unpack ()
|
||||
{
|
||||
int pt_x = 0;
|
||||
int pt_y = 0;
|
||||
long next_pos = m_info.DataOffset;
|
||||
for (;;)
|
||||
{
|
||||
@ -131,7 +155,12 @@ namespace GameRes.Formats.AliceSoft
|
||||
using (var input = new ZLibStream (m_input.AsStream, CompressionMode.Decompress, true))
|
||||
input.Read (m_mask, 0, unpacked_size);
|
||||
}
|
||||
else if (0x64676364 == id) // 'dcgd'
|
||||
else if (0x6C647470 == id) // 'ptdl'
|
||||
{
|
||||
pt_x = m_input.ReadInt32();
|
||||
pt_y = m_input.ReadInt32();
|
||||
}
|
||||
else if (0x64676364 == id || 0x64676370 == id) // 'dcgd' || 'pcgd'
|
||||
break;
|
||||
}
|
||||
long qnt_pos = m_input.Position;
|
||||
@ -147,18 +176,28 @@ namespace GameRes.Formats.AliceSoft
|
||||
var overlay = new QntFormat.Reader (reg, qnt_info);
|
||||
overlay.Unpack();
|
||||
m_overlay_bpp = overlay.BPP;
|
||||
if (m_mask != null)
|
||||
if (m_mask != null || m_info.IsPcf)
|
||||
ReadBaseImage();
|
||||
|
||||
if (m_base != null)
|
||||
if (m_info.IsPcf)
|
||||
{
|
||||
m_output = ApplyOverlay (overlay.Data);
|
||||
SetFormat ((int)m_info.Width, m_overlay_bpp);
|
||||
if (null == m_base)
|
||||
SetEmptyBase();
|
||||
qnt_info.OffsetX = pt_x;
|
||||
qnt_info.OffsetY = pt_y;
|
||||
BlendOverlay (qnt_info, overlay.Data);
|
||||
m_output = m_base;
|
||||
SetFormat (m_info.iWidth, m_base_bpp);
|
||||
}
|
||||
else if (m_base != null)
|
||||
{
|
||||
m_output = MaskOverlay (overlay.Data);
|
||||
SetFormat (m_info.iWidth, m_overlay_bpp);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_output = overlay.Data;
|
||||
SetFormat ((int)qnt_info.Width, m_overlay_bpp);
|
||||
SetFormat (qnt_info.iWidth, m_overlay_bpp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -169,14 +208,20 @@ namespace GameRes.Formats.AliceSoft
|
||||
Stride = width * (bpp / 8);
|
||||
}
|
||||
|
||||
byte[] ApplyOverlay (byte[] overlay)
|
||||
void SetEmptyBase ()
|
||||
{
|
||||
int blocks_x = (int)m_info.Width / 0x10;
|
||||
int blocks_y = (int)m_info.Height / 0x10;
|
||||
m_base_bpp = 32;
|
||||
m_base = new byte[m_info.Width * m_info.Height * 4];
|
||||
}
|
||||
|
||||
byte[] MaskOverlay (byte[] overlay)
|
||||
{
|
||||
int blocks_x = m_info.iWidth / 0x10;
|
||||
int blocks_y = m_info.iHeight / 0x10;
|
||||
int base_step = m_base_bpp / 8;
|
||||
int overlay_step = m_overlay_bpp / 8;
|
||||
int base_stride = (int)m_info.Width * base_step;
|
||||
int overlay_stride = (int)m_info.Width * overlay_step;
|
||||
int base_stride = m_info.iWidth * base_step;
|
||||
int overlay_stride = m_info.iWidth * overlay_step;
|
||||
int mask_pos = 4;
|
||||
for (int y = 0; y < blocks_y; ++y)
|
||||
{
|
||||
@ -208,6 +253,58 @@ namespace GameRes.Formats.AliceSoft
|
||||
return overlay;
|
||||
}
|
||||
|
||||
void BlendOverlay (ImageMetaData overlay_info, byte[] overlay)
|
||||
{
|
||||
int ovl_x = overlay_info.OffsetX;
|
||||
int ovl_y = overlay_info.OffsetY;
|
||||
int ovl_width = overlay_info.iWidth;
|
||||
int ovl_height = overlay_info.iHeight;
|
||||
int base_width = m_info.iWidth;
|
||||
int base_height = m_info.iHeight;
|
||||
if (checked(ovl_x + ovl_width) > base_width)
|
||||
ovl_width = base_width - ovl_x;
|
||||
if (checked(ovl_y + ovl_height) > base_height)
|
||||
ovl_height = base_height - ovl_y;
|
||||
if (ovl_height <= 0 || ovl_width <= 0)
|
||||
return;
|
||||
|
||||
int dst_stride = m_info.iWidth * 4;
|
||||
int src_stride = overlay_info.iWidth * 4;
|
||||
int dst = ovl_y * dst_stride + ovl_x * 4;
|
||||
int src = 0;
|
||||
int gap = dst_stride - src_stride;
|
||||
for (int y = 0; y < overlay_info.iHeight; ++y)
|
||||
{
|
||||
for (int x = 0; x < overlay_info.iWidth; ++x)
|
||||
{
|
||||
byte src_alpha = overlay[src+3];
|
||||
if (src_alpha != 0)
|
||||
{
|
||||
if (0xFF == src_alpha || 0 == m_base[dst+3])
|
||||
{
|
||||
m_base[dst] = overlay[src];
|
||||
m_base[dst+1] = overlay[src+1];
|
||||
m_base[dst+2] = overlay[src+2];
|
||||
m_base[dst+3] = src_alpha;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_base[dst+0] = (byte)((overlay[src+0] * src_alpha
|
||||
+ m_base[dst+0] * (0xFF - src_alpha)) / 0xFF);
|
||||
m_base[dst+1] = (byte)((overlay[src+1] * src_alpha
|
||||
+ m_base[dst+1] * (0xFF - src_alpha)) / 0xFF);
|
||||
m_base[dst+2] = (byte)((overlay[src+2] * src_alpha
|
||||
+ m_base[dst+2] * (0xFF - src_alpha)) / 0xFF);
|
||||
m_base[dst+3] = (byte)Math.Max (src_alpha, m_base[dst+3]);
|
||||
}
|
||||
}
|
||||
dst += 4;
|
||||
src += 4;
|
||||
}
|
||||
dst += gap;
|
||||
}
|
||||
}
|
||||
|
||||
void ReadBaseImage ()
|
||||
{
|
||||
try
|
||||
@ -215,13 +312,29 @@ namespace GameRes.Formats.AliceSoft
|
||||
string dir_name = VFS.GetDirectoryName (m_info.FileName);
|
||||
string base_name = Path.ChangeExtension (m_info.BaseName, "qnt");
|
||||
base_name = VFS.CombinePath (dir_name, base_name);
|
||||
ImageFormat base_format = null;
|
||||
Func<IBinaryStream, ImageMetaData, IBaseImageReader> create_reader;
|
||||
if (VFS.FileExists (base_name))
|
||||
{
|
||||
base_format = Qnt;
|
||||
create_reader = (s, m) => new QntFormat.Reader (s.AsStream, (QntMetaData)m);
|
||||
}
|
||||
else
|
||||
{
|
||||
base_name = Path.ChangeExtension (m_info.BaseName, "pcf");
|
||||
if (VFS.IsPathEqualsToFileName (m_info.FileName, base_name))
|
||||
return;
|
||||
base_name = VFS.CombinePath (dir_name, base_name);
|
||||
base_format = Dcf;
|
||||
create_reader = (s, m) => new DcfReader (s, (DcfMetaData)m);
|
||||
}
|
||||
using (var base_file = VFS.OpenBinaryStream (base_name))
|
||||
{
|
||||
var base_info = Qnt.ReadMetaData (base_file) as QntMetaData;
|
||||
var base_info = base_format.ReadMetaData (base_file);
|
||||
if (null != base_info && m_info.Width == base_info.Width && m_info.Height == base_info.Height)
|
||||
{
|
||||
base_info.FileName = base_name;
|
||||
var reader = new QntFormat.Reader (base_file.AsStream, base_info);
|
||||
var reader = create_reader (base_file, base_info);
|
||||
reader.Unpack();
|
||||
m_base_bpp = reader.BPP;
|
||||
m_base = reader.Data;
|
||||
@ -233,11 +346,5 @@ namespace GameRes.Formats.AliceSoft
|
||||
Trace.WriteLine (X.Message, "[DCF]");
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
public void Dispose ()
|
||||
{
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ namespace GameRes.Formats.AliceSoft
|
||||
return ImageData.Create (info, format, null, reader.Data, stride);
|
||||
}
|
||||
|
||||
internal class Reader
|
||||
internal class Reader : IBaseImageReader
|
||||
{
|
||||
byte[] m_input;
|
||||
byte[] m_alpha;
|
||||
|
@ -339,7 +339,7 @@ namespace GameRes.Formats.Amaterasu
|
||||
}
|
||||
|
||||
[Export(typeof(ScriptFormat))]
|
||||
public class ScrFormat : ScriptFormat
|
||||
public class ScrFormat : GenericScriptFormat
|
||||
{
|
||||
public override string Tag { get { return "SCR/AMI"; } }
|
||||
public override string Description { get { return Strings.arcStrings.SCRDescription; } }
|
||||
|
@ -83,126 +83,33 @@ namespace GameRes.Formats.Apricot
|
||||
dir.Add (entry);
|
||||
}
|
||||
}
|
||||
return new Mpf2Archive (file, this, dir, arc_list);
|
||||
var parts = new List<ArcView> (arc_list.Count);
|
||||
try
|
||||
{
|
||||
foreach (var arc_entry in arc_list)
|
||||
{
|
||||
var arc_file = VFS.OpenView (arc_entry);
|
||||
parts.Add (arc_file);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
foreach (var part in parts)
|
||||
part.Dispose();
|
||||
throw;
|
||||
}
|
||||
return new MultiFileArchive (file, this, dir, parts);
|
||||
}
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var mpf = (Mpf2Archive)arc;
|
||||
var input = mpf.ViewChain.CreateStream (entry.Offset, entry.Size);
|
||||
var mpf = (MultiFileArchive)arc;
|
||||
var input = mpf.OpenStream (entry);
|
||||
var pent = entry as PackedEntry;
|
||||
if (pent != null && pent.IsPacked)
|
||||
input = new ZLibStream (input, CompressionMode.Decompress);
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
internal class Mpf2Archive : ArcFile
|
||||
{
|
||||
ArcViewChain m_parts;
|
||||
|
||||
public ArcViewChain ViewChain { get { return m_parts; } }
|
||||
|
||||
public Mpf2Archive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, IReadOnlyList<Entry> arc_list)
|
||||
: base (arc, impl, dir)
|
||||
{
|
||||
m_parts = new ArcViewChain (arc, arc_list);
|
||||
}
|
||||
|
||||
bool m_disposed = false;
|
||||
protected override void Dispose (bool disposing)
|
||||
{
|
||||
if (disposing && !m_disposed)
|
||||
{
|
||||
if (m_parts != null)
|
||||
m_parts.Dispose();
|
||||
m_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ArcViewChain : IDisposable
|
||||
{
|
||||
ArcView m_file;
|
||||
IReadOnlyList<ArcView> m_parts;
|
||||
|
||||
public IEnumerable<ArcView> Parts {
|
||||
get {
|
||||
yield return m_file;
|
||||
if (m_parts != null)
|
||||
foreach (var part in m_parts)
|
||||
yield return part;
|
||||
}
|
||||
}
|
||||
|
||||
public ArcViewChain (ArcView file, IReadOnlyList<Entry> arc_list)
|
||||
{
|
||||
var parts = new List<ArcView> (arc_list.Count);
|
||||
try
|
||||
{
|
||||
foreach (var arc_entry in arc_list)
|
||||
{
|
||||
var arc_file = VFS.OpenView (arc_entry);
|
||||
parts.Add (arc_file);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
foreach (var part in parts)
|
||||
part.Dispose();
|
||||
throw;
|
||||
}
|
||||
m_file = file;
|
||||
m_parts = parts;
|
||||
}
|
||||
|
||||
public Stream CreateStream (long offset, uint size)
|
||||
{
|
||||
Stream input = null;
|
||||
try
|
||||
{
|
||||
long part_offset = 0;
|
||||
long entry_start = offset;
|
||||
long entry_end = offset + size;
|
||||
foreach (var part in Parts)
|
||||
{
|
||||
long part_end_offset = part_offset + part.MaxOffset;
|
||||
if (entry_start < part_end_offset)
|
||||
{
|
||||
uint part_size = (uint)Math.Min (entry_end - entry_start, part_end_offset - entry_start);
|
||||
var entry_part = part.CreateStream (entry_start - part_offset, part_size);
|
||||
if (input != null)
|
||||
input = new ConcatStream (input, entry_part);
|
||||
else
|
||||
input = entry_part;
|
||||
entry_start += part_size;
|
||||
if (entry_start >= entry_end)
|
||||
break;
|
||||
}
|
||||
part_offset = part_end_offset;
|
||||
}
|
||||
return input ?? Stream.Null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (input != null)
|
||||
input.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
bool m_disposed = false;
|
||||
public void Dispose ()
|
||||
{
|
||||
if (m_disposed)
|
||||
return;
|
||||
if (m_parts != null)
|
||||
{
|
||||
foreach (var arc in m_parts)
|
||||
arc.Dispose();
|
||||
}
|
||||
m_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -350,4 +350,10 @@ namespace GameRes.Formats
|
||||
public override string Description { get { return "Unidentified data file"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
}
|
||||
|
||||
// [970725][Guilty] Onii-chan e
|
||||
[Export(typeof(ResourceAlias))]
|
||||
[ExportMetadata("Extension", "GLT")]
|
||||
[ExportMetadata("Target", "BMP")]
|
||||
public class GltFormat : ResourceAlias { }
|
||||
}
|
||||
|
@ -98,15 +98,22 @@
|
||||
<Compile Include="Abogado\ImageKG.cs" />
|
||||
<Compile Include="Actgs\ArcCG.cs" />
|
||||
<Compile Include="Actgs\ArcDAT.cs" />
|
||||
<Compile Include="Adobe\ArcAIR.cs" />
|
||||
<Compile Include="Ads\ArcPAC.cs" />
|
||||
<Compile Include="AdvDx\ArcPKD.cs" />
|
||||
<Compile Include="AdvScripter\ArcPAK.cs" />
|
||||
<Compile Include="AdvSys\ArcAdvSys3.cs" />
|
||||
<Compile Include="AdvSys\ImageGWD.cs" />
|
||||
<Compile Include="Ail\ArcLNK2.cs" />
|
||||
<Compile Include="AIRNovel\ArcAIR.cs" />
|
||||
<Compile Include="BlueGale\ImageBBM.cs" />
|
||||
<Compile Include="ClickTeam\ArcMF.cs" />
|
||||
<Compile Include="DxLib\ArcDX8.cs" />
|
||||
<Compile Include="Hypatia\ArcLPK.cs" />
|
||||
<Compile Include="Hypatia\ImageLSG.cs" />
|
||||
<Compile Include="Illusion\ArcPP.cs" />
|
||||
<Compile Include="Interheart\ImageBMP.cs" />
|
||||
<Compile Include="Interheart\ImageHMP.cs" />
|
||||
<Compile Include="JamCreation\ArcDAT.cs" />
|
||||
<Compile Include="JamCreation\ImageDPO.cs" />
|
||||
<Compile Include="AliceSoft\ArcAAR.cs" />
|
||||
@ -130,6 +137,12 @@
|
||||
<Compile Include="Apricot\ArcDAT.cs" />
|
||||
<Compile Include="ArcARCX.cs" />
|
||||
<Compile Include="Artemis\ArcMJA.cs" />
|
||||
<Compile Include="Macromedia\ArcDXR.cs" />
|
||||
<Compile Include="Macromedia\DirectorFile.cs" />
|
||||
<Compile Include="Macromedia\Palettes.cs" />
|
||||
<Compile Include="NScripter\Script.cs" />
|
||||
<Compile Include="TechGian\ArcBIN.cs" />
|
||||
<Compile Include="Zyx\ImageXMG.cs" />
|
||||
<None Include="AudioAIFF.cs" />
|
||||
<Compile Include="Basil\ArcMIF.cs" />
|
||||
<Compile Include="Basil\AudioWHC.cs" />
|
||||
@ -251,6 +264,9 @@
|
||||
<Compile Include="Mnp\ArcMMA.cs" />
|
||||
<Compile Include="MultiFileArchive.cs" />
|
||||
<Compile Include="Musica\ArcANI.cs" />
|
||||
<Compile Include="MyAdv\ArcPAC.cs" />
|
||||
<Compile Include="Nekopack\ArcNEKO2.cs" />
|
||||
<Compile Include="Nekopack\ArcNEKO3.cs" />
|
||||
<Compile Include="NitroPlus\ArcLAY.cs" />
|
||||
<Compile Include="NitroPlus\ArcMPK.cs" />
|
||||
<Compile Include="NitroPlus\ArcNPP.cs" />
|
||||
@ -335,6 +351,8 @@
|
||||
<Compile Include="Software House Parsley\ArcPAC.cs" />
|
||||
<Compile Include="Software House Parsley\ArcUCG.cs" />
|
||||
<Compile Include="Software House Parsley\ImagePCG.cs" />
|
||||
<Compile Include="Sohfu\ArcSKA.cs" />
|
||||
<Compile Include="Sohfu\ImageDTL.cs" />
|
||||
<Compile Include="Speed\ArcARC.cs" />
|
||||
<Compile Include="Stack\ArcGPK.cs" />
|
||||
<Compile Include="Strikes\ArcPCK.cs" />
|
||||
@ -744,7 +762,7 @@
|
||||
<Compile Include="MangaGamer\ArcMGPK.cs" />
|
||||
<Compile Include="MnoViolet\ArcMnoViolet.cs" />
|
||||
<Compile Include="FC01\ArcMRG.cs" />
|
||||
<Compile Include="ArcNEKO.cs" />
|
||||
<Compile Include="Nekopack\ArcNEKO.cs" />
|
||||
<Compile Include="Nexas\ArcPAC.cs" />
|
||||
<Compile Include="NitroPlus\ArcNitro.cs" />
|
||||
<Compile Include="Entis\ArcNOA.cs" />
|
||||
@ -824,6 +842,7 @@
|
||||
<Compile Include="Will\ArcPNA.cs" />
|
||||
<Compile Include="Will\ArcPulltop.cs" />
|
||||
<Compile Include="Will\ArcWIP.cs" />
|
||||
<Compile Include="Winters\ArcCFP.cs" />
|
||||
<Compile Include="Winters\ArcDAT.cs" />
|
||||
<Compile Include="Winters\ArcIFP.cs" />
|
||||
<Compile Include="Winters\ArcIFX.cs" />
|
||||
@ -832,6 +851,7 @@
|
||||
<Compile Include="Xuse\ArcWAG.cs" />
|
||||
<Compile Include="Xuse\ArcXARC.cs" />
|
||||
<Compile Include="Xuse\ArcXuse.cs" />
|
||||
<Compile Include="Xuse\ImageP.cs" />
|
||||
<Compile Include="YaneSDK\ArcDAT.cs" />
|
||||
<Compile Include="YaneSDK\ArcHibiki.cs" />
|
||||
<Compile Include="Yatagarasu\ArcPKG.cs" />
|
||||
|
@ -27,11 +27,13 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using GameRes.Compression;
|
||||
using GameRes.Formats.Maika;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.BellDa
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public sealed class BldOpener : ArchiveFormat
|
||||
public sealed class BldOpener : Mk2Opener
|
||||
{
|
||||
public override string Tag { get { return "DAT/BLD"; } }
|
||||
public override string Description { get { return "BELL-DA resource archive"; } }
|
||||
@ -39,6 +41,13 @@ namespace GameRes.Formats.BellDa
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public BldOpener ()
|
||||
{
|
||||
Signatures = new[] { this.Signature };
|
||||
Settings = null;
|
||||
Scheme = null;
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
var version_str = file.View.ReadString (4, 4).TrimEnd ('\x1A');
|
||||
@ -66,26 +75,5 @@ namespace GameRes.Formats.BellDa
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
// XXX compression method identical to Maika.Mk2Opener
|
||||
var pent = (PackedEntry)entry;
|
||||
if (null == pent || !pent.IsPacked)
|
||||
{
|
||||
var id_str = arc.File.View.ReadString (entry.Offset, 2);
|
||||
if (id_str != "B1" && id_str != "C1" && id_str != "D1" && id_str != "E1")
|
||||
return base.OpenEntry (arc, entry);
|
||||
uint packed_size = arc.File.View.ReadUInt32 (entry.Offset+2);
|
||||
if (packed_size != entry.Size - 10)
|
||||
return base.OpenEntry (arc, entry);
|
||||
pent.Size = packed_size;
|
||||
pent.UnpackedSize = arc.File.View.ReadUInt32 (entry.Offset+6);
|
||||
pent.Offset += 10;
|
||||
pent.IsPacked = true;
|
||||
}
|
||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
return new LzssStream (input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -136,14 +136,18 @@ namespace GameRes.Formats.Cadath
|
||||
if (0 != (bits[i >> 3] & 1))
|
||||
{
|
||||
byte v = m_input.ReadUInt8();
|
||||
int count = m_input.ReadUInt8() + 3;
|
||||
int count = Math.Min (m_input.ReadUInt8() + 3, data.Length - dst);
|
||||
for (int j = 0; j < count; ++j)
|
||||
{
|
||||
data[dst++] = v;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
data[dst++] = m_input.ReadUInt8();
|
||||
}
|
||||
if (dst >= data.Length)
|
||||
break;
|
||||
bits[i >> 3] >>= 1;
|
||||
}
|
||||
CopyChannels (data);
|
||||
|
100
ArcFormats/ClickTeam/ArcMF.cs
Normal file
100
ArcFormats/ClickTeam/ArcMF.cs
Normal file
@ -0,0 +1,100 @@
|
||||
//! \file ArcMF.cs
|
||||
//! \date 2023 Aug 09
|
||||
//! \brief Multimedia Fusion 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.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace GameRes.Formats.ClickTeam
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class MfOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "MFS"; } }
|
||||
public override string Description { get { return "Multimedia Fusion resource archive"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override bool IsHierarchic { get { return true; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public MfOpener ()
|
||||
{
|
||||
Signatures = new uint[] { 0x77777777, 0 }; // 'wwww'
|
||||
Extensions = new string[] { "" };
|
||||
}
|
||||
|
||||
static readonly byte[] s_mfs_header = {
|
||||
(byte)'w', (byte)'w', (byte)'w', (byte)'w', 0x49, 0x87, 0x47, 0x12,
|
||||
};
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
long base_offset = 0;
|
||||
if (0x5A4D == file.View.ReadUInt16 (0)) // 'MZ'
|
||||
{
|
||||
var exe = new ExeFile (file);
|
||||
base_offset = exe.Overlay.Offset;
|
||||
}
|
||||
if (base_offset + 0x20 > file.MaxOffset || !file.View.BytesEqual (base_offset, s_mfs_header))
|
||||
return null;
|
||||
|
||||
int count = file.View.ReadInt32 (base_offset + 0x1C);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
|
||||
long index_pos = base_offset + 0x20;
|
||||
var dir = new List<Entry> (count);
|
||||
var name_buffer = new byte[520];
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
int name_length = file.View.ReadUInt16 (index_pos) * 2;
|
||||
if (name_length > name_buffer.Length)
|
||||
return null;
|
||||
file.View.Read (index_pos + 2, name_buffer, 0, (uint)name_length);
|
||||
var name = Encoding.Unicode.GetString (name_buffer, 0, name_length);
|
||||
index_pos += 2 + name_length;
|
||||
var entry = Create<PackedEntry> (name);
|
||||
entry.IsPacked = name != "mmfs2.dll";
|
||||
entry.Size = file.View.ReadUInt32 (index_pos + 4);
|
||||
entry.Offset = index_pos + 8;
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
index_pos = entry.Offset + entry.Size;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var pent = (PackedEntry)entry;
|
||||
Stream input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
if (pent.IsPacked)
|
||||
input = new ZLibStream (input, CompressionMode.Decompress);
|
||||
return input;
|
||||
}
|
||||
}
|
||||
}
|
@ -28,6 +28,7 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using GameRes.Formats.Cmvs;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Purple
|
||||
@ -133,18 +134,38 @@ namespace GameRes.Formats.Purple
|
||||
|
||||
var index_copy = new CowArray<byte> (index, 0, file_table_size).ToArray();
|
||||
var cmvs_md5 = cpz.CmvsMd5.Clone() as uint[];
|
||||
foreach (var scheme in KnownSchemes.Values.Where (s => s.Version == cpz.Version))
|
||||
var scheme = CreateCpz5Scheme(Md5Variant.Mirai);
|
||||
// foreach (var scheme in KnownSchemes.Values.Where (s => s.Version == cpz.Version))
|
||||
{
|
||||
var arc = ReadIndex (file, scheme, cpz, index, arc_key);
|
||||
if (null != arc)
|
||||
return arc;
|
||||
// both CmvsMd5 and index was altered by ReadIndex in decryption attempt
|
||||
Array.Copy (cmvs_md5, cpz.CmvsMd5, 4);
|
||||
Array.Copy (index, index_copy, file_table_size);
|
||||
Array.Copy (index_copy, index, file_table_size);
|
||||
}
|
||||
throw new UnknownEncryptionScheme();
|
||||
}
|
||||
|
||||
static uint[] DefaultCpz5Secret = {
|
||||
0xCD90F089, 0xE982B782, 0xA282AB88, 0xCD82718E, 0x52838A83, 0xA882AA82, 0x7592648E, 0xB582AB82,
|
||||
0xE182BF82, 0xDC82A282, 0x4281B782, 0xED82F48E, 0xBF82EA82, 0xA282E182, 0xB782DC82, 0x6081E682,
|
||||
0xC6824181, 0xA482A282, 0xE082A982, 0xF48EA482, 0xBF82C182, 0xA282E182, 0xB582DC82, 0xF481BD82,
|
||||
};
|
||||
|
||||
static CmvsScheme CreateCpz5Scheme(Md5Variant md5)
|
||||
{
|
||||
return new CmvsScheme
|
||||
{
|
||||
Version = 5,
|
||||
Cpz5Secret = DefaultCpz5Secret,
|
||||
Md5Variant = md5,
|
||||
DecoderFactor = 0x1A743125,
|
||||
EntryInitKey = 0x2547A39E,
|
||||
EntryTailKey = 0xBC,
|
||||
};
|
||||
}
|
||||
|
||||
internal ArcFile ReadIndex (ArcView file, CmvsScheme scheme, CpzHeader cpz, byte[] index, ArchiveKey arc_key)
|
||||
{
|
||||
var cmvs_md5 = Cmvs.MD5.Create (scheme.Md5Variant);
|
||||
|
@ -27,7 +27,7 @@ using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Cmvs
|
||||
{
|
||||
public enum Md5Variant { A, B, Chrono, Memoria, Natsu, Aoi }
|
||||
public enum Md5Variant { A, B, Chrono, Memoria, Natsu, Aoi, Mirai }
|
||||
|
||||
public abstract class MD5 : Cryptography.MD5Base
|
||||
{
|
||||
@ -46,6 +46,7 @@ namespace GameRes.Formats.Cmvs
|
||||
case Md5Variant.Memoria: return new Md5Memoria();
|
||||
case Md5Variant.Natsu: return new Md5Natsu();
|
||||
case Md5Variant.Aoi: return new Md5Aoi();
|
||||
case Md5Variant.Mirai: return new Md5Mirai();
|
||||
default: throw new System.ArgumentException ("Unknown MD5 variant", "variant");
|
||||
}
|
||||
}
|
||||
@ -153,6 +154,25 @@ namespace GameRes.Formats.Cmvs
|
||||
}
|
||||
}
|
||||
|
||||
public class Md5Mirai : MD5
|
||||
{
|
||||
protected override void InitState ()
|
||||
{
|
||||
m_state[0] = 0x67452301;
|
||||
m_state[1] = 0xEFCDAB89;
|
||||
m_state[2] = 0x98BADCFE;
|
||||
m_state[3] = 0x10325476;
|
||||
}
|
||||
|
||||
protected override void SetResult (uint[] data)
|
||||
{
|
||||
data[0] = m_state[0];
|
||||
data[1] = m_state[1];
|
||||
data[2] = m_state[2];
|
||||
data[3] = m_state[3];
|
||||
}
|
||||
}
|
||||
|
||||
public class Md5Aoi : MD5
|
||||
{
|
||||
protected override void InitState ()
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2022 by morkt
|
||||
// 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
|
||||
@ -29,11 +29,11 @@ namespace GameRes.Formats.??????
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class PakOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "ARC"; } }
|
||||
public override string Description { get { return "?????? engine resource archive"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
public override string Tag { get => "ARC"; }
|
||||
public override string Description { get => "?????? engine resource archive"; }
|
||||
public override uint Signature { get => 0; }
|
||||
public override bool IsHierarchic { get => false; }
|
||||
public override bool CanWrite { get => false; }
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2022 by morkt
|
||||
// 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
|
||||
@ -26,10 +26,10 @@ namespace GameRes.Formats.??????
|
||||
[Export(typeof(AudioFormat))]
|
||||
public class xxxAudio : AudioFormat
|
||||
{
|
||||
public override string Tag { get { return "xxx"; } }
|
||||
public override string Description { get { return "?????? audio resource"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
public override string Tag { get => "xxx"; }
|
||||
public override string Description { get => "?????? audio resource"; }
|
||||
public override uint Signature { get => 0; }
|
||||
public override bool CanWrite { get => false; }
|
||||
|
||||
public override SoundInput TryOpen (IBinaryStream file)
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2022 by morkt
|
||||
// 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
|
||||
@ -28,9 +28,9 @@ namespace GameRes.Formats.??????
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class xxxFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "xxx"; } }
|
||||
public override string Description { get { return "?????? image format"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override string Tag { get => "xxx"; }
|
||||
public override string Description { get => "?????? image format"; }
|
||||
public override uint Signature { get => 0; }
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
|
70
ArcFormats/DxLib/ArcDX8.cs
Normal file
70
ArcFormats/DxLib/ArcDX8.cs
Normal file
@ -0,0 +1,70 @@
|
||||
//! \file ArcDX8.cs
|
||||
//! \date 2022 Jun 05
|
||||
//! \brief DxLib archive version 8.
|
||||
//
|
||||
// Copyright (C) 2022 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.DxLib
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class Dx8Opener : DxOpener
|
||||
{
|
||||
public override string Tag { get { return "BIN/DXLIB"; } }
|
||||
public override string Description { get { return "DxLib archive version 8"; } }
|
||||
public override uint Signature { get { return 0x00085844; } }
|
||||
public override bool IsHierarchic { get { return true; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public Dx8Opener ()
|
||||
{
|
||||
Extensions = new[] { "bin" };
|
||||
Signatures = new[] { 0x00085844u };
|
||||
}
|
||||
|
||||
static readonly byte[] DefaultKey = new byte[] { 0xBE, 0xC8, 0x8A, 0xF5, 0x28, 0x50, 0xC9 };
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
var dx = new DxHeader {
|
||||
IndexSize = file.View.ReadUInt32 (4),
|
||||
BaseOffset = file.View.ReadInt64 (8),
|
||||
IndexOffset = file.View.ReadInt64 (0x10),
|
||||
FileTable = (uint)file.View.ReadInt64 (0x18),
|
||||
DirTable = (uint)file.View.ReadInt64 (0x20),
|
||||
CodePage = file.View.ReadInt32 (0x28),
|
||||
};
|
||||
if (dx.DirTable >= dx.IndexSize || dx.FileTable >= dx.IndexSize)
|
||||
return null;
|
||||
var key = DefaultKey;
|
||||
var index = file.View.ReadBytes (dx.IndexOffset, dx.IndexSize);
|
||||
Decrypt (index, 0, index.Length, 0, key);
|
||||
// decrypt-2
|
||||
// decompress
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -148,7 +148,7 @@ namespace GameRes.Formats.Emote
|
||||
|
||||
ImageFormat TlgFormat { get { return s_TlgFormat.Value; } }
|
||||
|
||||
static Lazy<ImageFormat> s_TlgFormat = new Lazy<ImageFormat> (() => ImageFormat.FindByTag ("TLG"));
|
||||
static ResourceInstance<ImageFormat> s_TlgFormat = new ResourceInstance<ImageFormat> ("TLG");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -56,6 +56,7 @@ namespace GameRes.Formats.Entis
|
||||
float[] m_ptrMatrixBuf;
|
||||
float[] m_ptrIQParamBuf;
|
||||
byte[] m_ptrIQParamTable;
|
||||
// float[] m_ptrBuffer2;
|
||||
|
||||
sbyte[] m_ptrBlockLineBuf;
|
||||
sbyte[] m_ptrNextBlockBuf;
|
||||
@ -769,6 +770,7 @@ namespace GameRes.Formats.Entis
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
/*
|
||||
|
||||
if (m_nChannelCount < 3)
|
||||
throw new InvalidFormatException();
|
||||
m_nDstPixelBytes = m_info.BPP >> 3;
|
||||
@ -1137,6 +1139,42 @@ namespace GameRes.Formats.Entis
|
||||
}
|
||||
}
|
||||
|
||||
void LossyRestoreRGB24_V9 ()
|
||||
{
|
||||
/*
|
||||
int nLineOffset = 0;
|
||||
int nBytesPerPixel = m_nDstPixelBytes;
|
||||
int ptrDstLine = m_ptrDstBlock;
|
||||
|
||||
for (uint y = 0; y < m_nDstHeight; ++y)
|
||||
{
|
||||
int nOffsetPos = nLineOffset;
|
||||
int ptrNextDst = ptrDstLine;
|
||||
|
||||
for (uint x = 0; x < m_nDstWidth; ++x)
|
||||
{
|
||||
m_output[ptrNextDst ] = RoundR32ToByte (m_ptrBuffer2[0][nOffsetPos]);
|
||||
m_output[ptrNextDst+1] = RoundR32ToByte (m_ptrBuffer2[1][nOffsetPos]);
|
||||
m_output[ptrNextDst+2] = RoundR32ToByte (m_ptrBuffer2[2][nOffsetPos]);
|
||||
++nOffsetPos;
|
||||
ptrNextDst += nBytesPerPixel;
|
||||
}
|
||||
nLineOffset += m_nBlockSize;
|
||||
ptrDstLine += m_nDstLineBytes;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
static byte RoundR32ToByte (float r)
|
||||
{
|
||||
int n = Erisa.RoundR32ToInt (r);
|
||||
if (n < 0)
|
||||
return 0;
|
||||
else if (n >= 0x100)
|
||||
return 0xFF;
|
||||
return (byte)n;
|
||||
}
|
||||
|
||||
void RestoreDeltaRGBA32 ()
|
||||
{
|
||||
int ptrDstLine = m_ptrDstBlock;
|
||||
|
@ -35,6 +35,7 @@ namespace GameRes.Formats.Eternity
|
||||
public bool HasAlpha;
|
||||
public int BlockSize;
|
||||
public uint DataOffset;
|
||||
public uint AlphaOffset;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
@ -64,6 +65,7 @@ namespace GameRes.Formats.Eternity
|
||||
HasAlpha = header.ToInt32 (8) != 0,
|
||||
BlockSize = header.ToUInt16 (0xC),
|
||||
DataOffset = header.ToUInt32 (0x14),
|
||||
AlphaOffset = header.ToUInt32 (0x1C),
|
||||
};
|
||||
}
|
||||
|
||||
@ -87,13 +89,14 @@ namespace GameRes.Formats.Eternity
|
||||
byte[] m_output;
|
||||
|
||||
public byte[] Data { get { return m_output; } }
|
||||
public PixelFormat Format { get { return PixelFormats.Bgr24; } }
|
||||
public PixelFormat Format { get; private set; }
|
||||
|
||||
public SgfReader (IBinaryStream input, SgfMetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
m_output = new byte[3 * info.Width * info.Height];
|
||||
Format = PixelFormats.Bgr24;
|
||||
}
|
||||
|
||||
public byte[] Unpack ()
|
||||
@ -128,9 +131,79 @@ namespace GameRes.Formats.Eternity
|
||||
g = m_output[start_pos+1];
|
||||
r = m_output[start_pos+2];
|
||||
}
|
||||
if (m_info.HasAlpha)
|
||||
{
|
||||
var alpha = ReadAlpha();
|
||||
if (alpha != null)
|
||||
{
|
||||
var pixels = new byte[m_info.iWidth * m_info.iHeight * 4];
|
||||
dst = 0;
|
||||
int p = 0;
|
||||
int a = 0;
|
||||
while (dst < pixels.Length)
|
||||
{
|
||||
pixels[dst++] = m_output[p++];
|
||||
pixels[dst++] = m_output[p++];
|
||||
pixels[dst++] = m_output[p++];
|
||||
pixels[dst++] = alpha[a++];
|
||||
}
|
||||
m_output = pixels;
|
||||
Format = PixelFormats.Bgra32;
|
||||
}
|
||||
}
|
||||
return m_output;
|
||||
}
|
||||
|
||||
byte[] ReadAlpha ()
|
||||
{
|
||||
m_input.Position = m_info.AlphaOffset;
|
||||
var signature = m_input.ReadUInt16();
|
||||
if (0x2041 == signature) // 'A '
|
||||
return ReadASection();
|
||||
else if (0x4D42 == signature) // 'BM'
|
||||
return ReadBmpSection();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] ReadASection ()
|
||||
{
|
||||
var alpha = new byte[m_info.iWidth * m_info.iHeight];
|
||||
m_input.Position = m_info.AlphaOffset + 8;
|
||||
int block_size = m_input.ReadUInt16();
|
||||
m_input.Position = m_info.AlphaOffset + 0x10;
|
||||
uint offset = m_input.ReadUInt32();
|
||||
uint next_pos = m_info.AlphaOffset + offset;
|
||||
int height = (int)m_info.Height;
|
||||
int dst = alpha.Length - m_info.iWidth;
|
||||
byte a = 0;
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
int start_pos = dst;
|
||||
if (0 == (y % block_size))
|
||||
{
|
||||
m_input.Position = next_pos;
|
||||
next_pos += m_input.ReadUInt32();
|
||||
a = (byte)m_input.ReadUInt32();
|
||||
mask1 = mask2 = mask3 = mask4 = mask5 = 1;
|
||||
}
|
||||
for (uint x = 0; x < m_info.Width; ++x)
|
||||
{
|
||||
a = GetNextByte (a);
|
||||
alpha[dst++] = a;
|
||||
}
|
||||
a = alpha[start_pos];
|
||||
dst = start_pos - m_info.iWidth;
|
||||
}
|
||||
return alpha;
|
||||
}
|
||||
|
||||
byte[] ReadBmpSection ()
|
||||
{
|
||||
// throw new NotImplementedException ("ReadBmpSection not implemented.");
|
||||
return null;
|
||||
}
|
||||
|
||||
uint mask1;
|
||||
uint mask2;
|
||||
uint mask3;
|
||||
|
@ -62,6 +62,11 @@ namespace GameRes.Formats.ExHibit
|
||||
public override string Description { get { return "ExHIBIT engine image format"; } }
|
||||
public override uint Signature { get { return 0x1A555947; } } // 'GYU'
|
||||
|
||||
public GyuFormat ()
|
||||
{
|
||||
Extensions = new[] { "gyu", "lvg" };
|
||||
}
|
||||
|
||||
GyuMap DefaultScheme = new GyuMap {
|
||||
NumericKeys = new Dictionary<string, Dictionary<int, uint>>(),
|
||||
StringKeys = new Dictionary<string, Dictionary<string, uint>>(),
|
||||
|
@ -37,6 +37,8 @@ namespace GameRes.Formats
|
||||
ArcView m_file;
|
||||
Dictionary<string, Section> m_section_table;
|
||||
Section m_overlay;
|
||||
uint m_image_base = 0;
|
||||
List<ImageSection> m_section_list;
|
||||
|
||||
public ExeFile (ArcView file)
|
||||
{
|
||||
@ -79,6 +81,16 @@ namespace GameRes.Formats
|
||||
}
|
||||
}
|
||||
|
||||
public uint ImageBase
|
||||
{
|
||||
get
|
||||
{
|
||||
if (0 == m_image_base)
|
||||
InitImageBase();
|
||||
return m_image_base;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Structure representing section of executable file in the form of its offset and size.
|
||||
/// </summary>
|
||||
@ -88,6 +100,16 @@ namespace GameRes.Formats
|
||||
public uint Size;
|
||||
}
|
||||
|
||||
public class ImageSection // IMAGE_SECTION_HEADER
|
||||
{
|
||||
public string Name;
|
||||
public uint VirtualSize;
|
||||
public uint VirtualAddress;
|
||||
public uint SizeOfRawData;
|
||||
public uint PointerToRawData;
|
||||
public uint Characteristics;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if executable file contains section <paramref name="name"/>.
|
||||
/// </summary>
|
||||
@ -140,6 +162,16 @@ namespace GameRes.Formats
|
||||
return -1;
|
||||
}
|
||||
|
||||
public Section SectionByOffset (long offset)
|
||||
{
|
||||
foreach (var section in Sections.Values)
|
||||
{
|
||||
if (offset >= section.Offset && offset < section.Offset + section.Size)
|
||||
return section;
|
||||
}
|
||||
return new Section { Offset = Whole.Size, Size = 0 };
|
||||
}
|
||||
|
||||
public long FindAsciiString (Section section, string seq, int step = 1)
|
||||
{
|
||||
return FindString (section, Encoding.ASCII.GetBytes (seq), step);
|
||||
@ -152,26 +184,101 @@ namespace GameRes.Formats
|
||||
return FindString (section, bytes, step);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert virtual address into raw file offset.
|
||||
/// </summary>
|
||||
public long GetAddressOffset (uint address)
|
||||
{
|
||||
var section = GetAddressSection (address);
|
||||
if (null == section)
|
||||
return m_file.MaxOffset;
|
||||
uint rva = address - ImageBase;
|
||||
return section.PointerToRawData + (rva - section.VirtualAddress);
|
||||
}
|
||||
|
||||
public string GetCString (uint address)
|
||||
{
|
||||
return GetCString (address, Encodings.cp932);
|
||||
}
|
||||
|
||||
static readonly byte[] ZeroByte = new byte[1] { 0 };
|
||||
|
||||
/// <summary>
|
||||
/// Returns null-terminated string from specified virtual address.
|
||||
/// </summary>
|
||||
public string GetCString (uint address, Encoding enc)
|
||||
{
|
||||
var section = GetAddressSection (address);
|
||||
if (null == section)
|
||||
return null;
|
||||
uint rva = address - ImageBase;
|
||||
uint offset = section.PointerToRawData + (rva - section.VirtualAddress);
|
||||
uint size = section.PointerToRawData + section.SizeOfRawData - offset;
|
||||
long eos = FindString (new Section { Offset = offset, Size = size }, ZeroByte);
|
||||
if (eos < 0)
|
||||
return null;
|
||||
return View.ReadString (offset, (uint)(eos - offset), enc);
|
||||
}
|
||||
|
||||
private ImageSection GetAddressSection (uint address)
|
||||
{
|
||||
var img_base = ImageBase;
|
||||
if (address < img_base)
|
||||
throw new ArgumentException ("Invalid virtual address.");
|
||||
if (null == m_section_list)
|
||||
InitSectionTable();
|
||||
uint rva = address - img_base;
|
||||
foreach (var section in m_section_list)
|
||||
{
|
||||
if (rva >= section.VirtualAddress && rva < section.VirtualAddress + section.SizeOfRawData)
|
||||
return section;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void InitImageBase ()
|
||||
{
|
||||
long hdr_offset = GetHeaderOffset() + 0x18;
|
||||
if (View.ReadUInt16 (hdr_offset) != 0x010B)
|
||||
throw new InvalidFormatException ("File is not a valid Windows 32-bit executable.");
|
||||
m_image_base = View.ReadUInt32 (hdr_offset+0x1C); // ImageBase
|
||||
}
|
||||
|
||||
private long GetHeaderOffset ()
|
||||
{
|
||||
long pe_offset = View.ReadUInt32 (0x3C);
|
||||
if (pe_offset >= m_file.MaxOffset-0x58 || !View.AsciiEqual (pe_offset, "PE\0\0"))
|
||||
throw new InvalidFormatException ("File is not a valid Windows 32-bit executable.");
|
||||
return pe_offset;
|
||||
}
|
||||
|
||||
private void InitSectionTable ()
|
||||
{
|
||||
long pe_offset = m_file.View.ReadUInt32 (0x3C);
|
||||
if (pe_offset >= m_file.MaxOffset-0x58 || !m_file.View.AsciiEqual (pe_offset, "PE\0\0"))
|
||||
throw new InvalidFormatException ("File is not a valid win32 executable.");
|
||||
|
||||
int opt_header = m_file.View.ReadUInt16 (pe_offset+0x14); // SizeOfOptionalHeader
|
||||
long offset = m_file.View.ReadUInt32 (pe_offset+0x54); // SizeOfHeaders
|
||||
long pe_offset = GetHeaderOffset();
|
||||
int opt_header = View.ReadUInt16 (pe_offset+0x14); // SizeOfOptionalHeader
|
||||
long section_table = pe_offset+opt_header+0x18;
|
||||
int count = m_file.View.ReadUInt16 (pe_offset+6); // NumberOfSections
|
||||
long offset = View.ReadUInt32 (pe_offset+0x54); // SizeOfHeaders
|
||||
int count = View.ReadUInt16 (pe_offset+6); // NumberOfSections
|
||||
var table = new Dictionary<string, Section> (count);
|
||||
var list = new List<ImageSection> (count);
|
||||
if (section_table + 0x28*count < m_file.MaxOffset)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var name = m_file.View.ReadString (section_table, 8);
|
||||
var section = new Section {
|
||||
Size = m_file.View.ReadUInt32 (section_table+0x10),
|
||||
Offset = m_file.View.ReadUInt32 (section_table+0x14)
|
||||
var name = View.ReadString (section_table, 8);
|
||||
var img_section = new ImageSection {
|
||||
Name = name,
|
||||
VirtualSize = View.ReadUInt32 (section_table+0x08),
|
||||
VirtualAddress = View.ReadUInt32 (section_table+0x0C),
|
||||
SizeOfRawData = View.ReadUInt32 (section_table+0x10),
|
||||
PointerToRawData = View.ReadUInt32 (section_table+0x14),
|
||||
Characteristics = View.ReadUInt32 (section_table+0x24),
|
||||
};
|
||||
var section = new Section {
|
||||
Offset = img_section.PointerToRawData,
|
||||
Size = img_section.SizeOfRawData
|
||||
};
|
||||
list.Add (img_section);
|
||||
if (!table.ContainsKey (name))
|
||||
table.Add (name, section);
|
||||
if (0 != section.Size)
|
||||
@ -183,6 +290,7 @@ namespace GameRes.Formats
|
||||
m_overlay.Offset = offset;
|
||||
m_overlay.Size = (uint)(m_file.MaxOffset - offset);
|
||||
m_section_table = table;
|
||||
m_section_list = list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -112,6 +112,7 @@ namespace GameRes.Formats.FC01
|
||||
{
|
||||
var options = Query<McgOptions> (arcStrings.ArcImageEncrypted);
|
||||
key = options.Key;
|
||||
LastKey = key;
|
||||
}
|
||||
else
|
||||
key = LastKey.Value;
|
||||
|
@ -54,6 +54,11 @@ namespace GameRes.Formats.FrontWing
|
||||
public override bool IsHierarchic { get { return true; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public FltOpener()
|
||||
{
|
||||
ContainedFormats = new[] { "FG/FWGI", "OGG", "DAT/GENERIC" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.View.AsciiEqual (4, "PACKDATA0000"))
|
||||
@ -130,4 +135,9 @@ namespace GameRes.Formats.FrontWing
|
||||
0x22, 0x45, 0xF2, 0x65, 0x74, 0x34, 0x35, 0xDE, 0x59, 0x27, 0xA3, 0xFB, 0x0C, 0x80, 0x32, 0xFF,
|
||||
};
|
||||
}
|
||||
|
||||
[Export(typeof(ResourceAlias))]
|
||||
[ExportMetadata("Extension", "CSF")]
|
||||
[ExportMetadata("Target", "DAT/GENERIC")]
|
||||
public class CsfFormat : ResourceAlias { }
|
||||
}
|
||||
|
@ -23,9 +23,9 @@
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media.Imaging;
|
||||
using GameRes.Compression;
|
||||
|
||||
namespace GameRes.Formats.FrontWing
|
||||
@ -94,7 +94,7 @@ namespace GameRes.Formats.FrontWing
|
||||
{
|
||||
var fge_name = Path.ChangeExtension (file.Name, ".fge");
|
||||
if (!VFS.FileExists (fge_name))
|
||||
return null;
|
||||
throw new OperationCanceledException("Required metadata file '"+Path.GetFileName(fge_name)+"' not found.");
|
||||
using (var fg = OpenFg (file))
|
||||
{
|
||||
return Fwgi.Value.ReadMetaData (fg);
|
||||
|
@ -230,7 +230,7 @@ namespace GameRes.Formats.Gs
|
||||
}
|
||||
|
||||
[Export(typeof(ScriptFormat))]
|
||||
public class GsScriptFormat : ScriptFormat
|
||||
public class GsScriptFormat : GenericScriptFormat
|
||||
{
|
||||
public override string Tag { get { return "SCW"; } }
|
||||
public override string Description { get { return "GsWin script file"; } }
|
||||
@ -240,15 +240,5 @@ namespace GameRes.Formats.Gs
|
||||
{
|
||||
Signatures = new uint[] { 0x20574353, 0x35776353, 0x34776353 };
|
||||
}
|
||||
|
||||
public override ScriptData Read (string name, Stream file)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ScriptData script)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ namespace GameRes.Formats.HSP
|
||||
|
||||
public DpmOpener ()
|
||||
{
|
||||
Extensions = new string[] { "dpm", "bin" };
|
||||
Extensions = new string[] { "dpm", "bin", "dat" };
|
||||
Signatures = new uint[] { 0x584D5044, 0 };
|
||||
}
|
||||
|
||||
|
239
ArcFormats/Illusion/ArcPP.cs
Normal file
239
ArcFormats/Illusion/ArcPP.cs
Normal file
@ -0,0 +1,239 @@
|
||||
//! \file ArcPP.cs
|
||||
//! \date 2022 Jun 01
|
||||
//! \brief Illusion resource archive.
|
||||
//
|
||||
// Copyright (C) 2022 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using GameRes.Utility;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
|
||||
namespace GameRes.Formats.Illusion
|
||||
{
|
||||
[Serializable]
|
||||
public class PpEncryptionScheme
|
||||
{
|
||||
public byte Method;
|
||||
public uint[] Key;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class PpScheme : ResourceScheme
|
||||
{
|
||||
public IDictionary<string, PpEncryptionScheme> KnownKeys;
|
||||
}
|
||||
|
||||
public class PpArchive : ArcFile
|
||||
{
|
||||
public readonly byte Method;
|
||||
public readonly PpEncryptionScheme Scheme;
|
||||
|
||||
public PpArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, byte encMethod, PpEncryptionScheme scheme)
|
||||
: base (arc, impl, dir)
|
||||
{
|
||||
Method = encMethod;
|
||||
Scheme = scheme;
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class PpOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "PP/ILLUSION"; } }
|
||||
public override string Description { get { return "Illusion resource archive"; } }
|
||||
public override uint Signature { get { return 0x5650505B; } } // '[PPVER]'
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
static readonly byte[][] DefaultIndexKey = new byte[][] {
|
||||
new byte[] { 0xFA, 0x49, 0x7B, 0x1C, 0xF9, 0x4D, 0x83, 0x0A },
|
||||
new byte[] { 0x3A, 0xE3, 0x87, 0xC2, 0xBD, 0x1E, 0xA6, 0xFE }
|
||||
};
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.View.AsciiEqual (0, "[PPVER]\0"))
|
||||
return null;
|
||||
|
||||
var buffer = file.View.ReadBytes (8, 9);
|
||||
|
||||
DecryptIndex (buffer, 0, 4);
|
||||
int version = buffer.ToInt32 (0);
|
||||
|
||||
DecryptIndex (buffer, 4, 1);
|
||||
byte encryption_method = buffer[4];
|
||||
if (encryption_method > 4)
|
||||
return null;
|
||||
|
||||
DecryptIndex (buffer, 5, 4);
|
||||
int count = buffer.ToInt32 (5);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
|
||||
if (version < 0x6C)
|
||||
return null;
|
||||
var dir = new List<Entry> (count);
|
||||
uint index_offset = 0x11;
|
||||
uint index_size = (uint)(count * 0x120);
|
||||
var index = file.View.ReadBytes (index_offset, index_size);
|
||||
index_offset += index_size;
|
||||
DecryptIndex (index, 0, index.Length);
|
||||
int pos = 0;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var name = Binary.GetCString (index, pos, 0x104);
|
||||
pos += 0x104;
|
||||
var entry = Create<Entry> (name);
|
||||
entry.Size = index.ToUInt32 (pos);
|
||||
entry.Offset = index.ToUInt32 (pos+4);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
pos += 0x1C;
|
||||
}
|
||||
var scheme = QueryEncryptionScheme (file);
|
||||
if (null == scheme)
|
||||
return null;
|
||||
return new PpArchive (file, this, dir, encryption_method, scheme);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var parc = arc as PpArchive;
|
||||
if (null == parc || 2 == parc.Method)
|
||||
return base.OpenEntry (arc, entry);
|
||||
var data = arc.File.View.ReadBytes (entry.Offset, entry.Size);
|
||||
if (1 == parc.Method)
|
||||
DecryptData1 (data, parc.Scheme);
|
||||
else if (3 == parc.Method)
|
||||
DecryptData3 (data, parc.Scheme);
|
||||
else if (4 == parc.Method)
|
||||
data = UnpackData (data);
|
||||
return new BinMemoryStream (data, entry.Name);
|
||||
}
|
||||
|
||||
byte[] UnpackData (byte[] input)
|
||||
{
|
||||
return input; // decompression not implemented
|
||||
}
|
||||
|
||||
void DecryptData1 (byte[] data, PpEncryptionScheme scheme)
|
||||
{
|
||||
if (scheme.Method > 2 || scheme.Key.Length == 0)
|
||||
return;
|
||||
if (0 == scheme.Method)
|
||||
{
|
||||
for (int i = 0; i < data.Length; ++i)
|
||||
{
|
||||
data[i] ^= (byte)scheme.Key[i % scheme.Key.Length];
|
||||
}
|
||||
return;
|
||||
}
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* data8 = data)
|
||||
{
|
||||
if (1 == scheme.Method)
|
||||
{
|
||||
ushort* data16 = (ushort*)data8;
|
||||
int size = data.Length / 2;
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
data16[i] ^= (ushort)scheme.Key[i % scheme.Key.Length];
|
||||
}
|
||||
}
|
||||
else if (2 == scheme.Method)
|
||||
{
|
||||
uint* data32 = (uint*)data8;
|
||||
int size = data.Length / 4;
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
data32[i] ^= scheme.Key[i % scheme.Key.Length];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DecryptData3 (byte[] data, PpEncryptionScheme scheme)
|
||||
{
|
||||
if (scheme.Key.Length < 8)
|
||||
return;
|
||||
var key0 = new ushort[] { (ushort)scheme.Key[0], (ushort)scheme.Key[1], (ushort)scheme.Key[2], (ushort)scheme.Key[3] };
|
||||
var key1 = new ushort[] { (ushort)scheme.Key[4], (ushort)scheme.Key[5], (ushort)scheme.Key[6], (ushort)scheme.Key[7] };
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* data8 = data)
|
||||
{
|
||||
ushort* data16 = (ushort*)data8;
|
||||
int size = data.Length / 2;
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
int k = i & 3;
|
||||
key0[k] += key1[k];
|
||||
data16[i] ^= key0[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void DecryptIndex (byte[] data, int pos, int length)
|
||||
{
|
||||
var key0 = DefaultIndexKey[0].Clone() as byte[];
|
||||
var key1 = DefaultIndexKey[1];
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
int k = i & 7;
|
||||
key0[k] += key1[k];
|
||||
data[pos+i] ^= key0[k];
|
||||
}
|
||||
}
|
||||
|
||||
PpScheme DefaultScheme = new PpScheme
|
||||
{
|
||||
KnownKeys = new Dictionary<string, PpEncryptionScheme>()
|
||||
};
|
||||
|
||||
internal IDictionary<string, PpEncryptionScheme> KnownKeys { get { return DefaultScheme.KnownKeys; } }
|
||||
|
||||
public override ResourceScheme Scheme
|
||||
{
|
||||
get { return DefaultScheme; }
|
||||
set { DefaultScheme = (PpScheme)value; }
|
||||
}
|
||||
|
||||
PpEncryptionScheme QueryEncryptionScheme (ArcView file)
|
||||
{
|
||||
var title = FormatCatalog.Instance.LookupGame (file.Name);
|
||||
if (string.IsNullOrEmpty (title))
|
||||
title = FormatCatalog.Instance.LookupGame (file.Name, @"..\*.exe");
|
||||
if (string.IsNullOrEmpty (title))
|
||||
return null;
|
||||
PpEncryptionScheme key;
|
||||
if (!KnownKeys.TryGetValue (title, out key))
|
||||
return null;
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
@ -43,7 +43,7 @@ namespace GameRes.Formats.CandySoft
|
||||
public FpkOpener ()
|
||||
{
|
||||
Signatures = new uint[] { 0, 1 };
|
||||
ContainedFormats = new[] { "BMP", "KG", "OGG", "SCR", "TXT" };
|
||||
ContainedFormats = new[] { "BMP", "KG", "OGG", "SCR", "TXT", "DAT/GENERIC" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
@ -134,19 +134,19 @@ namespace GameRes.Formats.CandySoft
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
if (entry.Size <= 8)
|
||||
return input;
|
||||
var sign = input.Signature;
|
||||
if (0x32434c5a != sign) // 'ZLC2'
|
||||
return input;
|
||||
|
||||
using (input)
|
||||
using (var reader = new Zlc2Reader (input, (int)entry.Size))
|
||||
IBinaryStream input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
while (input.Length > 8 && input.Signature == 0x32434C5A) // 'ZLC2'
|
||||
{
|
||||
reader.Unpack();
|
||||
return new BinMemoryStream (reader.Data, entry.Name);
|
||||
IBinaryStream unpacked;
|
||||
using (input)
|
||||
using (var reader = new Zlc2Reader (input, (int)input.Length))
|
||||
{
|
||||
reader.Unpack();
|
||||
unpacked = new BinMemoryStream (reader.Data, entry.Name);
|
||||
}
|
||||
input = unpacked;
|
||||
}
|
||||
return input.AsStream;
|
||||
}
|
||||
}
|
||||
|
||||
|
111
ArcFormats/Interheart/ImageBMP.cs
Normal file
111
ArcFormats/Interheart/ImageBMP.cs
Normal file
@ -0,0 +1,111 @@
|
||||
//! \file ImageBMP.cs
|
||||
//! \date 2022 Jun 06
|
||||
//! \brief Candy Soft RLE-compressed bitmap.
|
||||
//
|
||||
// Copyright (C) 2022 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.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
// [980925][Candy Soft] Osananajimi
|
||||
|
||||
namespace GameRes.Formats.Interheart
|
||||
{
|
||||
internal class BmpRleMetaData : ImageMetaData
|
||||
{
|
||||
public int HeaderSize;
|
||||
public int UnpackedSize;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class BmpRleFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "BMP/RLE"; } }
|
||||
public override string Description { get { return "Candy Soft RLE-compressed bitmap"; } }
|
||||
public override uint Signature { get { return 0x32504D42; } } // 'BMP24'
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x26);
|
||||
if (!header.AsciiEqual ("BMP24RLE")) // also "BMP08RLE" and "BMP16RLE"
|
||||
return null;
|
||||
return new BmpRleMetaData {
|
||||
Width = header.ToUInt32 (0x1A),
|
||||
Height = header.ToUInt32 (0x1E),
|
||||
BPP = header.ToUInt16 (0x24),
|
||||
HeaderSize = header.ToInt32 (0x12),
|
||||
UnpackedSize = header.ToInt32 (0x0A),
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var meta = (BmpRleMetaData)info;
|
||||
var output = new byte[meta.UnpackedSize];
|
||||
file.Position = 8;
|
||||
file.Read (output, 0, meta.HeaderSize);
|
||||
var buffer = new byte[3];
|
||||
int dst = meta.HeaderSize;
|
||||
// XXX BMMP08 and BMP16 bitmaps use exactly the same algorithm, just different pixel size
|
||||
file.Read (buffer, 0, 3);
|
||||
while (dst + 3 <= output.Length)
|
||||
{
|
||||
byte r = buffer[0];
|
||||
byte g = buffer[1];
|
||||
byte b = buffer[2];
|
||||
output[dst++] = b;
|
||||
output[dst++] = g;
|
||||
output[dst++] = r;
|
||||
file.Read (buffer, 0, 3);
|
||||
if (r == buffer[0] && g == buffer[1] && b == buffer[2])
|
||||
{
|
||||
if (dst + 3 > output.Length)
|
||||
break;
|
||||
output[dst++] = b;
|
||||
output[dst++] = g;
|
||||
output[dst++] = r;
|
||||
int count = file.ReadUInt16();
|
||||
while (count --> 0)
|
||||
{
|
||||
output[dst++] = b;
|
||||
output[dst++] = g;
|
||||
output[dst++] = r;
|
||||
}
|
||||
file.Read (buffer, 0, 3);
|
||||
}
|
||||
}
|
||||
using (var input = new BinMemoryStream (output))
|
||||
{
|
||||
var decoder = new BmpBitmapDecoder (input, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||
var frame = decoder.Frames[0];
|
||||
frame.Freeze();
|
||||
return new ImageData (frame, info);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("BmpRleFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
}
|
96
ArcFormats/Interheart/ImageHMP.cs
Normal file
96
ArcFormats/Interheart/ImageHMP.cs
Normal file
@ -0,0 +1,96 @@
|
||||
//! \file ImageHMP.cs
|
||||
//! \date 2022 Jun 16
|
||||
//! \brief Candy Soft hover map.
|
||||
//
|
||||
// Copyright (C) 2022 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.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace GameRes.Formats.Interheart
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class HmpFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "HMP"; } }
|
||||
public override string Description { get { return "Interheart hover map"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
if (!file.Name.HasExtension (".hmp"))
|
||||
return null;
|
||||
uint width = file.ReadUInt32();
|
||||
uint height = file.ReadUInt32();
|
||||
if (width == 0 || width > 0x7FFF || height == 0 || height > 0x7FFF)
|
||||
return null;
|
||||
return new ImageMetaData { Width = width, Height = height, BPP = 8 };
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
file.Position = 8;
|
||||
var pixels = file.ReadBytes (info.iWidth * info.iHeight);
|
||||
return ImageData.Create (info, PixelFormats.Indexed8, DefaultPalette, pixels);
|
||||
// return ImageData.Create (info, PixelFormats.Gray8, null, pixels);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("HmpFormat.Write not implemented");
|
||||
}
|
||||
|
||||
static readonly BitmapPalette DefaultPalette = new BitmapPalette (GenerateColors());
|
||||
|
||||
static Color[] GenerateColors ()
|
||||
{
|
||||
var colors = new Color[256];
|
||||
for (int i = 8; i < 256; ++i)
|
||||
{
|
||||
colors[i] = Color.FromRgb ((byte)i, (byte)i, (byte)i);
|
||||
}
|
||||
colors[0] = Color.FromRgb (0x00, 0x00, 0x00);
|
||||
colors[1] = Color.FromRgb (0x00, 0x00, 0x7F);
|
||||
colors[2] = Color.FromRgb (0x00, 0x7F, 0x00);
|
||||
colors[3] = Color.FromRgb (0x00, 0x7F, 0x7F);
|
||||
colors[4] = Color.FromRgb (0x7F, 0x00, 0x00);
|
||||
colors[5] = Color.FromRgb (0x7F, 0x00, 0x7F);
|
||||
colors[6] = Color.FromRgb (0x7F, 0x7F, 0x00);
|
||||
colors[7] = Color.FromRgb (0x7F, 0x7F, 0x7F);
|
||||
colors[8] = Color.FromRgb (0x00, 0x00, 0xFF);
|
||||
colors[9] = Color.FromRgb (0x00, 0xFF, 0x00);
|
||||
colors[10] = Color.FromRgb (0x00, 0xFF, 0xFF);
|
||||
colors[11] = Color.FromRgb (0xFF, 0x00, 0x00);
|
||||
colors[12] = Color.FromRgb (0xFF, 0x00, 0xFF);
|
||||
colors[13] = Color.FromRgb (0xFF, 0xFF, 0x00);
|
||||
colors[14] = Color.FromRgb (0xFF, 0xFF, 0xFF);
|
||||
colors[15] = Color.FromRgb (0xFF, 0x00, 0x7F);
|
||||
colors[16] = Color.FromRgb (0xFF, 0x7F, 0x00);
|
||||
colors[16] = Color.FromRgb (0xFF, 0x7F, 0x7F);
|
||||
colors[17] = Color.FromRgb (0x7F, 0x00, 0xFF);
|
||||
colors[18] = Color.FromRgb (0xFF, 0xFF, 0x7F);
|
||||
return colors;
|
||||
}
|
||||
}
|
||||
}
|
@ -39,7 +39,7 @@ namespace GameRes.Formats.Kaguya
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class ArcOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "ARI"; } }
|
||||
public override string Tag { get { return "ARC/ARI"; } }
|
||||
public override string Description { get { return "KaGuYa script engine resource archive"; } }
|
||||
public override uint Signature { get { return 0x314c4657; } } // 'WFL1'
|
||||
public override bool IsHierarchic { get { return true; } }
|
||||
@ -48,11 +48,12 @@ namespace GameRes.Formats.Kaguya
|
||||
public ArcOpener ()
|
||||
{
|
||||
Extensions = new string[] { "arc" };
|
||||
ContainedFormats = new[] { "AP", "APS3", "OGG", "DAT/GENERIC" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
var reader = new IndexReader();
|
||||
var reader = new IndexReader (this);
|
||||
var dir = reader.ReadIndex (file);
|
||||
if (null == dir || 0 == dir.Count)
|
||||
return null;
|
||||
@ -77,9 +78,15 @@ namespace GameRes.Formats.Kaguya
|
||||
|
||||
internal class IndexReader
|
||||
{
|
||||
ArchiveFormat m_format;
|
||||
byte[] m_name_buf = new byte[0x20];
|
||||
List<Entry> m_dir = new List<Entry>();
|
||||
|
||||
public IndexReader (ArchiveFormat format)
|
||||
{
|
||||
m_format = format;
|
||||
}
|
||||
|
||||
public List<Entry> ReadIndex (ArcView file)
|
||||
{
|
||||
string ari_name = Path.ChangeExtension (file.Name, "ari");
|
||||
@ -163,7 +170,7 @@ namespace GameRes.Formats.Kaguya
|
||||
else if (1 == entry.Mode)
|
||||
entry.Type = "image";
|
||||
else
|
||||
entry.Type = FormatCatalog.Instance.GetTypeFromName (entry.Name);
|
||||
entry.Type = FormatCatalog.Instance.GetTypeFromName (entry.Name, m_format.ContainedFormats);
|
||||
}
|
||||
|
||||
string ReadName (ArcView file, long offset, int name_len)
|
||||
@ -246,4 +253,9 @@ namespace GameRes.Formats.Kaguya
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
[Export(typeof(ResourceAlias))]
|
||||
[ExportMetadata("Extension", "TBL")]
|
||||
[ExportMetadata("Target", "DAT/GENERIC")]
|
||||
public class TblFormat : ResourceAlias { }
|
||||
}
|
||||
|
@ -228,8 +228,7 @@ namespace GameRes.Formats.Kaguya
|
||||
|
||||
public virtual LinkEncryption GetEncryption ()
|
||||
{
|
||||
var params_dir = VFS.GetDirectoryName (m_input.Name);
|
||||
var params_dat = VFS.CombinePath (params_dir, "params.dat");
|
||||
var params_dat = VFS.ChangeFileName (m_input.Name, "params.dat");
|
||||
if (!VFS.FileExists (params_dat))
|
||||
return null;
|
||||
|
||||
@ -555,8 +554,10 @@ namespace GameRes.Formats.Kaguya
|
||||
m_input.Position = start;
|
||||
SkipChunk();
|
||||
m_title = ReadString();
|
||||
if (m_version.Major < 3)
|
||||
if (m_version.Major < 2)
|
||||
m_input.ReadCString();
|
||||
// else
|
||||
// SkipString();
|
||||
SkipString();
|
||||
SkipString();
|
||||
m_input.ReadByte();
|
||||
@ -583,7 +584,7 @@ namespace GameRes.Formats.Kaguya
|
||||
{
|
||||
ReadHeader (0x17);
|
||||
|
||||
if ("幼なじみと甘~くエッチに過ごす方法" == m_title)
|
||||
if ("幼なじみと甘~くエッチに過ごす方法" == m_title || "艶女医" == m_title)
|
||||
{
|
||||
int count = m_input.ReadUInt8();
|
||||
for (int i = 0; i < count; ++i)
|
||||
@ -595,8 +596,15 @@ namespace GameRes.Formats.Kaguya
|
||||
}
|
||||
SkipArray();
|
||||
SkipArray();
|
||||
m_input.ReadInt32();
|
||||
return m_input.ReadBytes (240000);
|
||||
if ("幼なじみと甘~くエッチに過ごす方法" == m_title)
|
||||
{
|
||||
m_input.ReadInt32();
|
||||
return m_input.ReadBytes (240000);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ReadKey();
|
||||
}
|
||||
}
|
||||
else // 毎日がM!
|
||||
{
|
||||
|
166
ArcFormats/KiriKiri/CroixCrypt.cs
Normal file
166
ArcFormats/KiriKiri/CroixCrypt.cs
Normal file
@ -0,0 +1,166 @@
|
||||
//! \file CroixCrypt.cs
|
||||
//! \date 2017 Dec 27
|
||||
//! \brief Croix encryption algorithm for KiriKiri.
|
||||
//
|
||||
// Copyright (C) 2017 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.
|
||||
//
|
||||
|
||||
namespace GameRes.Formats.KiriKiri
|
||||
{
|
||||
[Serializable]
|
||||
public class CroixCrypt : ICrypt
|
||||
{
|
||||
// ulong sigdata_checksum = 0xA86704CF2727BE76;
|
||||
ulong sigdata_checksum = 0x6C04B9AB66EF2EF0;
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] data, int pos, int count)
|
||||
{
|
||||
uint v16 = 0x6EDC44A8;
|
||||
uint v17 = 0x139E;
|
||||
uint v19 = 0x4B93;
|
||||
for (int i = 0; i < 0x3D; ++i)
|
||||
{
|
||||
v17 += byte_10014F64[i];
|
||||
v19 += v17;
|
||||
}
|
||||
v17 %= 0xFFEFu;
|
||||
v19 %= 0xFFEFu;
|
||||
uint v21 = v17 ^ (v19 << 16);
|
||||
uint v22 = (v21 & 0xF | ((v21 & 0xFFFFFFF0) << 14)) << 3
|
||||
| ((v21 & 0x18000 | ((v21 & 0x1F00000
|
||||
| ((v21 & 0xE0000 | (v21 >> 1) & 0x7F000000) >> 13)) >> 3)) >> 1);
|
||||
uint v25 = ~(((v16 & 0x78 | (v16 >> 14) & 0x3FF80) >> 3) | (v16 & 0xC000 | (v16 & 0x1F0000
|
||||
| ((v16 & 7 | 2 * (v16 & 0xFFFFFF80)) << 13)) << 3) << 1);
|
||||
for (int i = 0; i < 0x3D; ++i)
|
||||
{
|
||||
v25 = dword_10014070[(v25 ^ byte_10014F64[i]) & 0xFF] ^ (v25 >> 8);
|
||||
}
|
||||
v25 = ~v25;
|
||||
v25 = (v25 & 0xF | ((v25 & 0xFFFFFFF0) << 14)) << 3
|
||||
| (v25 & 0x18000 | ((v25 & 0x1F00000 | ((v25 & 0xE0000 | (v25 >> 1) & 0x7F000000) >> 13)) >> 3)) >> 1;
|
||||
uint v29 = 0x5793CE00;
|
||||
uint v71 = v22 ^ (v29 << 21);
|
||||
uint v75 = v25 ^ (v29 >> 11);
|
||||
ulong v33 = sigdata_checksum ^ 0x5793CE00u ^ v71 ^ ((ulong)v75 << 32);
|
||||
uint v55 = (uint)(v33 >> 17);
|
||||
uint hash = entry.Hash ^ v55 ^ 0x15C3F972u;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
int shift = (((int)offset + i) & 3) << 3;
|
||||
uint v40 = data[pos+i] ^ (hash >> shift);
|
||||
v40 -= 0xE3AD9ACBu >> shift;
|
||||
v40 ^= (byte)(offset >> ((((int)offset + i) & 7) << 3));
|
||||
v40 += 0xFECAB9F2u >> shift;
|
||||
data[pos+i] = (byte)v40;
|
||||
}
|
||||
hash = entry.Hash ^ 0x27D3BCA1;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
int shift = (((int)offset + i) & 3) << 3;
|
||||
uint v40 = data[pos+i] ^ (hash >> shift);
|
||||
v40 -= 0xE3779ACBu >> shift;
|
||||
v40 ^= (byte)(offset >> ((((int)offset + i) & 7) << 3));
|
||||
v40 += 0xFDCAB972u >> shift;
|
||||
data[pos+i] = (byte)v40;
|
||||
}
|
||||
hash = entry.Hash & 0x7FFFFFFF;
|
||||
var v83 = new byte[0x1F];
|
||||
for (int i = 0; i < 0x1F; ++i)
|
||||
{
|
||||
v83[i] = (byte)hash;
|
||||
hash = v55 ^ (hash >> 8 | (hash & 0xFF) << 23);
|
||||
}
|
||||
uint v58 = 0;
|
||||
uint v59 = 0;
|
||||
var v84 = new byte[0x3D];
|
||||
for (int i = 0; i < 0x3D; ++i)
|
||||
{
|
||||
byte v61 = (byte)v58;
|
||||
v84[i] = v61;
|
||||
v58 = v55 ^ (uint)(v59 << 24 | v58 >> 8);
|
||||
v59 = v59 >> 8 | (uint)v61 << 21;
|
||||
}
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
long v63 = offset + i;
|
||||
data[pos+i] ^= v83[v63 % 0x1F];
|
||||
data[pos+i] += (byte)(v55 ^ v84[v63 % 0x3D] ^ byte_10014F64[v63 % 0x3D]);
|
||||
}
|
||||
}
|
||||
|
||||
static ulong v160 = 0xA5665A5F061EC576;
|
||||
static ulong CRC = 0xE51804DAE70D133E;
|
||||
|
||||
// static ulong v160 = 0xA6129778061EC576;
|
||||
// static ulong CRC = 0xE4EFF66CE70D133E;
|
||||
static byte[] byte_10014F64 = InitTable();
|
||||
|
||||
static byte[] InitTable ()
|
||||
{
|
||||
var key = new byte[0x3D];
|
||||
uint v58 = (uint)(v160 + CRC);
|
||||
uint v59 = (uint)((v160 + CRC) >> 32) & 0x1FFFFFFF;
|
||||
for (int i = 0; i < 0x3D; ++i)
|
||||
{
|
||||
byte v61 = (byte)v58;
|
||||
key[i] = v61;
|
||||
v58 = v59 << 24 | v58 >> 8;
|
||||
v59 = v59 >> 8 | (uint)v61 << 21;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
static readonly uint[] dword_10014070 = {
|
||||
0x00000000, 0x09073096, 0x120E612C, 0x1B0951BA, 0xFF6DC419, 0xF66AF48F, 0xED63A535, 0xE46495A3,
|
||||
0xFEDB8832, 0xF7DCB8A4, 0xECD5E91E, 0xE5D2D988, 0x01B64C2B, 0x08B17CBD, 0x13B82D07, 0x1ABF1D91,
|
||||
0xFDB71064, 0xF4B020F2, 0xEFB97148, 0xE6BE41DE, 0x02DAD47D, 0x0BDDE4EB, 0x10D4B551, 0x19D385C7,
|
||||
0x036C9856, 0x0A6BA8C0, 0x1162F97A, 0x1865C9EC, 0xFC015C4F, 0xF5066CD9, 0xEE0F3D63, 0xE7080DF5,
|
||||
0xFB6E20C8, 0xF269105E, 0xE96041E4, 0xE0677172, 0x0403E4D1, 0x0D04D447, 0x160D85FD, 0x1F0AB56B,
|
||||
0x05B5A8FA, 0x0CB2986C, 0x17BBC9D6, 0x1EBCF940, 0xFAD86CE3, 0xF3DF5C75, 0xE8D60DCF, 0xE1D13D59,
|
||||
0x06D930AC, 0x0FDE003A, 0x14D75180, 0x1DD06116, 0xF9B4F4B5, 0xF0B3C423, 0xEBBA9599, 0xE2BDA50F,
|
||||
0xF802B89E, 0xF1058808, 0xEA0CD9B2, 0xE30BE924, 0x076F7C87, 0x0E684C11, 0x15611DAB, 0x1C662D3D,
|
||||
0xF6DC4190, 0xFFDB7106, 0xE4D220BC, 0xEDD5102A, 0x09B18589, 0x00B6B51F, 0x1BBFE4A5, 0x12B8D433,
|
||||
0x0807C9A2, 0x0100F934, 0x1A09A88E, 0x130E9818, 0xF76A0DBB, 0xFE6D3D2D, 0xE5646C97, 0xEC635C01,
|
||||
0x0B6B51F4, 0x026C6162, 0x196530D8, 0x1062004E, 0xF40695ED, 0xFD01A57B, 0xE608F4C1, 0xEF0FC457,
|
||||
0xF5B0D9C6, 0xFCB7E950, 0xE7BEB8EA, 0xEEB9887C, 0x0ADD1DDF, 0x03DA2D49, 0x18D37CF3, 0x11D44C65,
|
||||
0x0DB26158, 0x04B551CE, 0x1FBC0074, 0x16BB30E2, 0xF2DFA541, 0xFBD895D7, 0xE0D1C46D, 0xE9D6F4FB,
|
||||
0xF369E96A, 0xFA6ED9FC, 0xE1678846, 0xE860B8D0, 0x0C042D73, 0x05031DE5, 0x1E0A4C5F, 0x170D7CC9,
|
||||
0xF005713C, 0xF90241AA, 0xE20B1010, 0xEB0C2086, 0x0F68B525, 0x066F85B3, 0x1D66D409, 0x1461E49F,
|
||||
0x0EDEF90E, 0x07D9C998, 0x1CD09822, 0x15D7A8B4, 0xF1B33D17, 0xF8B40D81, 0xE3BD5C3B, 0xEABA6CAD,
|
||||
0xEDB88320, 0xE4BFB3B6, 0xFFB6E20C, 0xF6B1D29A, 0x12D54739, 0x1BD277AF, 0x00DB2615, 0x09DC1683,
|
||||
0x13630B12, 0x1A643B84, 0x016D6A3E, 0x086A5AA8, 0xEC0ECF0B, 0xE509FF9D, 0xFE00AE27, 0xF7079EB1,
|
||||
0x100F9344, 0x1908A3D2, 0x0201F268, 0x0B06C2FE, 0xEF62575D, 0xE66567CB, 0xFD6C3671, 0xF46B06E7,
|
||||
0xEED41B76, 0xE7D32BE0, 0xFCDA7A5A, 0xF5DD4ACC, 0x11B9DF6F, 0x18BEEFF9, 0x03B7BE43, 0x0AB08ED5,
|
||||
0x16D6A3E8, 0x1FD1937E, 0x04D8C2C4, 0x0DDFF252, 0xE9BB67F1, 0xE0BC5767, 0xFBB506DD, 0xF2B2364B,
|
||||
0xE80D2BDA, 0xE10A1B4C, 0xFA034AF6, 0xF3047A60, 0x1760EFC3, 0x1E67DF55, 0x056E8EEF, 0x0C69BE79,
|
||||
0xEB61B38C, 0xE266831A, 0xF96FD2A0, 0xF068E236, 0x140C7795, 0x1D0B4703, 0x060216B9, 0x0F05262F,
|
||||
0x15BA3BBE, 0x1CBD0B28, 0x07B45A92, 0x0EB36A04, 0xEAD7FFA7, 0xE3D0CF31, 0xF8D99E8B, 0xF1DEAE1D,
|
||||
0x1B64C2B0, 0x1263F226, 0x096AA39C, 0x006D930A, 0xE40906A9, 0xED0E363F, 0xF6076785, 0xFF005713,
|
||||
0xE5BF4A82, 0xECB87A14, 0xF7B12BAE, 0xFEB61B38, 0x1AD28E9B, 0x13D5BE0D, 0x08DCEFB7, 0x01DBDF21,
|
||||
0xE6D3D2D4, 0xEFD4E242, 0xF4DDB3F8, 0xFDDA836E, 0x19BE16CD, 0x10B9265B, 0x0BB077E1, 0x02B74777,
|
||||
0x18085AE6, 0x110F6A70, 0x0A063BCA, 0x03010B5C, 0xE7659EFF, 0xEE62AE69, 0xF56BFFD3, 0xFC6CCF45,
|
||||
0xE00AE278, 0xE90DD2EE, 0xF2048354, 0xFB03B3C2, 0x1F672661, 0x166016F7, 0x0D69474D, 0x046E77DB,
|
||||
0x1ED16A4A, 0x17D65ADC, 0x0CDF0B66, 0x05D83BF0, 0xE1BCAE53, 0xE8BB9EC5, 0xF3B2CF7F, 0xFAB5FFE9,
|
||||
0x1DBDF21C, 0x14BAC28A, 0x0FB39330, 0x06B4A3A6, 0xE2D03605, 0xEBD70693, 0xF0DE5729, 0xF9D967BF,
|
||||
0xE3667A2E, 0xEA614AB8, 0xF1681B02, 0xF86F2B94, 0x1C0BBE37, 0x150C8EA1, 0x0E05DF1B, 0x0702EF8D,
|
||||
};
|
||||
}
|
||||
}
|
@ -1406,4 +1406,43 @@ namespace GameRes.Formats.KiriKiri
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class GensouCrypt : ICrypt
|
||||
{
|
||||
byte[] Key1;
|
||||
uint[] Key2;
|
||||
|
||||
public GensouCrypt (byte[] key1, uint[] key2)
|
||||
{
|
||||
Key1 = key1;
|
||||
Key2 = key2;
|
||||
StartupTjsNotEncrypted = true;
|
||||
}
|
||||
|
||||
public override void Decrypt (Xp3Entry entry, long offset, byte[] buffer, int pos, int count)
|
||||
{
|
||||
ushort off16 = (ushort)offset;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
buffer[pos+i] ^= (byte)(Key1[(Key2[off16 >> 3] >> ((off16 & 7) << 2)) & 0xF]
|
||||
^ ((Key2[off16 >> 3] ^ entry.Hash) >> ((off16 & 3) << 3)));
|
||||
off16++;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Encrypt (Xp3Entry entry, long offset, byte[] buffer, int pos, int count)
|
||||
{
|
||||
Decrypt (entry, offset, buffer, pos, count);
|
||||
}
|
||||
|
||||
public override void Init (ArcFile arc)
|
||||
{
|
||||
foreach (Xp3Entry entry in arc.Dir.Where (e => VFS.IsPathEqualsToFileName (e.Name, "btext.dll")
|
||||
|| VFS.IsPathEqualsToFileName (e.Name, "initialize.tjs")))
|
||||
{
|
||||
entry.Cipher = Xp3Opener.NoCryptAlgorithm;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ namespace GameRes.Formats.Lambda
|
||||
internal class ClsMetaData : ImageMetaData
|
||||
{
|
||||
public int FrameOffset;
|
||||
public bool IsCompressed;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
@ -67,8 +68,7 @@ namespace GameRes.Formats.Lambda
|
||||
int y = file.ReadInt32();
|
||||
|
||||
file.Position = frame_offset+0x30;
|
||||
if (file.ReadByte() != 1)
|
||||
return null;
|
||||
bool compressed = file.ReadByte() != 0;
|
||||
int format = file.ReadByte();
|
||||
if (format != 4 && format != 5 && format != 2)
|
||||
return null;
|
||||
@ -79,6 +79,7 @@ namespace GameRes.Formats.Lambda
|
||||
OffsetY = y,
|
||||
BPP = 8 * (format - 1),
|
||||
FrameOffset = frame_offset,
|
||||
IsCompressed = compressed,
|
||||
};
|
||||
}
|
||||
|
||||
@ -104,6 +105,7 @@ namespace GameRes.Formats.Lambda
|
||||
int m_channels;
|
||||
int m_base_offset;
|
||||
int[] m_rows_sizes;
|
||||
bool m_compressed;
|
||||
|
||||
public PixelFormat Format { get; private set; }
|
||||
public BitmapPalette Palette { get; private set; }
|
||||
@ -112,6 +114,7 @@ namespace GameRes.Formats.Lambda
|
||||
{
|
||||
m_input = input;
|
||||
m_base_offset = info.FrameOffset;
|
||||
m_compressed = info.IsCompressed;
|
||||
m_width = (int)info.Width;
|
||||
m_height = (int)info.Height;
|
||||
m_channels = info.BPP / 8;
|
||||
@ -120,7 +123,7 @@ namespace GameRes.Formats.Lambda
|
||||
else if (4 == m_channels)
|
||||
Format = PixelFormats.Bgra32;
|
||||
else
|
||||
Format = PixelFormats.Bgr32;
|
||||
Format = PixelFormats.Bgr24;
|
||||
m_channel = new byte[m_width * m_height];
|
||||
m_rows_sizes = new int[m_height];
|
||||
}
|
||||
@ -148,13 +151,20 @@ namespace GameRes.Formats.Lambda
|
||||
UnpackChannel (sizes[0]);
|
||||
return m_channel;
|
||||
}
|
||||
var output = new byte[m_width * m_height * 4];
|
||||
else if (!m_compressed)
|
||||
{
|
||||
var pixels = new byte[sizes[0]];
|
||||
SetPosition (offsets[0]);
|
||||
m_input.Read (pixels, 0, pixels.Length);
|
||||
return pixels;
|
||||
}
|
||||
var output = new byte[m_width * m_height * m_channels];
|
||||
for (int i = 0; i < m_channels; ++i)
|
||||
{
|
||||
SetPosition (offsets[i]);
|
||||
UnpackChannel (sizes[i]);
|
||||
int src = 0;
|
||||
for (int dst = ChannelOrder[i]; dst < output.Length; dst += 4)
|
||||
for (int dst = ChannelOrder[i]; dst < output.Length; dst += m_channels)
|
||||
{
|
||||
output[dst] = m_channel[src++];
|
||||
}
|
||||
@ -169,6 +179,11 @@ namespace GameRes.Formats.Lambda
|
||||
|
||||
void UnpackChannel (int size)
|
||||
{
|
||||
if (!m_compressed)
|
||||
{
|
||||
ReadV0 (size);
|
||||
return;
|
||||
}
|
||||
int method = Binary.BigEndian (m_input.ReadUInt16());
|
||||
if (method > 1)
|
||||
throw new InvalidFormatException();
|
||||
|
@ -181,7 +181,7 @@ namespace GameRes.Formats.Liar
|
||||
}
|
||||
|
||||
//[Export(typeof(ScriptFormat))]
|
||||
public class GscFormat : ScriptFormat
|
||||
public class GscFormat : GenericScriptFormat
|
||||
{
|
||||
public override string Tag { get { return "GSC"; } }
|
||||
public override string Description { get { return Strings.arcStrings.GSCDescription; } }
|
||||
|
@ -41,6 +41,11 @@ namespace GameRes.Formats.Lilim
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public AosOpener ()
|
||||
{
|
||||
ContainedFormats = new[] { "BMP", "ABM", "IMG/BMP", "DAT/GENERIC", "OGG" };
|
||||
}
|
||||
|
||||
static readonly byte[] IndexLink = Enumerable.Repeat<byte> (0xff, 0x10).ToArray();
|
||||
static readonly byte[] IndexEnd = Enumerable.Repeat<byte> (0, 0x10).ToArray();
|
||||
|
||||
|
@ -32,13 +32,13 @@ using System.Text;
|
||||
using GameRes.Compression;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Selen
|
||||
namespace GameRes.Formats.Macromedia
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class CctOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "CCT"; } }
|
||||
public override string Description { get { return "Macromedia Director resource archive"; } }
|
||||
public override string Description { get { return "Macromedia Shockwave resource archive"; } }
|
||||
public override uint Signature { get { return 0x52494658; } } // 'XFIR'
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
286
ArcFormats/Macromedia/ArcDXR.cs
Normal file
286
ArcFormats/Macromedia/ArcDXR.cs
Normal file
@ -0,0 +1,286 @@
|
||||
//! \file ArcDXR.cs
|
||||
//! \date 2023 Aug 17
|
||||
//! \brief Macromedia Director presentation container.
|
||||
//
|
||||
// Copyright (C) 2023 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using GameRes.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace GameRes.Formats.Macromedia
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class DxrOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get => "DXR"; }
|
||||
public override string Description { get => "Macromedia Director resource archive"; }
|
||||
public override uint Signature { get => 0x52494658; } // 'XFIR'
|
||||
public override bool IsHierarchic { get => false; }
|
||||
public override bool CanWrite { get => false; }
|
||||
|
||||
public DxrOpener ()
|
||||
{
|
||||
Extensions = new[] { "dxr", "cxt" };
|
||||
Signatures = new[] { 0x52494658u, 0x58464952u };
|
||||
}
|
||||
|
||||
internal static readonly HashSet<string> RawChunks = new HashSet<string> {
|
||||
"RTE0", "RTE1", "FXmp", "VWFI", "VWSC", "Lscr", "STXT",
|
||||
};
|
||||
|
||||
internal bool ConvertText = true;
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
using (var input = file.CreateStream())
|
||||
{
|
||||
ByteOrder ord = input.Signature == 0x52494658u ? ByteOrder.LittleEndian : ByteOrder.BigEndian;
|
||||
var reader = new Reader (input, ord);
|
||||
reader.Position = 4;
|
||||
uint length = reader.ReadU32();
|
||||
var context = new SerializationContext();
|
||||
var dir_file = new DirectorFile();
|
||||
if (!dir_file.Deserialize (context, reader))
|
||||
return null;
|
||||
|
||||
var dir = new List<Entry> ();
|
||||
ImportMedia (dir_file, dir);
|
||||
foreach (MemoryMapEntry entry in dir_file.MMap.Dir)
|
||||
{
|
||||
if (RawChunks.Contains (entry.FourCC))
|
||||
{
|
||||
entry.Name = string.Format ("{0:D6}.{1}", entry.Id, entry.FourCC.Trim());
|
||||
dir.Add (entry);
|
||||
}
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var snd = entry as SoundEntry;
|
||||
if (snd != null)
|
||||
return OpenSound (arc, snd);
|
||||
var ment = entry as MemoryMapEntry;
|
||||
if (!ConvertText || null == ment || ment.FourCC != "STXT")
|
||||
return base.OpenEntry (arc, entry);
|
||||
uint offset = Binary.BigEndian (arc.File.View.ReadUInt32 (entry.Offset));
|
||||
uint length = Binary.BigEndian (arc.File.View.ReadUInt32 (entry.Offset + 4));
|
||||
return arc.File.CreateStream (entry.Offset + offset, length);
|
||||
}
|
||||
|
||||
internal Stream OpenSound (ArcFile arc, SoundEntry entry)
|
||||
{
|
||||
var header = arc.File.View.ReadBytes (entry.Header.Offset, entry.Header.Size);
|
||||
var format = entry.DeserializeHeader (header);
|
||||
var riff = new MemoryStream (0x2C);
|
||||
WaveAudio.WriteRiffHeader (riff, format, entry.Size);
|
||||
if (format.BitsPerSample < 16)
|
||||
{
|
||||
using (riff)
|
||||
{
|
||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
return new PrefixStream (riff.ToArray(), input);
|
||||
}
|
||||
}
|
||||
// samples are stored in big-endian format
|
||||
var samples = arc.File.View.ReadBytes (entry.Offset, entry.Size);
|
||||
for (int i = 1; i < samples.Length; i += 2)
|
||||
{
|
||||
byte s = samples[i-1];
|
||||
samples[i-1] = samples[i];
|
||||
samples[i] = s;
|
||||
}
|
||||
riff.Write (samples, 0, samples.Length);
|
||||
riff.Position = 0;
|
||||
return riff;
|
||||
}
|
||||
|
||||
void ImportMedia (DirectorFile dir_file, List<Entry> dir)
|
||||
{
|
||||
var seen_ids = new HashSet<int>();
|
||||
var mmap = dir_file.MMap;
|
||||
foreach (var cast in dir_file.Casts)
|
||||
{
|
||||
foreach (var piece in cast.Members.Values)
|
||||
{
|
||||
if (seen_ids.Contains (piece.Id))
|
||||
continue;
|
||||
seen_ids.Add (piece.Id);
|
||||
Entry entry = null;
|
||||
if (piece.Type == DataType.Bitmap)
|
||||
entry = ImportBitmap (piece, dir_file, cast);
|
||||
else if (piece.Type == DataType.Sound)
|
||||
entry = ImportSound (piece, dir_file);
|
||||
if (entry != null)
|
||||
dir.Add (entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Entry ImportSound (CastMember sound, DirectorFile dir_file)
|
||||
{
|
||||
var sndHrec = dir_file.KeyTable.FindByCast (sound.Id, "sndH");
|
||||
var sndSrec = dir_file.KeyTable.FindByCast (sound.Id, "sndS");
|
||||
if (sndHrec == null || sndSrec == null)
|
||||
return null;
|
||||
var name = sound.Info.Name;
|
||||
var sndH = dir_file.MMap[sndHrec.Id];
|
||||
var sndS = dir_file.MMap[sndSrec.Id];
|
||||
if (string.IsNullOrEmpty (name))
|
||||
name = sndSrec.Id.ToString ("D6");
|
||||
return new SoundEntry
|
||||
{
|
||||
Name = name + ".snd",
|
||||
Type = "audio",
|
||||
Offset = sndS.Offset,
|
||||
Size = sndS.Size,
|
||||
Header = sndH,
|
||||
};
|
||||
}
|
||||
|
||||
Entry ImportBitmap (CastMember bitmap, DirectorFile dir_file, Cast cast)
|
||||
{
|
||||
var bitd = dir_file.KeyTable.FindByCast (bitmap.Id, "BITD");
|
||||
if (bitd == null)
|
||||
return null;
|
||||
var entry = new BitmapEntry();
|
||||
entry.DeserializeHeader (bitmap.SpecificData);
|
||||
var name = bitmap.Info.Name;
|
||||
if (string.IsNullOrEmpty (name))
|
||||
name = bitd.Id.ToString ("D6");
|
||||
var chunk = dir_file.MMap[bitd.Id];
|
||||
entry.Name = name + ".BITD";
|
||||
entry.Type = "image";
|
||||
entry.Offset = chunk.Offset;
|
||||
entry.Size = chunk.Size;
|
||||
if (entry.Palette > 0)
|
||||
{
|
||||
var cast_id = cast.Index[entry.Palette-1];
|
||||
var clut = dir_file.KeyTable.FindByCast (cast_id, "CLUT");
|
||||
if (clut != null)
|
||||
entry.PaletteRef = dir_file.MMap[clut.Id];
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
|
||||
{
|
||||
var bent = entry as BitmapEntry;
|
||||
if (null == bent)
|
||||
return base.OpenImage (arc, entry);
|
||||
BitmapPalette palette = null;
|
||||
if (bent.PaletteRef != null)
|
||||
{
|
||||
var pal_bytes = arc.File.View.ReadBytes (bent.PaletteRef.Offset, bent.PaletteRef.Size);
|
||||
palette = ReadPalette (pal_bytes);
|
||||
}
|
||||
else if (bent.BitDepth <= 8)
|
||||
{
|
||||
switch (bent.Palette)
|
||||
{
|
||||
case 0: palette = Palettes.SystemMac; break;
|
||||
case -1: palette = Palettes.Rainbow; break;
|
||||
case -2: palette = Palettes.Grayscale; break;
|
||||
case -100: palette = Palettes.WindowsDirector4; break;
|
||||
default:
|
||||
case -101: palette = Palettes.SystemWindows; break;
|
||||
}
|
||||
}
|
||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
var info = new ImageMetaData {
|
||||
Width = (uint)(bent.Right - bent.Left),
|
||||
Height = (uint)(bent.Bottom - bent.Top),
|
||||
BPP = bent.BitDepth
|
||||
};
|
||||
return new BitdDecoder (input.AsStream, info, palette);
|
||||
}
|
||||
|
||||
BitmapPalette ReadPalette (byte[] data)
|
||||
{
|
||||
int num_colors = data.Length / 6;
|
||||
var colors = new Color[num_colors];
|
||||
for (int i = 0; i < data.Length; i += 6)
|
||||
{
|
||||
colors[i/6] = Color.FromRgb (data[i], data[i+2], data[i+4]);
|
||||
}
|
||||
return new BitmapPalette (colors);
|
||||
}
|
||||
}
|
||||
|
||||
internal class BitmapEntry : Entry
|
||||
{
|
||||
public byte Flags;
|
||||
public byte DepthType;
|
||||
public int Top;
|
||||
public int Left;
|
||||
public int Bottom;
|
||||
public int Right;
|
||||
public int BitDepth;
|
||||
public int Palette;
|
||||
public Entry PaletteRef;
|
||||
|
||||
public void DeserializeHeader (byte[] data)
|
||||
{
|
||||
using (var input = new MemoryStream (data, false))
|
||||
{
|
||||
var reader = new Reader (input, ByteOrder.BigEndian);
|
||||
DepthType = reader.ReadU8();
|
||||
Flags = reader.ReadU8();
|
||||
Top = reader.ReadI16();
|
||||
Left = reader.ReadI16();
|
||||
Bottom = reader.ReadI16();
|
||||
Right = reader.ReadI16();
|
||||
reader.Skip (0x0C);
|
||||
BitDepth = reader.ReadU16() & 0xFF; // ???
|
||||
reader.Skip (2);
|
||||
Palette = reader.ReadI16();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class SoundEntry : Entry
|
||||
{
|
||||
public Entry Header;
|
||||
|
||||
public WaveFormat DeserializeHeader (byte[] header)
|
||||
{
|
||||
// pure guesswork
|
||||
return new WaveFormat {
|
||||
FormatTag = 1,
|
||||
Channels = (ushort)BigEndian.ToUInt32 (header, 0x4C),
|
||||
SamplesPerSecond = BigEndian.ToUInt32 (header, 0x2C),
|
||||
AverageBytesPerSecond = BigEndian.ToUInt32 (header, 0x30),
|
||||
BlockAlign = (ushort)BigEndian.ToUInt32 (header, 0x50),
|
||||
BitsPerSample = (ushort)BigEndian.ToUInt32 (header, 0x44),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
712
ArcFormats/Macromedia/DirectorFile.cs
Normal file
712
ArcFormats/Macromedia/DirectorFile.cs
Normal file
@ -0,0 +1,712 @@
|
||||
//! \file DirectorFile.cs
|
||||
//! \date 2023 Aug 21
|
||||
//! \brief Macromedia Director container deserialization.
|
||||
//
|
||||
// Copyright (C) 2023 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using GameRes.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace GameRes.Formats.Macromedia
|
||||
{
|
||||
internal enum ByteOrder
|
||||
{
|
||||
LittleEndian, BigEndian
|
||||
}
|
||||
|
||||
internal enum DataType
|
||||
{
|
||||
Null = 0,
|
||||
Bitmap = 1,
|
||||
FilmLoop = 2,
|
||||
Text = 3,
|
||||
Palette = 4,
|
||||
Picture = 5,
|
||||
Sound = 6,
|
||||
Button = 7,
|
||||
Shape = 8,
|
||||
Movie = 9,
|
||||
DigitalVideo = 10,
|
||||
Script = 11,
|
||||
RTE = 12,
|
||||
}
|
||||
|
||||
internal class SerializationContext
|
||||
{
|
||||
public int Version;
|
||||
public Encoding Encoding;
|
||||
|
||||
public SerializationContext ()
|
||||
{
|
||||
Encoding = Encodings.cp932;
|
||||
}
|
||||
}
|
||||
|
||||
internal class DirectorFile
|
||||
{
|
||||
MemoryMap m_mmap = new MemoryMap();
|
||||
KeyTable m_keyTable = new KeyTable();
|
||||
DirectorConfig m_config = new DirectorConfig();
|
||||
List<Cast> m_casts = new List<Cast>();
|
||||
|
||||
public MemoryMap MMap => m_mmap;
|
||||
public KeyTable KeyTable => m_keyTable;
|
||||
public DirectorConfig Config => m_config;
|
||||
public List<Cast> Casts => m_casts;
|
||||
|
||||
public bool Deserialize (SerializationContext context, Reader reader)
|
||||
{
|
||||
reader.Position = 8;
|
||||
string codec = reader.ReadFourCC();
|
||||
if (codec != "MV93" && codec != "MC95")
|
||||
return false;
|
||||
return ReadMMap (context, reader)
|
||||
&& ReadKeyTable (context, reader)
|
||||
&& ReadConfig (context, reader)
|
||||
&& ReadCasts (context, reader);
|
||||
}
|
||||
|
||||
bool ReadMMap (SerializationContext context, Reader reader)
|
||||
{
|
||||
if (reader.ReadFourCC() != "imap")
|
||||
return false;
|
||||
reader.Skip (8);
|
||||
uint mmap_pos = reader.ReadU32();
|
||||
reader.Position = mmap_pos;
|
||||
if (reader.ReadFourCC() != "mmap")
|
||||
return false;
|
||||
reader.Position = mmap_pos + 8;
|
||||
MMap.Deserialize (context, reader);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadKeyTable (SerializationContext context, Reader reader)
|
||||
{
|
||||
var key_chunk = MMap.Find ("KEY*");
|
||||
if (null == key_chunk)
|
||||
return false;
|
||||
reader.Position = key_chunk.Offset;
|
||||
KeyTable.Deserialize (context, reader);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadConfig (SerializationContext context, Reader reader)
|
||||
{
|
||||
var config_chunk = MMap.Find ("VWCF") ?? MMap.Find ("DRCF");
|
||||
if (null == config_chunk)
|
||||
return false;
|
||||
reader.Position = config_chunk.Offset;
|
||||
Config.Deserialize (context, reader);
|
||||
context.Version = Config.Version;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadCasts (SerializationContext context, Reader reader)
|
||||
{
|
||||
if (context.Version > 1200)
|
||||
{
|
||||
var mcsl = MMap.Find ("MCsL");
|
||||
if (mcsl != null)
|
||||
{
|
||||
reader.Position = mcsl.Offset;
|
||||
var cast_list = new CastList();
|
||||
cast_list.Deserialize (context, reader);
|
||||
foreach (var entry in cast_list.Entries)
|
||||
{
|
||||
var key_entry = KeyTable.FindByCast (entry.Id, "CAS*");
|
||||
if (key_entry != null)
|
||||
{
|
||||
var mmap_entry = MMap[key_entry.Id];
|
||||
var cast = new Cast (context, reader, mmap_entry);
|
||||
if (!PopulateCast (cast, context, reader, entry))
|
||||
return false;
|
||||
Casts.Add (cast);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
var cas_chunk = MMap.Find ("CAS*");
|
||||
if (null == cas_chunk)
|
||||
return false;
|
||||
var new_entry = new CastListEntry { Name = "internal", Id = 0x400, MinMember = Config.MinMember };
|
||||
var new_cast = new Cast (context, reader, cas_chunk);
|
||||
if (!PopulateCast (new_cast, context, reader, new_entry))
|
||||
return false;
|
||||
Casts.Add (new_cast);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool PopulateCast (Cast cast, SerializationContext context, Reader reader, CastListEntry entry)
|
||||
{
|
||||
cast.Name = entry.Name;
|
||||
/*
|
||||
var lctx_ref = KeyTable.Table.Find (e => e.CastId == entry.Id && (e.FourCC == "Lctx" || e.FourCC == "LctX"));
|
||||
MemoryMapEntry lctx_chunk = null;
|
||||
if (lctx_ref != null)
|
||||
lctx_chunk = MMap[lctx_ref.Id];
|
||||
else
|
||||
lctx_chunk = MMap.Dir.Find (e => e.FourCC == "Lctx" || e.FourCC == "LctX");
|
||||
if (null == lctx_chunk)
|
||||
return false;
|
||||
reader.Position = lctx_chunk.Offset;
|
||||
var lctx = new ScriptContext();
|
||||
lctx.Deserialize (context, reader);
|
||||
cast.Context = lctx;
|
||||
*/
|
||||
for (int i = 0; i < cast.Index.Length; ++i)
|
||||
{
|
||||
int chunk_id = cast.Index[i];
|
||||
if (chunk_id > 0)
|
||||
{
|
||||
var chunk = MMap[chunk_id];
|
||||
var member = new CastMember();
|
||||
member.Id = chunk_id;
|
||||
reader.Position = chunk.Offset;
|
||||
member.Deserialize (context, reader);
|
||||
cast.Members[member.Id] = member;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal class CastMember
|
||||
{
|
||||
public DataType Type;
|
||||
public CastInfo Info = new CastInfo();
|
||||
public byte[] SpecificData;
|
||||
public byte Flags;
|
||||
public int Id;
|
||||
|
||||
public void Deserialize (SerializationContext context, Reader reader)
|
||||
{
|
||||
reader = reader.CloneUnless (ByteOrder.BigEndian);
|
||||
if (context.Version > 1200)
|
||||
{
|
||||
Type = (DataType)reader.ReadI32();
|
||||
int info_length = reader.ReadI32();
|
||||
int data_length = reader.ReadI32();
|
||||
if (info_length > 0)
|
||||
{
|
||||
Info.Deserialize (context, reader);
|
||||
}
|
||||
SpecificData = reader.ReadBytes (data_length);
|
||||
}
|
||||
else
|
||||
{
|
||||
int data_length = reader.ReadU16();
|
||||
int info_length = reader.ReadI32();
|
||||
Type = (DataType)reader.ReadU8();
|
||||
--data_length;
|
||||
if (data_length > 0)
|
||||
{
|
||||
Flags = reader.ReadU8();
|
||||
--data_length;
|
||||
}
|
||||
SpecificData = reader.ReadBytes (data_length);
|
||||
if (info_length > 0)
|
||||
{
|
||||
Info.Deserialize (context, reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class CastInfo
|
||||
{
|
||||
public uint DataOffset;
|
||||
public uint ScriptKey;
|
||||
public uint Flags;
|
||||
public int ScriptId;
|
||||
public string Name;
|
||||
public string SourceText;
|
||||
public List<byte[]> Items = new List<byte[]>();
|
||||
|
||||
public void Deserialize (SerializationContext context, Reader reader)
|
||||
{
|
||||
long base_offset = reader.Position;
|
||||
DataOffset = reader.ReadU32();
|
||||
ScriptKey = reader.ReadU32();
|
||||
reader.Skip (4);
|
||||
Flags = reader.ReadU32();
|
||||
ScriptId = reader.ReadI32();
|
||||
reader.Position = base_offset + DataOffset;
|
||||
int table_len = reader.ReadU16();
|
||||
var offsets = new int[table_len];
|
||||
for (int i = 0; i < table_len; ++i)
|
||||
offsets[i] = reader.ReadI32();
|
||||
|
||||
int data_length = reader.ReadI32();
|
||||
long list_offset = reader.Position;
|
||||
Items.Clear();
|
||||
Items.Capacity = offsets.Length;
|
||||
for (int i = 0; i < offsets.Length; ++i)
|
||||
{
|
||||
int offset = offsets[i];
|
||||
int next_offset = (i + 1 < offsets.Length) ? offsets[i+1] : data_length;
|
||||
reader.Position = list_offset + offset;
|
||||
Items.Add (reader.ReadBytes (next_offset - offset));
|
||||
}
|
||||
|
||||
SourceText = Items.Count > 0 ? Binary.GetCString (Items[0], 0) : string.Empty;
|
||||
Name = GetString (1, context.Encoding);
|
||||
}
|
||||
|
||||
string GetString (int item_idx, Encoding enc)
|
||||
{
|
||||
if (item_idx >= Items.Count)
|
||||
return string.Empty;
|
||||
var src = Items[item_idx];
|
||||
if (src.Length <= 1 || 0 == src[0])
|
||||
return string.Empty;
|
||||
int len = src[0];
|
||||
return enc.GetString (src, 1, len);
|
||||
}
|
||||
}
|
||||
|
||||
internal class Cast
|
||||
{
|
||||
public int[] Index;
|
||||
public string Name;
|
||||
public Dictionary<int, CastMember> Members = new Dictionary<int, CastMember>();
|
||||
|
||||
public Cast (SerializationContext context, Reader reader, MemoryMapEntry entry)
|
||||
{
|
||||
int count = (int)(entry.Size / 4);
|
||||
Index = new int[count];
|
||||
reader.Position = entry.Offset;
|
||||
Deserialize (context, reader);
|
||||
}
|
||||
|
||||
public void Deserialize (SerializationContext context, Reader reader)
|
||||
{
|
||||
reader = reader.CloneUnless (ByteOrder.BigEndian);
|
||||
for (int i = 0; i < Index.Length; ++i)
|
||||
Index[i] = reader.ReadI32();
|
||||
}
|
||||
}
|
||||
|
||||
internal class CastList
|
||||
{
|
||||
public uint DataOffset;
|
||||
public int OffsetCount;
|
||||
public int[] OffsetTable;
|
||||
public int ItemsLength;
|
||||
public int CastCount;
|
||||
public int ItemsPerCast;
|
||||
public List<byte[]> Items = new List<byte[]>();
|
||||
|
||||
public readonly List<CastListEntry> Entries = new List<CastListEntry>();
|
||||
|
||||
public void Deserialize (SerializationContext context, Reader reader)
|
||||
{
|
||||
long base_offset = reader.Position;
|
||||
reader = reader.CloneUnless (ByteOrder.BigEndian);
|
||||
DataOffset = reader.ReadU32();
|
||||
reader.Skip (2);
|
||||
CastCount = reader.ReadU16();
|
||||
ItemsPerCast = reader.ReadU16();
|
||||
reader.Skip (2);
|
||||
reader.Position = base_offset + DataOffset;
|
||||
OffsetCount = reader.ReadU16();
|
||||
OffsetTable = new int[OffsetCount];
|
||||
for (int i = 0; i < OffsetCount; ++i)
|
||||
{
|
||||
OffsetTable[i] = reader.ReadI32();
|
||||
}
|
||||
ItemsLength = reader.ReadI32();
|
||||
long items_offset = reader.Position;
|
||||
Items.Clear();
|
||||
Items.Capacity = OffsetCount;
|
||||
for (int i = 0; i < OffsetCount; ++i)
|
||||
{
|
||||
int offset = OffsetTable[i];
|
||||
int next_offset = (i + 1 < OffsetCount) ? OffsetTable[i + 1] : ItemsLength;
|
||||
int item_size = next_offset - offset;
|
||||
Items.Add (reader.ReadBytes (item_size));
|
||||
}
|
||||
|
||||
Entries.Clear();
|
||||
Entries.Capacity = CastCount;
|
||||
int item_idx = 0;
|
||||
for (int i = 0; i < CastCount; ++i)
|
||||
{
|
||||
var entry = new CastListEntry();
|
||||
if (ItemsPerCast >= 1)
|
||||
entry.Name = GetString (item_idx + 1, context.Encoding);
|
||||
if (ItemsPerCast >= 2)
|
||||
entry.Path = GetString (item_idx + 2, context.Encoding);
|
||||
if (ItemsPerCast >= 3 && Items[item_idx + 3].Length >= 2)
|
||||
entry.Flags = BigEndian.ToUInt16 (Items[item_idx + 3], 0);
|
||||
if (ItemsPerCast >= 4 && Items[item_idx + 4].Length >= 8)
|
||||
{
|
||||
entry.MinMember = BigEndian.ToUInt16 (Items[item_idx + 4], 0);
|
||||
entry.MaxMember = BigEndian.ToUInt16 (Items[item_idx + 4], 2);
|
||||
entry.Id = BigEndian.ToInt32 (Items[item_idx + 4], 4);
|
||||
}
|
||||
Entries.Add (entry);
|
||||
}
|
||||
}
|
||||
|
||||
string GetString (int item_idx, Encoding enc)
|
||||
{
|
||||
var src = Items[item_idx];
|
||||
if (src.Length <= 1 || 0 == src[0])
|
||||
return string.Empty;
|
||||
int len = src[0];
|
||||
return enc.GetString (src, 1, len);
|
||||
}
|
||||
}
|
||||
|
||||
internal class CastListEntry
|
||||
{
|
||||
public string Name;
|
||||
public string Path;
|
||||
public ushort Flags;
|
||||
public int MinMember;
|
||||
public int MaxMember;
|
||||
public int Id;
|
||||
}
|
||||
|
||||
internal class ScriptContext
|
||||
{
|
||||
public int EntriesOffset;
|
||||
public int LnamChunkId;
|
||||
public int ValidCount;
|
||||
public ushort Flags;
|
||||
public short FreePtr;
|
||||
public List<ScriptContextMap> ChunkMap = new List<ScriptContextMap>();
|
||||
|
||||
public void Deserialize (SerializationContext context, Reader reader)
|
||||
{
|
||||
long base_offset = reader.Position;
|
||||
reader = reader.CloneUnless (ByteOrder.BigEndian);
|
||||
reader.Skip (8);
|
||||
int count = reader.ReadI32();
|
||||
reader.Skip (4);
|
||||
EntriesOffset = reader.ReadU16();
|
||||
reader.Skip (14);
|
||||
LnamChunkId = reader.ReadI32();
|
||||
ValidCount = reader.ReadU16();
|
||||
Flags = reader.ReadU16();
|
||||
FreePtr = reader.ReadI16();
|
||||
reader.Position = base_offset + EntriesOffset;
|
||||
|
||||
ChunkMap.Clear();
|
||||
ChunkMap.Capacity = count;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var entry = new ScriptContextMap();
|
||||
entry.Deserialize (context, reader);
|
||||
ChunkMap.Add (entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class ScriptContextMap
|
||||
{
|
||||
public int Key;
|
||||
public int ChunkId;
|
||||
|
||||
public void Deserialize (SerializationContext context, Reader reader)
|
||||
{
|
||||
Key = reader.ReadI32();
|
||||
ChunkId = reader.ReadI32();
|
||||
reader.Skip (4);
|
||||
}
|
||||
}
|
||||
|
||||
internal class DirectorConfig
|
||||
{
|
||||
public short Length;
|
||||
public short FileVersion;
|
||||
public short StageTop;
|
||||
public short StageLeft;
|
||||
public short StageBottom;
|
||||
public short StageRight;
|
||||
public short MinMember;
|
||||
public short MaxMember;
|
||||
public ushort StageColor;
|
||||
public ushort BitDepth;
|
||||
public int Version;
|
||||
public int FrameRate;
|
||||
public int Platform;
|
||||
public int Protection;
|
||||
public uint CheckSum;
|
||||
public int DefaultPalette;
|
||||
|
||||
public void Deserialize (SerializationContext context, Reader reader)
|
||||
{
|
||||
long base_offset = reader.Position;
|
||||
reader = reader.CloneUnless (ByteOrder.BigEndian);
|
||||
|
||||
reader.Position = base_offset + 0x24;
|
||||
Version = reader.ReadU16();
|
||||
reader.Position = base_offset;
|
||||
Length = reader.ReadI16();
|
||||
FileVersion = reader.ReadI16();
|
||||
StageTop = reader.ReadI16();
|
||||
StageLeft = reader.ReadI16();
|
||||
StageBottom = reader.ReadI16();
|
||||
StageRight = reader.ReadI16();
|
||||
MinMember = reader.ReadI16();
|
||||
MaxMember = reader.ReadI16();
|
||||
reader.Skip (0x0A);
|
||||
StageColor = reader.ReadU16();
|
||||
BitDepth = reader.ReadU16();
|
||||
reader.Skip (0x18);
|
||||
FrameRate = reader.ReadU16();
|
||||
Platform = reader.ReadI16();
|
||||
Protection = reader.ReadI16();
|
||||
reader.Skip (4);
|
||||
CheckSum = reader.ReadU32();
|
||||
if (Version > 1200)
|
||||
{
|
||||
reader.Position = base_offset + 0x4E;
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.Position = base_offset + 0x46;
|
||||
}
|
||||
DefaultPalette = reader.ReadU16();
|
||||
}
|
||||
}
|
||||
|
||||
internal class KeyTable
|
||||
{
|
||||
public int EntrySize;
|
||||
public int TotalCount;
|
||||
public int UsedCount;
|
||||
public readonly List<KeyTableEntry> Table = new List<KeyTableEntry>();
|
||||
|
||||
public KeyTableEntry this[int index] => Table[index];
|
||||
|
||||
public KeyTableEntry FindByCast (int cast_id, string four_cc)
|
||||
{
|
||||
return Table.Find (e => e.CastId == cast_id && e.FourCC == four_cc);
|
||||
}
|
||||
|
||||
public void Deserialize (SerializationContext context, Reader reader)
|
||||
{
|
||||
EntrySize = reader.ReadU16();
|
||||
reader.Skip(2);
|
||||
TotalCount = reader.ReadI32();
|
||||
UsedCount = reader.ReadI32();
|
||||
|
||||
Table.Clear();
|
||||
Table.Capacity = TotalCount;
|
||||
for (int i = 0; i < TotalCount; ++i)
|
||||
{
|
||||
var entry = new KeyTableEntry();
|
||||
entry.Deserialize (context, reader);
|
||||
Table.Add (entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class KeyTableEntry
|
||||
{
|
||||
public int Id;
|
||||
public int CastId;
|
||||
public string FourCC;
|
||||
|
||||
public void Deserialize (SerializationContext context, Reader input)
|
||||
{
|
||||
Id = input.ReadI32();
|
||||
CastId = input.ReadI32();
|
||||
FourCC = input.ReadFourCC();
|
||||
}
|
||||
}
|
||||
|
||||
internal class MemoryMap
|
||||
{
|
||||
public ushort HeaderLength;
|
||||
public ushort EntryLength;
|
||||
public int ChunkCountMax;
|
||||
public int ChunkCountUsed;
|
||||
public int FreeHead;
|
||||
public readonly List<MemoryMapEntry> Dir = new List<MemoryMapEntry>();
|
||||
|
||||
public MemoryMapEntry this[int index] => Dir[index];
|
||||
|
||||
public MemoryMapEntry Find (string four_cc) => Dir.Find (e => e.FourCC == four_cc);
|
||||
|
||||
public void Deserialize (SerializationContext context, Reader reader)
|
||||
{
|
||||
long header_pos = reader.Position;
|
||||
HeaderLength = reader.ReadU16();
|
||||
if (HeaderLength < 0x18)
|
||||
throw new InvalidFormatException ("Invalid <mmap> header length.");
|
||||
EntryLength = reader.ReadU16();
|
||||
if (EntryLength < 0x14)
|
||||
throw new InvalidFormatException ("Invalid <mmap> entry length.");
|
||||
ChunkCountMax = reader.ReadI32();
|
||||
ChunkCountUsed = reader.ReadI32();
|
||||
reader.Skip (8);
|
||||
FreeHead = reader.ReadI32();
|
||||
|
||||
Dir.Clear();
|
||||
Dir.Capacity = ChunkCountUsed;
|
||||
long entry_pos = header_pos + HeaderLength;
|
||||
for (int i = 0; i < ChunkCountUsed; ++i)
|
||||
{
|
||||
reader.Position = entry_pos;
|
||||
var entry = new MemoryMapEntry (i);
|
||||
entry.Deserialize (context, reader);
|
||||
Dir.Add (entry);
|
||||
entry_pos += EntryLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class MemoryMapEntry : Entry
|
||||
{
|
||||
public int Id;
|
||||
public string FourCC;
|
||||
public ushort Flags;
|
||||
|
||||
public MemoryMapEntry (int id = 0)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public void Deserialize (SerializationContext context, Reader reader)
|
||||
{
|
||||
FourCC = reader.ReadFourCC();
|
||||
Size = reader.ReadU32();
|
||||
Offset = reader.ReadU32() + 8;
|
||||
Flags = reader.ReadU16();
|
||||
int Next = reader.ReadI32();
|
||||
}
|
||||
}
|
||||
|
||||
internal class Reader
|
||||
{
|
||||
Stream m_input;
|
||||
byte[] m_buffer = new byte[4];
|
||||
|
||||
public Reader (Stream input, ByteOrder e = ByteOrder.LittleEndian) : this (input, Encodings.cp932, e)
|
||||
{
|
||||
}
|
||||
|
||||
public Reader (Stream input, Encoding enc, ByteOrder e = ByteOrder.LittleEndian)
|
||||
{
|
||||
m_input = input;
|
||||
Encoding = enc;
|
||||
SetByteOrder (e);
|
||||
}
|
||||
|
||||
public Stream Source { get => m_input; }
|
||||
|
||||
public ByteOrder ByteOrder { get; private set; }
|
||||
|
||||
public Encoding Encoding { get; set; }
|
||||
|
||||
public long Position
|
||||
{
|
||||
get => m_input.Position;
|
||||
set => m_input.Position = value;
|
||||
}
|
||||
|
||||
private Func<ushort> ToU16;
|
||||
private Func<uint> ToU32;
|
||||
|
||||
public void SetByteOrder (ByteOrder e)
|
||||
{
|
||||
this.ByteOrder = e;
|
||||
if (ByteOrder.LittleEndian == e)
|
||||
{
|
||||
ToU16 = () => LittleEndian.ToUInt16 (m_buffer, 0);
|
||||
ToU32 = () => LittleEndian.ToUInt32 (m_buffer, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
ToU16 = () => BigEndian.ToUInt16 (m_buffer, 0);
|
||||
ToU32 = () => BigEndian.ToUInt32 (m_buffer, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static Dictionary<uint, string> KnownFourCC = new Dictionary<uint, string>();
|
||||
|
||||
public string ReadFourCC ()
|
||||
{
|
||||
uint signature = ReadU32();
|
||||
string four_cc;
|
||||
if (KnownFourCC.TryGetValue (signature, out four_cc))
|
||||
return four_cc;
|
||||
BigEndian.Pack (signature, m_buffer, 0);
|
||||
return KnownFourCC[signature] = Encoding.GetString (m_buffer, 0, 4);
|
||||
}
|
||||
|
||||
public void Skip (int amount) => m_input.Seek (amount, SeekOrigin.Current);
|
||||
|
||||
public byte ReadU8 ()
|
||||
{
|
||||
int b = m_input.ReadByte();
|
||||
if (-1 == b)
|
||||
throw new EndOfStreamException();
|
||||
return (byte)b;
|
||||
}
|
||||
|
||||
public sbyte ReadI8 () => (sbyte)ReadU8();
|
||||
|
||||
public ushort ReadU16 ()
|
||||
{
|
||||
if (m_input.Read (m_buffer, 0, 2) < 2)
|
||||
throw new EndOfStreamException();
|
||||
return ToU16();
|
||||
}
|
||||
|
||||
public short ReadI16 () => (short)ReadU16();
|
||||
|
||||
public uint ReadU32 ()
|
||||
{
|
||||
if (m_input.Read (m_buffer, 0, 4) < 4)
|
||||
throw new EndOfStreamException();
|
||||
return ToU32();
|
||||
}
|
||||
|
||||
public int ReadI32 () => (int)ReadU32();
|
||||
|
||||
public byte[] ReadBytes (int length)
|
||||
{
|
||||
if (0 == length)
|
||||
return Array.Empty<byte>();
|
||||
var buffer = new byte[length];
|
||||
if (m_input.Read (buffer, 0, length) < length)
|
||||
throw new EndOfStreamException();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public Reader CloneUnless (ByteOrder order)
|
||||
{
|
||||
if (this.ByteOrder != order)
|
||||
return new Reader (this.Source, this.Encoding, order);
|
||||
else
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
@ -29,9 +29,10 @@ using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Selen
|
||||
namespace GameRes.Formats.Macromedia
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class BitdFormat : ImageFormat
|
||||
@ -202,4 +203,134 @@ namespace GameRes.Formats.Selen
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class BitdDecoder : IImageDecoder
|
||||
{
|
||||
Stream m_input;
|
||||
byte[] m_output;
|
||||
int m_width;
|
||||
int m_height;
|
||||
int m_stride;
|
||||
ImageMetaData m_info;
|
||||
ImageData m_image;
|
||||
BitmapPalette m_palette;
|
||||
|
||||
public Stream Source { get => m_input; }
|
||||
public ImageFormat SourceFormat { get => null; }
|
||||
public ImageMetaData Info { get => m_info; }
|
||||
public ImageData Image { get => m_image ?? (m_image = GetImageData()); }
|
||||
public PixelFormat Format { get; private set; }
|
||||
|
||||
public BitdDecoder (Stream input, ImageMetaData info, BitmapPalette palette)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
m_width = info.iWidth;
|
||||
m_height = info.iHeight;
|
||||
m_stride = (m_width * m_info.BPP + 7) / 8;
|
||||
m_stride = (m_stride + 1) & ~1;
|
||||
m_output = new byte[m_stride * m_height];
|
||||
Format = info.BPP == 4 ? PixelFormats.Indexed4
|
||||
: info.BPP == 8 ? PixelFormats.Indexed8
|
||||
: info.BPP == 16 ? PixelFormats.Bgr555
|
||||
: PixelFormats.Bgr32;
|
||||
m_palette = palette;
|
||||
}
|
||||
|
||||
protected ImageData GetImageData ()
|
||||
{
|
||||
if (Info.BPP <= 8)
|
||||
Unpack8bpp();
|
||||
else
|
||||
UnpackChannels (Info.BPP / 8);
|
||||
return ImageData.Create (m_info, Format, m_palette, m_output, m_stride);
|
||||
}
|
||||
|
||||
void Unpack8bpp ()
|
||||
{
|
||||
for (int line = 0; line < m_output.Length; line += m_stride)
|
||||
{
|
||||
int x = 0;
|
||||
while (x < m_stride)
|
||||
{
|
||||
int b = m_input.ReadByte();
|
||||
if (-1 == b)
|
||||
throw new InvalidFormatException ("Unexpected end of file");
|
||||
int count = b;
|
||||
if (b > 0x7f)
|
||||
count = (byte)-(sbyte)b;
|
||||
++count;
|
||||
if (x + count > m_stride)
|
||||
throw new InvalidFormatException();
|
||||
if (b > 0x7f)
|
||||
{
|
||||
b = m_input.ReadByte();
|
||||
if (-1 == b)
|
||||
throw new InvalidFormatException ("Unexpected end of file");
|
||||
for (int i = 0; i < count; ++i)
|
||||
m_output[line + x++] = (byte)b;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_input.Read (m_output, line + x, count);
|
||||
x += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void UnpackChannels (int channels)
|
||||
{
|
||||
var scan_line = new byte[m_stride];
|
||||
for (int line = 0; line < m_output.Length; line += m_stride)
|
||||
{
|
||||
int x = 0;
|
||||
while (x < m_stride)
|
||||
{
|
||||
int b = m_input.ReadByte();
|
||||
if (-1 == b)
|
||||
throw new InvalidFormatException ("Unexpected end of file");
|
||||
int count = b;
|
||||
if (b > 0x7f)
|
||||
count = (byte)-(sbyte)b;
|
||||
++count;
|
||||
if (x + count > m_stride)
|
||||
throw new InvalidFormatException();
|
||||
if (b > 0x7f)
|
||||
{
|
||||
b = m_input.ReadByte();
|
||||
if (-1 == b)
|
||||
throw new InvalidFormatException ("Unexpected end of file");
|
||||
for (int i = 0; i < count; ++i)
|
||||
scan_line[x++] = (byte)b;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_input.Read (scan_line, x, count);
|
||||
x += count;
|
||||
}
|
||||
}
|
||||
int dst = line;
|
||||
for (int i = 0; i < m_width; ++i)
|
||||
{
|
||||
for (int src = m_width * (channels - 1); src >= 0; src -= m_width)
|
||||
m_output[dst++] = scan_line[i + src];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
bool m_disposed = false;
|
||||
|
||||
public void Dispose ()
|
||||
{
|
||||
if (!m_disposed)
|
||||
{
|
||||
m_input.Dispose();
|
||||
m_disposed = true;
|
||||
}
|
||||
GC.SuppressFinalize (this);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
1327
ArcFormats/Macromedia/Palettes.cs
Normal file
1327
ArcFormats/Macromedia/Palettes.cs
Normal file
File diff suppressed because it is too large
Load Diff
@ -142,7 +142,7 @@ namespace GameRes.Formats.Maika
|
||||
|
||||
Stream input;
|
||||
// XXX scrambling might be applicable for 'E1' signatures only
|
||||
if (scheme.ScrambledSize > 0)
|
||||
if (0x3145 == signature && scheme.ScrambledSize > 0)
|
||||
{
|
||||
var prefix = arc.File.View.ReadBytes (entry.Offset+10, scheme.ScrambledSize);
|
||||
foreach (var pair in scheme.ScrambleMap)
|
||||
|
@ -176,6 +176,7 @@ namespace GameRes.Formats.Marble
|
||||
if (3 == b)
|
||||
{
|
||||
length += 9;
|
||||
// length = Math.Min(m_output.Length - dst, length);
|
||||
int read = m_input.Read (m_output, dst, length);
|
||||
if (read < length)
|
||||
break;
|
||||
|
@ -75,9 +75,7 @@ namespace GameRes.Formats
|
||||
}
|
||||
part_offset = part_end_offset;
|
||||
}
|
||||
if (null == input)
|
||||
return Stream.Null;
|
||||
return input;
|
||||
return input ?? Stream.Null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
103
ArcFormats/MyAdv/ArcPAC.cs
Normal file
103
ArcFormats/MyAdv/ArcPAC.cs
Normal file
@ -0,0 +1,103 @@
|
||||
//! \file ArcPAC.cs
|
||||
//! \date 2022 Jun 01
|
||||
//! \brief MyAdv engine resource archive.
|
||||
//
|
||||
// Copyright (C) 2022 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using GameRes.Compression;
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression;
|
||||
|
||||
namespace GameRes.Formats.MyAdv
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class PacOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "PAC/MyAdv"; } }
|
||||
public override string Description { get { return "MyAdv engine resource archive"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override bool IsHierarchic { get { return true; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public PacOpener ()
|
||||
{
|
||||
ContainedFormats = new[] { "DDS", "OGG", "TXT" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (0);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
|
||||
var name_buffer = new byte[0x100];
|
||||
var zlib_buffer = new byte[0x100];
|
||||
long pos = 4;
|
||||
var zlib = new Inflater();
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
int name_len = file.View.ReadInt32 (pos);
|
||||
if (name_len <= 0 || name_len > name_buffer.Length)
|
||||
return null;
|
||||
int unpacked_size = file.View.ReadInt32 (pos+4);
|
||||
if (unpacked_size < name_len || unpacked_size > name_buffer.Length)
|
||||
return null;
|
||||
int packed_size = file.View.ReadInt32 (pos+8);
|
||||
if (packed_size <= 0 || packed_size > zlib_buffer.Length)
|
||||
return null;
|
||||
pos += 12;
|
||||
file.View.Read (pos, zlib_buffer, 0, (uint)packed_size);
|
||||
pos += packed_size;
|
||||
zlib.Reset();
|
||||
zlib.SetInput (zlib_buffer, 0, packed_size);
|
||||
zlib.Inflate (name_buffer);
|
||||
var name = Encodings.cp932.GetString (name_buffer, 0, name_len);
|
||||
var entry = Create<PackedEntry> (name);
|
||||
dir.Add (entry);
|
||||
}
|
||||
foreach (PackedEntry entry in dir)
|
||||
{
|
||||
entry.Offset = file.View.ReadUInt32 (pos);
|
||||
entry.Size = file.View.ReadUInt32 (pos+4);
|
||||
entry.UnpackedSize = file.View.ReadUInt32 (pos+8);
|
||||
entry.IsPacked = true;
|
||||
pos += 12;
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var pent = entry as PackedEntry;
|
||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
if (null == pent || !pent.IsPacked)
|
||||
return input;
|
||||
return new ZLibStream (input, CompressionMode.Decompress);
|
||||
}
|
||||
}
|
||||
}
|
53
ArcFormats/NScripter/Script.cs
Normal file
53
ArcFormats/NScripter/Script.cs
Normal file
@ -0,0 +1,53 @@
|
||||
//! \file Script.cs
|
||||
//! \date 2023 Aug 23
|
||||
//! \brief
|
||||
//
|
||||
// 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.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.NScripter
|
||||
{
|
||||
[Export(typeof(ScriptFormat))]
|
||||
public class NSOpener : GenericScriptFormat
|
||||
{
|
||||
public override string Tag { get => "NScripter"; }
|
||||
public override string Description { get => "NScripter engine script file"; }
|
||||
public override uint Signature { get => 0; }
|
||||
|
||||
public override bool IsScript (IBinaryStream file)
|
||||
{
|
||||
return VFS.IsPathEqualsToFileName (file.Name, "nscript.dat");
|
||||
}
|
||||
|
||||
public override Stream ConvertFrom (IBinaryStream file)
|
||||
{
|
||||
return new XoredStream (file.AsStream, 0x84);
|
||||
}
|
||||
|
||||
public override Stream ConvertBack (IBinaryStream file)
|
||||
{
|
||||
return new XoredStream (file.AsStream, 0x84);
|
||||
}
|
||||
}
|
||||
}
|
@ -377,256 +377,4 @@ namespace GameRes.Formats.Neko
|
||||
0x58, 0xC5, 0x74, 0xB7, 0x8E, 0x7D, 0x89, 0x8A, 0x56, 0x4D, 0x86, 0x94, 0x9A, 0x4C, 0x92, 0xB0,
|
||||
};
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class Pak2Opener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "NEKOPACK/2"; } }
|
||||
public override string Description { get { return "NekoPack resource archive"; } }
|
||||
public override uint Signature { get { return 0x4F4B454E; } } // "NEKO"
|
||||
public override bool IsHierarchic { get { return true; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public Pak2Opener ()
|
||||
{
|
||||
Extensions = new string[] { "dat" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.View.AsciiEqual (4, "PACK"))
|
||||
return null;
|
||||
|
||||
uint init_key = file.View.ReadUInt32 (0xC);
|
||||
var xdec = new NekoXCode (init_key);
|
||||
uint seed = file.View.ReadUInt32 (0x10);
|
||||
var buffer = file.View.ReadBytes (0x14, 8);
|
||||
xdec.Decrypt (seed, buffer, 0, 8);
|
||||
|
||||
uint index_size = LittleEndian.ToUInt32 (buffer, 0);
|
||||
if (index_size < 0x14 || index_size != LittleEndian.ToUInt32 (buffer, 4))
|
||||
return null;
|
||||
var index = new byte[(index_size + 7u) & ~7u];
|
||||
if (file.View.Read (0x1C, index, 0, index_size) < index_size)
|
||||
return null;
|
||||
xdec.Decrypt (seed, index, 0, index.Length);
|
||||
|
||||
using (var reader = new IndexReader (file, xdec, index, (int)index_size))
|
||||
{
|
||||
var dir = reader.Parse (0x1C+index.Length);
|
||||
if (null == dir)
|
||||
return null;
|
||||
reader.DetectTypes (dir, entry => {
|
||||
uint key = file.View.ReadUInt32 (entry.Offset);
|
||||
file.View.Read (entry.Offset+12, buffer, 0, 8);
|
||||
xdec.Decrypt (key, buffer, 0, 8);
|
||||
return LittleEndian.ToUInt32 (buffer, 0);
|
||||
});
|
||||
return new NekoArchive (file, this, dir, xdec);
|
||||
}
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var narc = arc as NekoArchive;
|
||||
if (null == narc || entry.Size <= 12)
|
||||
return base.OpenEntry (arc, entry);
|
||||
uint key = arc.File.View.ReadUInt32 (entry.Offset);
|
||||
var data = new byte[entry.Size];
|
||||
arc.File.View.Read (entry.Offset+4, data, 0, 8);
|
||||
narc.Decoder.Decrypt (key, data, 0, 8);
|
||||
int size = LittleEndian.ToInt32 (data, 0);
|
||||
if (size != LittleEndian.ToInt32 (data, 4))
|
||||
{
|
||||
Trace.WriteLine ("entry decryption failed", "[NEKOPACK]");
|
||||
return base.OpenEntry (arc, entry);
|
||||
}
|
||||
int aligned_size = (size + 7) & ~7;
|
||||
if (aligned_size > data.Length)
|
||||
data = new byte[aligned_size];
|
||||
arc.File.View.Read (entry.Offset+12, data, 0, (uint)size);
|
||||
narc.Decoder.Decrypt (key, data, 0, aligned_size);
|
||||
return new BinMemoryStream (data, 0, size, entry.Name);
|
||||
}
|
||||
}
|
||||
|
||||
internal class NekoXCode : INekoFormat
|
||||
{
|
||||
uint m_seed;
|
||||
uint[] m_random;
|
||||
SimdProgram m_program;
|
||||
|
||||
public NekoXCode (uint init_key)
|
||||
{
|
||||
m_seed = init_key;
|
||||
m_random = InitTable (init_key);
|
||||
m_program = new SimdProgram (init_key);
|
||||
}
|
||||
|
||||
public void Decrypt (uint key, byte[] input, int offset, int length)
|
||||
{
|
||||
for (int i = 1; i < 7; ++i)
|
||||
{
|
||||
uint src = key % 0x28 * 2;
|
||||
m_program.mm[i] = m_random[src] | (ulong)m_random[src+1] << 32;
|
||||
key /= 0x28;
|
||||
}
|
||||
m_program.Execute (input, offset, length);
|
||||
}
|
||||
|
||||
public uint HashFromName (byte[] str, int offset, int length)
|
||||
{
|
||||
uint hash = m_seed;
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
hash = 0x100002A * (ShiftMap[str[offset+i] & 0xFF] ^ hash);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
public DirRecord ReadDir (IBinaryStream input)
|
||||
{
|
||||
uint hash = input.ReadUInt32();
|
||||
int count = input.ReadInt32();
|
||||
if (count != input.ReadInt32())
|
||||
throw new InvalidFormatException();
|
||||
return new DirRecord { Hash = hash, FileCount = count };
|
||||
}
|
||||
|
||||
public long NextOffset (Entry entry)
|
||||
{
|
||||
return entry.Offset + entry.Size;
|
||||
}
|
||||
|
||||
static readonly byte[] ShiftMap = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xc9, 0xca, 0x00, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0x00, 0xd2, 0xd3, 0x27, 0x25, 0xc8,
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0x00,
|
||||
0xd6, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
|
||||
0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0xd7, 0xc8, 0xd8, 0xd9, 0x26,
|
||||
0xda, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
|
||||
0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0xdb, 0x00, 0xdc, 0xdd, 0x00,
|
||||
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
|
||||
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
||||
0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
||||
0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
||||
0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
|
||||
0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
|
||||
0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8,
|
||||
};
|
||||
|
||||
class SimdProgram
|
||||
{
|
||||
public ulong[] mm = new ulong[7];
|
||||
|
||||
Action[] m_transform = new Action[4];
|
||||
Action[] m_shuffle = new Action[6];
|
||||
|
||||
Action<int>[] TransformList;
|
||||
Action[] ShuffleList;
|
||||
|
||||
public SimdProgram (uint key)
|
||||
{
|
||||
TransformList = new Action<int>[] {
|
||||
pxor, paddb, paddw, paddd, psubb, psubw, psubd,
|
||||
pxor, psubb, psubw, psubd, paddb, paddw, paddd,
|
||||
};
|
||||
ShuffleList = new Action[] {
|
||||
paddq_1_2, paddq_2_3, paddq_3_4, paddq_4_5, paddq_5_6, paddq_6_1,
|
||||
};
|
||||
|
||||
GenerateProgram (key);
|
||||
}
|
||||
|
||||
void pxor (int i) { mm[0] ^= mm[i]; }
|
||||
void paddb (int i) { mm[0] = MMX.PAddB (mm[0], mm[i]); }
|
||||
void paddw (int i) { mm[0] = MMX.PAddW (mm[0], mm[i]); }
|
||||
void paddd (int i) { mm[0] = MMX.PAddD (mm[0], mm[i]); }
|
||||
void psubb (int i) { mm[0] = MMX.PSubB (mm[0], mm[i]); }
|
||||
void psubw (int i) { mm[0] = MMX.PSubW (mm[0], mm[i]); }
|
||||
void psubd (int i) { mm[0] = MMX.PSubD (mm[0], mm[i]); }
|
||||
|
||||
void paddq_1_2 () { mm[1] += mm[2]; }
|
||||
void paddq_2_3 () { mm[2] += mm[3]; }
|
||||
void paddq_3_4 () { mm[3] += mm[4]; }
|
||||
void paddq_4_5 () { mm[4] += mm[5]; }
|
||||
void paddq_5_6 () { mm[5] += mm[6]; }
|
||||
void paddq_6_1 () { mm[6] += mm[1]; }
|
||||
|
||||
void GenerateProgram (uint key)
|
||||
{
|
||||
int t1 = 7 + (int)(key >> 28);
|
||||
int cmd_base = (int)key & 0xffff;
|
||||
int arg_base = (int)(key >> 16) & 0xfff;
|
||||
for (int i = 3; i >= 0; --i)
|
||||
{
|
||||
int cmd = ((cmd_base >> (4 * i)) + t1) % TransformList.Length;
|
||||
int arg = (arg_base >> (3 * i)) % 6 + 1;
|
||||
m_transform[3-i] = () => TransformList[cmd] (arg);
|
||||
}
|
||||
for (uint i = 0; i < 6; ++i)
|
||||
{
|
||||
m_shuffle[i] = ShuffleList[(i + key) % (uint)ShuffleList.Length];
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void Execute (byte[] input, int offset, int length)
|
||||
{
|
||||
if (offset < 0 || offset > input.Length)
|
||||
throw new ArgumentException ("offset");
|
||||
int count = Math.Min (length, input.Length-offset) / 8;
|
||||
if (0 == count)
|
||||
return;
|
||||
fixed (byte* data = &input[offset])
|
||||
{
|
||||
ulong* data64 = (ulong*)data;
|
||||
for (;;)
|
||||
{
|
||||
mm[0] = *data64;
|
||||
foreach (var cmd in m_transform)
|
||||
cmd();
|
||||
*data64++ = mm[0];
|
||||
if (1 == count--)
|
||||
break;
|
||||
foreach (var cmd in m_shuffle)
|
||||
cmd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint[] InitTable (uint key)
|
||||
{
|
||||
uint a = 0;
|
||||
uint b = 0;
|
||||
do
|
||||
{
|
||||
a <<= 1;
|
||||
b ^= 1;
|
||||
a = ((a | b) << (int)(key & 1)) | b;
|
||||
key >>= 1;
|
||||
}
|
||||
while (0 == (a & 0x80000000));
|
||||
key = a << 1;
|
||||
a = key + Binary.BigEndian (key);
|
||||
byte count = (byte)key;
|
||||
do
|
||||
{
|
||||
b = key ^ a;
|
||||
a = (b << 4) ^ (b >> 4) ^ (b << 3) ^ (b >> 3) ^ b;
|
||||
}
|
||||
while (--count != 0);
|
||||
|
||||
var table = new uint[154];
|
||||
for (int i = 0; i < table.Length; ++i)
|
||||
{
|
||||
b = key ^ a;
|
||||
a = (b << 4) ^ (b >> 4) ^ (b << 3) ^ (b >> 3) ^ b;
|
||||
table[i] = a;
|
||||
}
|
||||
return table;
|
||||
}
|
||||
}
|
||||
}
|
285
ArcFormats/Nekopack/ArcNEKO2.cs
Normal file
285
ArcFormats/Nekopack/ArcNEKO2.cs
Normal file
@ -0,0 +1,285 @@
|
||||
//! \file ArcNEKO2.cs
|
||||
//! \date 2022 Jun 17
|
||||
//! \brief Nekopack archive format implementation.
|
||||
//
|
||||
// Copyright (C) 2015-2016 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using GameRes.Utility;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.Neko
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class Pak2Opener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "NEKOPACK/2"; } }
|
||||
public override string Description { get { return "NekoPack resource archive"; } }
|
||||
public override uint Signature { get { return 0x4F4B454E; } } // "NEKO"
|
||||
public override bool IsHierarchic { get { return true; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public Pak2Opener ()
|
||||
{
|
||||
Extensions = new string[] { "dat" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.View.AsciiEqual (4, "PACK"))
|
||||
return null;
|
||||
|
||||
uint init_key = file.View.ReadUInt32 (0xC);
|
||||
var xdec = new NekoXCode (init_key);
|
||||
uint seed = file.View.ReadUInt32 (0x10);
|
||||
var buffer = file.View.ReadBytes (0x14, 8);
|
||||
xdec.Decrypt (seed, buffer, 0, 8);
|
||||
|
||||
uint index_size = LittleEndian.ToUInt32 (buffer, 0);
|
||||
if (index_size < 0x14 || index_size != LittleEndian.ToUInt32 (buffer, 4))
|
||||
return null;
|
||||
var index = new byte[(index_size + 7u) & ~7u];
|
||||
if (file.View.Read (0x1C, index, 0, index_size) < index_size)
|
||||
return null;
|
||||
xdec.Decrypt (seed, index, 0, index.Length);
|
||||
|
||||
using (var reader = new IndexReader (file, xdec, index, (int)index_size))
|
||||
{
|
||||
var dir = reader.Parse (0x1C+index.Length);
|
||||
if (null == dir)
|
||||
return null;
|
||||
reader.DetectTypes (dir, entry => {
|
||||
uint key = file.View.ReadUInt32 (entry.Offset);
|
||||
file.View.Read (entry.Offset+12, buffer, 0, 8);
|
||||
xdec.Decrypt (key, buffer, 0, 8);
|
||||
return LittleEndian.ToUInt32 (buffer, 0);
|
||||
});
|
||||
return new NekoArchive (file, this, dir, xdec);
|
||||
}
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var narc = arc as NekoArchive;
|
||||
if (null == narc || entry.Size <= 12)
|
||||
return base.OpenEntry (arc, entry);
|
||||
uint key = arc.File.View.ReadUInt32 (entry.Offset);
|
||||
var data = new byte[entry.Size];
|
||||
arc.File.View.Read (entry.Offset+4, data, 0, 8);
|
||||
narc.Decoder.Decrypt (key, data, 0, 8);
|
||||
int size = LittleEndian.ToInt32 (data, 0);
|
||||
if (size != LittleEndian.ToInt32 (data, 4))
|
||||
{
|
||||
Trace.WriteLine ("entry decryption failed", "[NEKOPACK]");
|
||||
return base.OpenEntry (arc, entry);
|
||||
}
|
||||
int aligned_size = (size + 7) & ~7;
|
||||
if (aligned_size > data.Length)
|
||||
data = new byte[aligned_size];
|
||||
arc.File.View.Read (entry.Offset+12, data, 0, (uint)size);
|
||||
narc.Decoder.Decrypt (key, data, 0, aligned_size);
|
||||
return new BinMemoryStream (data, 0, size, entry.Name);
|
||||
}
|
||||
}
|
||||
|
||||
internal class NekoXCode : INekoFormat
|
||||
{
|
||||
uint m_seed;
|
||||
uint[] m_random;
|
||||
SimdProgram m_program;
|
||||
|
||||
public NekoXCode (uint init_key)
|
||||
{
|
||||
m_seed = init_key;
|
||||
m_random = InitTable (init_key);
|
||||
m_program = new SimdProgram (init_key);
|
||||
}
|
||||
|
||||
public void Decrypt (uint key, byte[] input, int offset, int length)
|
||||
{
|
||||
for (int i = 1; i < 7; ++i)
|
||||
{
|
||||
uint src = key % 0x28 * 2;
|
||||
m_program.mm[i] = m_random[src] | (ulong)m_random[src+1] << 32;
|
||||
key /= 0x28;
|
||||
}
|
||||
m_program.Execute (input, offset, length);
|
||||
}
|
||||
|
||||
public uint HashFromName (byte[] str, int offset, int length)
|
||||
{
|
||||
uint hash = m_seed;
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
hash = 0x100002A * (ShiftMap[str[offset+i] & 0xFF] ^ hash);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
public DirRecord ReadDir (IBinaryStream input)
|
||||
{
|
||||
uint hash = input.ReadUInt32();
|
||||
int count = input.ReadInt32();
|
||||
if (count != input.ReadInt32())
|
||||
throw new InvalidFormatException();
|
||||
return new DirRecord { Hash = hash, FileCount = count };
|
||||
}
|
||||
|
||||
public long NextOffset (Entry entry)
|
||||
{
|
||||
return entry.Offset + entry.Size;
|
||||
}
|
||||
|
||||
static readonly byte[] ShiftMap = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xc9, 0xca, 0x00, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0x00, 0xd2, 0xd3, 0x27, 0x25, 0xc8,
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0x00,
|
||||
0xd6, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
|
||||
0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0xd7, 0xc8, 0xd8, 0xd9, 0x26,
|
||||
0xda, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
|
||||
0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0xdb, 0x00, 0xdc, 0xdd, 0x00,
|
||||
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
|
||||
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
||||
0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
||||
0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
||||
0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
|
||||
0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
|
||||
0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8,
|
||||
};
|
||||
|
||||
class SimdProgram
|
||||
{
|
||||
public ulong[] mm = new ulong[7];
|
||||
|
||||
Action[] m_transform = new Action[4];
|
||||
Action[] m_shuffle = new Action[6];
|
||||
|
||||
Action<int>[] TransformList;
|
||||
Action[] ShuffleList;
|
||||
|
||||
public SimdProgram (uint key)
|
||||
{
|
||||
TransformList = new Action<int>[] {
|
||||
pxor, paddb, paddw, paddd, psubb, psubw, psubd,
|
||||
pxor, psubb, psubw, psubd, paddb, paddw, paddd,
|
||||
};
|
||||
ShuffleList = new Action[] {
|
||||
paddq_1_2, paddq_2_3, paddq_3_4, paddq_4_5, paddq_5_6, paddq_6_1,
|
||||
};
|
||||
|
||||
GenerateProgram (key);
|
||||
}
|
||||
|
||||
void pxor (int i) { mm[0] ^= mm[i]; }
|
||||
void paddb (int i) { mm[0] = MMX.PAddB (mm[0], mm[i]); }
|
||||
void paddw (int i) { mm[0] = MMX.PAddW (mm[0], mm[i]); }
|
||||
void paddd (int i) { mm[0] = MMX.PAddD (mm[0], mm[i]); }
|
||||
void psubb (int i) { mm[0] = MMX.PSubB (mm[0], mm[i]); }
|
||||
void psubw (int i) { mm[0] = MMX.PSubW (mm[0], mm[i]); }
|
||||
void psubd (int i) { mm[0] = MMX.PSubD (mm[0], mm[i]); }
|
||||
|
||||
void paddq_1_2 () { mm[1] += mm[2]; }
|
||||
void paddq_2_3 () { mm[2] += mm[3]; }
|
||||
void paddq_3_4 () { mm[3] += mm[4]; }
|
||||
void paddq_4_5 () { mm[4] += mm[5]; }
|
||||
void paddq_5_6 () { mm[5] += mm[6]; }
|
||||
void paddq_6_1 () { mm[6] += mm[1]; }
|
||||
|
||||
void GenerateProgram (uint key)
|
||||
{
|
||||
int t1 = 7 + (int)(key >> 28);
|
||||
int cmd_base = (int)key & 0xffff;
|
||||
int arg_base = (int)(key >> 16) & 0xfff;
|
||||
for (int i = 3; i >= 0; --i)
|
||||
{
|
||||
int cmd = ((cmd_base >> (4 * i)) + t1) % TransformList.Length;
|
||||
int arg = (arg_base >> (3 * i)) % 6 + 1;
|
||||
m_transform[3-i] = () => TransformList[cmd] (arg);
|
||||
}
|
||||
for (uint i = 0; i < 6; ++i)
|
||||
{
|
||||
m_shuffle[i] = ShuffleList[(i + key) % (uint)ShuffleList.Length];
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void Execute (byte[] input, int offset, int length)
|
||||
{
|
||||
if (offset < 0 || offset > input.Length)
|
||||
throw new ArgumentException ("offset");
|
||||
int count = Math.Min (length, input.Length-offset) / 8;
|
||||
if (0 == count)
|
||||
return;
|
||||
fixed (byte* data = &input[offset])
|
||||
{
|
||||
ulong* data64 = (ulong*)data;
|
||||
for (;;)
|
||||
{
|
||||
mm[0] = *data64;
|
||||
foreach (var cmd in m_transform)
|
||||
cmd();
|
||||
*data64++ = mm[0];
|
||||
if (1 == count--)
|
||||
break;
|
||||
foreach (var cmd in m_shuffle)
|
||||
cmd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint[] InitTable (uint key)
|
||||
{
|
||||
uint a = 0;
|
||||
uint b = 0;
|
||||
do
|
||||
{
|
||||
a <<= 1;
|
||||
b ^= 1;
|
||||
a = ((a | b) << (int)(key & 1)) | b;
|
||||
key >>= 1;
|
||||
}
|
||||
while (0 == (a & 0x80000000));
|
||||
key = a << 1;
|
||||
a = key + Binary.BigEndian (key);
|
||||
byte count = (byte)key;
|
||||
do
|
||||
{
|
||||
b = key ^ a;
|
||||
a = (b << 4) ^ (b >> 4) ^ (b << 3) ^ (b >> 3) ^ b;
|
||||
}
|
||||
while (--count != 0);
|
||||
|
||||
var table = new uint[154];
|
||||
for (int i = 0; i < table.Length; ++i)
|
||||
{
|
||||
b = key ^ a;
|
||||
a = (b << 4) ^ (b >> 4) ^ (b << 3) ^ (b >> 3) ^ b;
|
||||
table[i] = a;
|
||||
}
|
||||
return table;
|
||||
}
|
||||
}
|
||||
}
|
157
ArcFormats/Nekopack/ArcNEKO3.cs
Normal file
157
ArcFormats/Nekopack/ArcNEKO3.cs
Normal file
@ -0,0 +1,157 @@
|
||||
//! \file ArcNEKO3.cs
|
||||
//! \date 2022 Jun 17
|
||||
//! \brief Nekopack archive format implementation.
|
||||
//
|
||||
// Copyright (C) 2022 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using GameRes.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.Neko
|
||||
{
|
||||
internal class Neko3Archive : ArcFile
|
||||
{
|
||||
public readonly byte[] Key;
|
||||
|
||||
public Neko3Archive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, byte[] key)
|
||||
: base (arc, impl, dir)
|
||||
{
|
||||
Key = key;
|
||||
}
|
||||
}
|
||||
|
||||
internal class Neko3Entry : Entry
|
||||
{
|
||||
public ushort Seed;
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class Pak3Opener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "NEKOPACK/3"; } }
|
||||
public override string Description { get { return "NekoPack resource archive"; } }
|
||||
public override uint Signature { get { return 0x4F4B454E; } } // "NEKO"
|
||||
public override bool IsHierarchic { get { return true; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public Pak3Opener ()
|
||||
{
|
||||
Extensions = new string[] { "dat" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.View.AsciiEqual (4, "PACK") || file.MaxOffset <= 0x410)
|
||||
return null;
|
||||
|
||||
uint seed = file.View.ReadUInt32 (0xC);
|
||||
int count = (int)(seed % 7u) + 3;
|
||||
var key = file.View.ReadBytes (0x10, 0x400);
|
||||
while (count --> 0)
|
||||
{
|
||||
Decrypt (key, 0x400, key, (ushort)seed, true);
|
||||
}
|
||||
seed = file.View.ReadUInt16 (0x410);
|
||||
var index_info = file.View.ReadBytes (0x414, 8);
|
||||
Decrypt (index_info, 8, key, (ushort)seed);
|
||||
uint index_size = LittleEndian.ToUInt32 (index_info, 0);
|
||||
long data_offset = 0x41CL + index_size;
|
||||
if (data_offset >= file.MaxOffset)
|
||||
return null;
|
||||
var index = file.View.ReadBytes (0x41C, index_size);
|
||||
Decrypt (index, index.Length, key, (ushort)seed);
|
||||
|
||||
var dir = new List<Entry>();
|
||||
using (var input = new BinMemoryStream (index, file.Name))
|
||||
{
|
||||
int dir_count = input.ReadInt32();
|
||||
if (!IsSaneCount (dir_count))
|
||||
return null;
|
||||
for (int d = 0; d < dir_count; ++d)
|
||||
{
|
||||
int name_len = input.ReadUInt8();
|
||||
string dir_name = input.ReadCString (name_len);
|
||||
if (string.IsNullOrEmpty (dir_name))
|
||||
return null;
|
||||
int file_count = input.ReadInt32();
|
||||
if (!IsSaneCount (file_count))
|
||||
return null;
|
||||
for (int i = 0; i < file_count; ++i)
|
||||
{
|
||||
input.ReadByte();
|
||||
name_len = input.ReadUInt8();
|
||||
string name = input.ReadCString (name_len);
|
||||
name = string.Join ("/", dir_name, name);
|
||||
var entry = Create<Neko3Entry> (name);
|
||||
entry.Offset = data_offset + input.ReadUInt32();
|
||||
dir.Add (entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
var buffer = new byte[12];
|
||||
foreach (Neko3Entry entry in dir)
|
||||
{
|
||||
entry.Seed = file.View.ReadUInt16 (entry.Offset);
|
||||
file.View.Read (entry.Offset+4, buffer, 0, 8);
|
||||
Decrypt (buffer, 8, key, entry.Seed);
|
||||
entry.Size = LittleEndian.ToUInt32 (buffer, 0);
|
||||
entry.Offset += 12;
|
||||
}
|
||||
return new Neko3Archive (file, this, dir, key);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var narc = (Neko3Archive)arc;
|
||||
var nent = (Neko3Entry)entry;
|
||||
var data = arc.File.View.ReadBytes (entry.Offset, entry.Size);
|
||||
Decrypt (data, data.Length, narc.Key, nent.Seed);
|
||||
return new BinMemoryStream (data, entry.Name);
|
||||
}
|
||||
|
||||
void Decrypt (byte[] data, int length, byte[] key, ushort seed, bool init = false)
|
||||
{
|
||||
int count = length / 4;
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* data8 = data)
|
||||
{
|
||||
uint* data32 = (uint*)data8;
|
||||
while (count --> 0)
|
||||
{
|
||||
uint s = *data32;
|
||||
seed = (ushort)((seed + 0xC3) & 0x1FF);
|
||||
uint d = s ^ LittleEndian.ToUInt32 (key, seed);
|
||||
if (init)
|
||||
seed += (ushort)s;
|
||||
else
|
||||
seed += (ushort)d;
|
||||
*data32++ = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -34,6 +34,24 @@ namespace GameRes.Formats.Nonono
|
||||
public int Seed;
|
||||
}
|
||||
|
||||
internal class NpfArchive : ArcFile
|
||||
{
|
||||
public readonly IRandomGenerator KeyGenerator;
|
||||
|
||||
public NpfArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, IRandomGenerator rnd)
|
||||
: base (arc, impl, dir)
|
||||
{
|
||||
KeyGenerator = rnd;
|
||||
}
|
||||
}
|
||||
|
||||
internal interface IRandomGenerator
|
||||
{
|
||||
void SRand (int seed);
|
||||
|
||||
int Rand ();
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class NpfOpener : ArchiveFormat
|
||||
{
|
||||
@ -49,8 +67,28 @@ namespace GameRes.Formats.Nonono
|
||||
{
|
||||
if (file.View.ReadInt32 (4) != 4 || file.View.ReadInt32 (8) != 1)
|
||||
return null;
|
||||
|
||||
foreach (var rnd in GetGenerators())
|
||||
{
|
||||
var dir = ReadIndex (file, rnd);
|
||||
if (dir != null)
|
||||
{
|
||||
return new NpfArchive (file, this, dir, rnd);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
internal IEnumerable<IRandomGenerator> GetGenerators ()
|
||||
{
|
||||
yield return new RandomGenerator1 (DefaultSeed);
|
||||
yield return new RandomGenerator2 (DefaultSeed);
|
||||
}
|
||||
|
||||
List<Entry> ReadIndex (ArcView file, IRandomGenerator rnd)
|
||||
{
|
||||
var header = file.View.ReadBytes (12, 20);
|
||||
var rnd = new RandomGenerator (DefaultSeed);
|
||||
// rnd.SRand (DefaultSeed); // generator already seeded by GetGenerators()
|
||||
Decrypt (header, 0, header.Length, rnd);
|
||||
if (!header.AsciiEqual ("FAT "))
|
||||
return null;
|
||||
@ -84,16 +122,16 @@ namespace GameRes.Formats.Nonono
|
||||
pos += 20;
|
||||
name_pos += name_length;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
return dir;
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var nent = entry as NpfEntry;
|
||||
if (null == nent)
|
||||
return base.OpenEntry (arc, entry);
|
||||
var narc = (NpfArchive)arc;
|
||||
var nent = (NpfEntry)entry;
|
||||
var data = arc.File.View.ReadBytes (entry.Offset, entry.Size);
|
||||
var rnd = new RandomGenerator (nent.Seed);
|
||||
var rnd = narc.KeyGenerator;
|
||||
rnd.SRand (nent.Seed);
|
||||
Decrypt (data, 0, data.Length, rnd);
|
||||
return new BinMemoryStream (data, entry.Name);
|
||||
}
|
||||
@ -106,20 +144,20 @@ namespace GameRes.Formats.Nonono
|
||||
return new ImgXDecoder (input);
|
||||
}
|
||||
|
||||
internal void Decrypt (byte[] data, int pos, int count, RandomGenerator rnd)
|
||||
internal void Decrypt (byte[] data, int pos, int count, IRandomGenerator rnd)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
data[pos + i] ^= (byte)rnd.Rand();
|
||||
}
|
||||
}
|
||||
|
||||
internal class RandomGenerator
|
||||
internal class RandomGenerator1 : IRandomGenerator
|
||||
{
|
||||
int m_seed;
|
||||
|
||||
const int DefaultSeed = 0x67895;
|
||||
|
||||
public RandomGenerator (int seed = DefaultSeed)
|
||||
public RandomGenerator1 (int seed = DefaultSeed)
|
||||
{
|
||||
SRand (seed);
|
||||
}
|
||||
@ -141,4 +179,30 @@ namespace GameRes.Formats.Nonono
|
||||
return m_seed;
|
||||
}
|
||||
}
|
||||
|
||||
internal class RandomGenerator2 : IRandomGenerator
|
||||
{
|
||||
int m_seed1;
|
||||
int m_seed2;
|
||||
|
||||
const int DefaultSeed = 0x67895;
|
||||
|
||||
public RandomGenerator2 (int seed = DefaultSeed)
|
||||
{
|
||||
SRand (seed);
|
||||
}
|
||||
|
||||
public void SRand (int seed)
|
||||
{
|
||||
m_seed1 = seed;
|
||||
m_seed2 = ((seed >> 12) ^ (seed << 18)) - 0x579E2B8D;
|
||||
}
|
||||
|
||||
public int Rand ()
|
||||
{
|
||||
int n = m_seed2 + ((m_seed1 >> 10) ^ (m_seed1 << 14));
|
||||
m_seed2 = n - 0x15633649 + ((m_seed2 >> 12) ^ (m_seed2 << 18));
|
||||
return m_seed2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,12 @@ namespace GameRes.Formats.Nonono
|
||||
using (var input = new MemoryStream (bitmap, header_size, bitmap.Length - header_size))
|
||||
{
|
||||
if (8 == m_info.BPP)
|
||||
palette = ImageFormat.ReadPalette (input);
|
||||
{
|
||||
int num_colors = bitmap.ToInt32 (0x20);
|
||||
if (0 == num_colors)
|
||||
num_colors = 0x100;
|
||||
palette = ImageFormat.ReadPalette (input, num_colors);
|
||||
}
|
||||
input.Read (pixels, 0, pixels.Length);
|
||||
}
|
||||
PixelFormat format = 8 == m_info.BPP ? PixelFormats.Indexed8
|
||||
|
@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion ("1.2.48.2176")]
|
||||
[assembly: AssemblyFileVersion ("1.2.48.2176")]
|
||||
[assembly: AssemblyVersion ("1.2.48.2200")]
|
||||
[assembly: AssemblyFileVersion ("1.2.48.2200")]
|
||||
|
14
ArcFormats/Properties/Settings.Designer.cs
generated
14
ArcFormats/Properties/Settings.Designer.cs
generated
@ -12,7 +12,7 @@ namespace GameRes.Formats.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")]
|
||||
public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
@ -801,5 +801,17 @@ namespace GameRes.Formats.Properties {
|
||||
this["SJDatTitle"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("932")]
|
||||
public int AFAEncodingCP {
|
||||
get {
|
||||
return ((int)(this["AFAEncodingCP"]));
|
||||
}
|
||||
set {
|
||||
this["AFAEncodingCP"] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -197,5 +197,8 @@
|
||||
<Setting Name="SJDatTitle" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
<Setting Name="AFAEncodingCP" Type="System.Int32" Scope="User">
|
||||
<Value Profile="(Default)">932</Value>
|
||||
</Setting>
|
||||
</Settings>
|
||||
</SettingsFile>
|
@ -84,6 +84,9 @@ namespace GameRes.Formats.Rpm
|
||||
/// <summary>Minimum entry length across all possible archive schemes.</summary>
|
||||
const int MinEntryLength = 0x24;
|
||||
|
||||
// largest size should be first
|
||||
static readonly int[] PossibleNameSizes = new[] { 0x20, 0x18 };
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (0);
|
||||
@ -94,7 +97,7 @@ namespace GameRes.Formats.Rpm
|
||||
return null;
|
||||
|
||||
var index_reader = new ArcIndexReader (file, count, is_compressed != 0);
|
||||
var scheme = index_reader.GuessScheme (8, new int[] { 0x20, 0x18 });
|
||||
var scheme = index_reader.GuessScheme (8, PossibleNameSizes);
|
||||
// additional checks to avoid dialog popup on false positives
|
||||
if (null == scheme && KnownSchemes.Count > 0 && file.Name.HasExtension (".arc"))
|
||||
{
|
||||
|
@ -28,6 +28,9 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
// [960920][Petit] Trouble Outsiders
|
||||
// [980220][Euphony Production] Happening Journey
|
||||
|
||||
namespace GameRes.Formats.ArchAngel
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
@ -44,7 +47,7 @@ namespace GameRes.Formats.ArchAngel
|
||||
ContainedFormats = new[] { "CB" };
|
||||
}
|
||||
|
||||
static readonly string[] DefaultSections = { "image", "script", null };
|
||||
static readonly string[] DefaultSections = { "image", "script", "" };
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
@ -54,13 +57,7 @@ namespace GameRes.Formats.ArchAngel
|
||||
int file_count = file.View.ReadInt16 (0);
|
||||
if (!IsSaneCount (file_count))
|
||||
return null;
|
||||
uint index_pos = 2;
|
||||
var size_table = new uint[file_count];
|
||||
for (int i = 0; i < file_count; ++i)
|
||||
{
|
||||
size_table[i] = file.View.ReadUInt32 (index_pos);
|
||||
index_pos += 4;
|
||||
}
|
||||
long index_pos = 2 + 4 * file_count;
|
||||
var section_table = new SortedDictionary<int, uint>();
|
||||
uint min_offset = (uint)file.MaxOffset;
|
||||
while (index_pos + 6 <= min_offset)
|
||||
@ -76,26 +73,34 @@ namespace GameRes.Formats.ArchAngel
|
||||
}
|
||||
var dir = new List<Entry> (file_count);
|
||||
int section_num = 0;
|
||||
Func<string> get_type;
|
||||
if (section_table.Count == DefaultSections.Length)
|
||||
get_type = () => DefaultSections[section_num];
|
||||
else
|
||||
get_type = () => section_num > 0 ? "image" : "";
|
||||
foreach (var section in section_table)
|
||||
{
|
||||
int i = section.Key;
|
||||
uint base_offset = section.Value;
|
||||
do
|
||||
{
|
||||
uint size = size_table[i];
|
||||
var entry = new PackedEntry {
|
||||
Name = string.Format ("{0}-{1:D6}", section_num, i),
|
||||
Offset = base_offset,
|
||||
Size = size,
|
||||
};
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
if (section_num < DefaultSections.Length && DefaultSections[section_num] != null)
|
||||
entry.Type = DefaultSections[section_num];
|
||||
if ("script" == entry.Type)
|
||||
entry.IsPacked = true;
|
||||
dir.Add (entry);
|
||||
base_offset += size;
|
||||
uint size = file.View.ReadUInt32 (2 + i * 4);
|
||||
if (size > 0)
|
||||
{
|
||||
var entry = new PackedEntry
|
||||
{
|
||||
Name = string.Format("{0}-{1:D6}", section_num, i),
|
||||
Type = get_type(),
|
||||
Offset = base_offset,
|
||||
Size = size,
|
||||
};
|
||||
if (!entry.CheckPlacement(file.MaxOffset))
|
||||
return null;
|
||||
if ("script" == entry.Type)
|
||||
entry.IsPacked = true;
|
||||
dir.Add(entry);
|
||||
base_offset += size;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
while (i < file_count && !section_table.ContainsKey (i));
|
||||
|
@ -85,6 +85,14 @@ namespace GameRes.Formats.Seraphim
|
||||
{
|
||||
input = arc.File.CreateStream (entry.Offset+4, entry.Size-4);
|
||||
return new ZLibStream (input.AsStream, CompressionMode.Decompress);
|
||||
/*
|
||||
using (var compr = new ZLibStream (input.AsStream, CompressionMode.Decompress))
|
||||
using (var bin = new BinaryStream (compr, entry.Name))
|
||||
{
|
||||
var data = LzDecompress (bin);
|
||||
return new BinMemoryStream (data, entry.Name);
|
||||
}
|
||||
*/
|
||||
}
|
||||
input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
if (signature < 4 || 0 != (signature & 0xFF000000))
|
||||
@ -141,4 +149,72 @@ namespace GameRes.Formats.Seraphim
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class Scn95Opener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "SCN/ARCH"; } }
|
||||
public override string Description { get { return "Archangel engine scripts archive"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public Scn95Opener ()
|
||||
{
|
||||
Extensions = new[] { "dat" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!VFS.IsPathEqualsToFileName (file.Name, "SCNPAC.DAT"))
|
||||
return null;
|
||||
uint offset = file.View.ReadUInt32 (0);
|
||||
int count = (int)offset / 4;
|
||||
if (offset >= file.MaxOffset || !IsSaneCount (count))
|
||||
return null;
|
||||
|
||||
int index_offset = 4;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
uint size = file.View.ReadUInt32 (index_offset);
|
||||
if (0 == size)
|
||||
return null;
|
||||
var entry = new Entry {
|
||||
Name = i.ToString ("D5"),
|
||||
Type = "script",
|
||||
Offset = offset + 4,
|
||||
Size = size,
|
||||
};
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
offset += size;
|
||||
index_offset += 4;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
IBinaryStream input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
if (input.Signature < 4 || 0 != (input.Signature & 0xFF000000))
|
||||
{
|
||||
return input.AsStream;
|
||||
}
|
||||
try
|
||||
{
|
||||
var data = ScnOpener.LzDecompress (input);
|
||||
return new BinMemoryStream (data, entry.Name);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
}
|
||||
finally
|
||||
{
|
||||
input.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -120,6 +120,8 @@ namespace GameRes.Formats.Seraphim
|
||||
|
||||
List<Entry> ReadIndex (ArcView file, long index_offset, long max_offset)
|
||||
{
|
||||
if (index_offset >= max_offset)
|
||||
return null;
|
||||
int base_count = file.View.ReadInt32 (index_offset);
|
||||
int file_count = file.View.ReadInt32 (index_offset + 4);
|
||||
index_offset += 8;
|
||||
@ -220,7 +222,9 @@ namespace GameRes.Formats.Seraphim
|
||||
{
|
||||
uint width = input.ReadUInt16();
|
||||
uint height = input.ReadUInt16();
|
||||
if (width > 0x4100 || 0 == width || 0 == height || width * height * 3 + 4 != input.Length)
|
||||
uint plane_size = width * height;
|
||||
uint total_size = (uint)(input.Length - 4);
|
||||
if (width > 0x4100 || 0 == width || 0 == height || (total_size % plane_size) != 0)
|
||||
{
|
||||
input.Position = 0;
|
||||
return new ImageFormatDecoder (input);
|
||||
|
@ -47,7 +47,7 @@ namespace GameRes.Formats.Seraphim
|
||||
Extensions = new string[] { "dat" };
|
||||
}
|
||||
|
||||
static readonly Regex VoiceRe = new Regex (@"^Voice\d\.dat$", RegexOptions.IgnoreCase);
|
||||
static readonly Regex VoiceRe = new Regex (@"^Voice(?:\d|pac)\.dat$", RegexOptions.IgnoreCase);
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
|
@ -47,12 +47,15 @@ namespace GameRes.Formats.Seraphim
|
||||
|
||||
public SeraphCfImage ()
|
||||
{
|
||||
Extensions = new string[] { "cts" };
|
||||
Signatures = new [] { 0x4643u, 0x024643u, 0x044643u, 0x074643u, 0x094643u, 0x144643u, 0u };
|
||||
Extensions = new [] { "cts" };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream stream)
|
||||
{
|
||||
var header = stream.ReadHeader (0x10);
|
||||
if ('C' != header[0] || 'F' != header[1] || 0 != header[3])
|
||||
return null;
|
||||
int packed_size = header.ToInt32 (12);
|
||||
if (packed_size <= 0 || packed_size > stream.Length-0x10)
|
||||
return null;
|
||||
|
139
ArcFormats/Sohfu/ArcSKA.cs
Normal file
139
ArcFormats/Sohfu/ArcSKA.cs
Normal file
@ -0,0 +1,139 @@
|
||||
//! \file ArcSKA.cs
|
||||
//! \date 2022 Jun 12
|
||||
//! \brief Sohfu resource archive.
|
||||
//
|
||||
// Copyright (C) 2022 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using GameRes.Utility;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.Sohfu
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class SkaOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "SKA/SOHFU"; } }
|
||||
public override string Description { get { return "Sohfu resource archive"; } }
|
||||
public override uint Signature { get { return 0x32465049; } } // 'IPF2'
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (4);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
|
||||
uint index_pos = 8;
|
||||
var name_buffer = new byte[0x10];
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
file.View.Read (index_pos, name_buffer, 0, 0x10);
|
||||
int name_len = 0;
|
||||
while (name_len < name_buffer.Length && name_buffer[name_len] != 0)
|
||||
++name_len;
|
||||
var name = Encodings.cp932.GetString (name_buffer, 0, name_len);
|
||||
++name_len;
|
||||
string ext = null;
|
||||
if (name_len < 0x10)
|
||||
ext = Binary.GetCString (name_buffer, name_len, 0x10 - name_len);
|
||||
if (!string.IsNullOrEmpty (ext))
|
||||
name = Path.ChangeExtension (name, ext);
|
||||
var entry = Create<PackedEntry> (name);
|
||||
entry.Offset = file.View.ReadUInt32 (index_pos+0x10);
|
||||
entry.Size = file.View.ReadUInt32 (index_pos+0x14);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
index_pos += 0x18;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var pent = (PackedEntry)entry;
|
||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
if (!pent.IsPacked)
|
||||
{
|
||||
if (input.Signature != 0x4238534C) // 'LS8B'
|
||||
return input;
|
||||
pent.IsPacked = true;
|
||||
pent.UnpackedSize = arc.File.View.ReadUInt32 (pent.Offset+4);
|
||||
}
|
||||
using (input)
|
||||
{
|
||||
var data = new byte[pent.UnpackedSize];
|
||||
input.Position = 0xC;
|
||||
LzssUnpack (input, data);
|
||||
return new BinMemoryStream (data);
|
||||
}
|
||||
}
|
||||
|
||||
void LzssUnpack (IBinaryStream input, byte[] output)
|
||||
{
|
||||
byte[] frame = new byte[0x1000];
|
||||
int frame_pos = 0xFFF;
|
||||
const int frame_mask = 0xFFF;
|
||||
int ctl = 1;
|
||||
int dst = 0;
|
||||
while (dst < output.Length)
|
||||
{
|
||||
if (1 == ctl)
|
||||
{
|
||||
ctl = input.ReadByte();
|
||||
if (-1 == ctl)
|
||||
break;
|
||||
ctl |= 0x100;
|
||||
}
|
||||
if (0 == (ctl & 1))
|
||||
{
|
||||
int b = input.ReadByte();
|
||||
if (-1 == b)
|
||||
break;
|
||||
frame[++frame_pos & frame_mask] = (byte)b;
|
||||
output[dst++] = (byte)b;
|
||||
}
|
||||
else
|
||||
{
|
||||
int lo = input.ReadByte();
|
||||
if (-1 == lo)
|
||||
break;
|
||||
int hi = input.ReadByte();
|
||||
if (-1 == hi)
|
||||
break;
|
||||
int offset = hi << 4 | lo >> 4;
|
||||
for (int count = 3 + (lo & 0xF); count != 0; --count)
|
||||
{
|
||||
byte v = frame[(offset + frame_pos++ - 0xFFF) & frame_mask];
|
||||
frame[frame_pos & frame_mask] = v;
|
||||
output[dst++] = v;
|
||||
}
|
||||
}
|
||||
ctl >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
146
ArcFormats/Sohfu/ImageDTL.cs
Normal file
146
ArcFormats/Sohfu/ImageDTL.cs
Normal file
@ -0,0 +1,146 @@
|
||||
//! \file ImageDTL.cs
|
||||
//! \date 2022 Jun 12
|
||||
//! \brief Sohfu image format;
|
||||
//
|
||||
// Copyright (C) 2022 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.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace GameRes.Formats.Sohfu
|
||||
{
|
||||
internal class DtlMetaData : ImageMetaData
|
||||
{
|
||||
public int Stride;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class DtlFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "DTL/SOHFU"; } }
|
||||
public override string Description { get { return "Sohfu image format"; } }
|
||||
public override uint Signature { get { return 0x5F4C5444; } } // 'DTL_'
|
||||
|
||||
public DtlFormat ()
|
||||
{
|
||||
Extensions = new[] { "ls8b", "ls8" };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x18);
|
||||
int bpp = header.ToInt32 (16);
|
||||
if (bpp != 24 && bpp != 32 && bpp != 8 && bpp != 4)
|
||||
return null;
|
||||
return new DtlMetaData
|
||||
{
|
||||
Width = header.ToUInt32 (8),
|
||||
Height = header.ToUInt32 (12),
|
||||
BPP = bpp,
|
||||
Stride = header.ToInt32 (20),
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var meta = (DtlMetaData)info;
|
||||
file.Position = 0x18;
|
||||
var pixels = file.ReadBytes (meta.Stride * meta.iHeight);
|
||||
PixelFormat format;
|
||||
if (24 == meta.BPP)
|
||||
format = PixelFormats.Bgr24;
|
||||
else if (32 == meta.BPP)
|
||||
format = PixelFormats.Bgra32;
|
||||
else if (4 == meta.BPP)
|
||||
format = PixelFormats.Gray4;
|
||||
else
|
||||
format = PixelFormats.Gray8;
|
||||
return ImageData.Create (meta, format, null, pixels, meta.Stride);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("DtlFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class DtlcFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "DTLC/SOHFU"; } }
|
||||
public override string Description { get { return "Sohfu image format"; } }
|
||||
public override uint Signature { get { return 0x434C5444; } } // 'DTLC'
|
||||
|
||||
public DtlcFormat ()
|
||||
{
|
||||
Extensions = new[] { "ls8b", "ls8" };
|
||||
Signatures = new[] { 0x434C5444u, 0x414C5444u }; // 'DTLA'
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x18);
|
||||
int bpp = header.ToInt32 (16);
|
||||
if (bpp != 24 && bpp != 32)
|
||||
return null;
|
||||
return new DtlMetaData
|
||||
{
|
||||
Width = header.ToUInt32 (8),
|
||||
Height = header.ToUInt32 (12),
|
||||
BPP = bpp,
|
||||
Stride = header.ToInt32 (20),
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var meta = (DtlMetaData)info;
|
||||
// var lineBuffer = new int[meta.iHeight][];
|
||||
file.Position = 0x18;
|
||||
for (int y = 0; y < meta.iHeight; ++y)
|
||||
{
|
||||
int n = file.ReadInt32();
|
||||
// lineBuffer[y] = new int[n * 2];
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
// lineBuffer[y][i*2] = file.ReadInt32(); // x
|
||||
// lineBuffer[y][i*2+1] = file.ReadInt32(); // number of pixels
|
||||
file.ReadInt32();
|
||||
file.ReadInt32();
|
||||
}
|
||||
}
|
||||
var pixels = file.ReadBytes (meta.Stride * meta.iHeight);
|
||||
PixelFormat format;
|
||||
if (24 == meta.BPP)
|
||||
format = PixelFormats.Bgr24;
|
||||
else
|
||||
format = PixelFormats.Bgra32;
|
||||
return ImageData.Create (meta, format, null, pixels, meta.Stride);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("DtlcFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
}
|
@ -37,6 +37,11 @@ namespace GameRes.Formats.Will
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public BmxOpener ()
|
||||
{
|
||||
ContainedFormats = new[] { "BC" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
uint total_size = file.View.ReadUInt32 (0);
|
||||
@ -48,34 +53,29 @@ namespace GameRes.Formats.Will
|
||||
|
||||
var dir = new List<Entry> (count);
|
||||
uint index_offset = 0x10;
|
||||
uint next_offset = file.View.ReadUInt32 (index_offset+0x1C);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var name = file.View.ReadString (index_offset, 0x1C);
|
||||
if (0 == name.Length)
|
||||
break;
|
||||
index_offset += 0x20;
|
||||
var entry = FormatCatalog.Instance.Create<Entry> (name);
|
||||
entry.Offset = next_offset;
|
||||
if (i+1 < count)
|
||||
{
|
||||
next_offset = file.View.ReadUInt32 (index_offset+0x1C);
|
||||
if (0 == next_offset)
|
||||
next_offset = (uint)file.MaxOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
next_offset = (uint)file.MaxOffset;
|
||||
}
|
||||
entry.Size = (uint)(next_offset - entry.Offset);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
entry.Offset = file.View.ReadUInt32 (index_offset+0x1C);
|
||||
if (string.IsNullOrEmpty (entry.Type))
|
||||
entry.Type = "image";
|
||||
dir.Add (entry);
|
||||
index_offset += 0x20;
|
||||
}
|
||||
if (0 == dir.Count)
|
||||
return null;
|
||||
long last_offset = file.MaxOffset;
|
||||
for (int i = dir.Count - 1; i >= 0; --i)
|
||||
{
|
||||
var entry = dir[i];
|
||||
entry.Size = (uint)(last_offset - entry.Offset);
|
||||
last_offset = entry.Offset;
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ namespace GameRes.Formats.Will
|
||||
public MbfOpener ()
|
||||
{
|
||||
Signatures = new uint[] { 0x3046424D, 0x3146424D };
|
||||
ContainedFormats = new[] { "BC" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
|
@ -43,6 +43,7 @@ namespace GameRes.Formats.Will
|
||||
{
|
||||
Extensions = new string[] { "vpk" };
|
||||
Signatures = new uint[] { 0x314B5056, 0x304B5056 };
|
||||
ContainedFormats = new[] { "WAV" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
|
@ -43,6 +43,7 @@ namespace GameRes.Formats.Will
|
||||
{
|
||||
Extensions = new string[] { "wsm" };
|
||||
Signatures = new uint[] { 0x324D5357, 0x334D5357 };
|
||||
ContainedFormats = new[] { "WAV" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
@ -71,6 +72,7 @@ namespace GameRes.Formats.Will
|
||||
dir.Add (entry);
|
||||
}
|
||||
int index_offset = 0;
|
||||
var names = new HashSet<string>();
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
int entry_pos = index.ToInt32 (index_offset);
|
||||
@ -83,8 +85,20 @@ namespace GameRes.Formats.Will
|
||||
int entry_idx = index[entry_pos+3];
|
||||
if (entry_idx >= dir.Count)
|
||||
return null;
|
||||
if (0 == entry_idx)
|
||||
entry_idx = i;
|
||||
var entry = dir[entry_idx];
|
||||
entry.Name = string.Format ("{0:D2}_{1}.wav", entry_idx, name);
|
||||
entry.Name = name + ".wav";
|
||||
names.Add (entry.Name);
|
||||
}
|
||||
if (names.Count != dir.Count)
|
||||
{
|
||||
// make filenames unique by prepending index number
|
||||
for (int i = 0; i < dir.Count; ++i)
|
||||
{
|
||||
var entry = dir[i];
|
||||
entry.Name = string.Format("{0:D2}_{1}", i, entry.Name);
|
||||
}
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
122
ArcFormats/TechGian/ArcBIN.cs
Normal file
122
ArcFormats/TechGian/ArcBIN.cs
Normal file
@ -0,0 +1,122 @@
|
||||
//! \file ArcBIN.cs
|
||||
//! \date 2023 Aug 09
|
||||
//! \brief Tech Gian 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.IO;
|
||||
using GameRes.Formats.Eagls;
|
||||
using GameRes.Utility;
|
||||
|
||||
// [121102][Tech Gian Archive] Hanafuda Market Totsugeki! Tonari no Kanban Musume
|
||||
|
||||
namespace GameRes.Formats.TechGian
|
||||
{
|
||||
internal class RfilEntry : Entry
|
||||
{
|
||||
public int EncryptionMethod;
|
||||
|
||||
public bool IsEncrypted {
|
||||
get { return EncryptionMethod == 1 || EncryptionMethod == 2 || EncryptionMethod == 4; }
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class BinOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "BIN/RFIL"; } }
|
||||
public override string Description { get { return "Tech Gian archive"; } }
|
||||
public override uint Signature { get { return 0x4C494652; } } // 'RFIL'
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (8);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
bool is_encrypted = file.View.ReadInt32 (12) == 1234;
|
||||
long index_pos = 0x10;
|
||||
var buffer = new byte[0x40];
|
||||
var rnd = new CRuntimeRandomGenerator();
|
||||
rnd.SRand (0);
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
file.View.Read (index_pos, buffer, 0, 0x40);
|
||||
if (is_encrypted)
|
||||
DecryptRand (buffer, 0, 0x40, rnd);
|
||||
var name = Binary.GetCString (buffer, 0, 0x30);
|
||||
var entry = Create<RfilEntry> (name);
|
||||
entry.Offset = buffer.ToUInt32 (0x34);
|
||||
entry.Size = buffer.ToUInt32 (0x38);
|
||||
entry.EncryptionMethod = buffer.ToInt32 (0x3C);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
index_pos += 0x40;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var rent = (RfilEntry)entry;
|
||||
if (!rent.IsEncrypted)
|
||||
return base.OpenEntry (arc, entry);
|
||||
var data = arc.File.View.ReadBytes (rent.Offset, rent.Size);
|
||||
switch (rent.EncryptionMethod)
|
||||
{
|
||||
case 1:
|
||||
DecryptData (data, 0, data.Length);
|
||||
break;
|
||||
case 2:
|
||||
if (data.Length > 0)
|
||||
DecryptData (data, 0, (data.Length - 1) / 100 + 1);
|
||||
break;
|
||||
case 4:
|
||||
DecryptData (data, 0, Math.Min (1024, data.Length));
|
||||
break;
|
||||
}
|
||||
return new BinMemoryStream (data, entry.Name);
|
||||
}
|
||||
|
||||
internal static void DecryptData (byte[] data, int pos, int length)
|
||||
{
|
||||
while (length --> 0)
|
||||
{
|
||||
data[pos++] ^= 0x7F;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void DecryptRand (byte[] data, int pos, int length, IRandomGenerator rnd)
|
||||
{
|
||||
while (length --> 0)
|
||||
{
|
||||
data[pos++] ^= (byte)rnd.Rand();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -93,7 +93,7 @@ namespace GameRes.Formats.TechnoBrain
|
||||
if (0x20706D62 != file.ReadInt32()) // 'bmp '
|
||||
return false;
|
||||
int bmp_size = file.ReadInt32();
|
||||
if (bmp_size <= 0x20)
|
||||
if (bmp_size < 0x1C)
|
||||
return false;
|
||||
info.BmpOffset = file.Position + 0x18;
|
||||
info.Width = file.ReadUInt16();
|
||||
|
@ -93,8 +93,9 @@ namespace GameRes.Formats.Unity
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
input.Align();
|
||||
var file_id = input.ReadInt32();
|
||||
var id = input.ReadId();
|
||||
m_adds[id] = input.ReadInt32();
|
||||
m_adds[id] = file_id;
|
||||
}
|
||||
}
|
||||
if (Format >= 6)
|
||||
@ -353,8 +354,11 @@ namespace GameRes.Formats.Unity
|
||||
{
|
||||
int count = reader.ReadInt32();
|
||||
int buffer_bytes = reader.ReadInt32();
|
||||
var node_data = reader.ReadBytes (24 * count);
|
||||
int node_size = m_format >= 18 ? 32 : 24;
|
||||
var node_data = reader.ReadBytes (node_size * count);
|
||||
m_data = reader.ReadBytes (buffer_bytes);
|
||||
if (m_format >= 21)
|
||||
reader.Skip (4);
|
||||
|
||||
var parents = new Stack<TypeTree>();
|
||||
parents.Push (this);
|
||||
@ -384,6 +388,8 @@ namespace GameRes.Formats.Unity
|
||||
current.Size = buf.ReadInt32();
|
||||
current.Index = buf.ReadUInt32();
|
||||
current.Flags = buf.ReadInt32();
|
||||
if (m_format >= 18)
|
||||
buf.ReadInt64();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -104,13 +104,42 @@ namespace GameRes.Formats.Unity
|
||||
Load2021 (reader);
|
||||
return;
|
||||
}
|
||||
if (type.Version != "2017.3.1f1")
|
||||
if (type.Version != "2017.3.1f1" && type.Version != "2019.3.0f1" && type.Version != "2017.4.3f1")
|
||||
{
|
||||
Load (reader);
|
||||
if (0 == m_DataLength && type.Version.StartsWith ("2017.")) // "2017.2.0f3" || "2017.1.1p1"
|
||||
reader.ReadInt64();
|
||||
return;
|
||||
}
|
||||
// type hash = [1E 87 D8 2D 4F D0 58 50 9A 3C 78 66 DB 0E 73 56]
|
||||
m_Name = reader.ReadString();
|
||||
reader.Align();
|
||||
reader.ReadInt32(); // m_ForcedFallbackFormat
|
||||
reader.ReadInt32(); // m_DownscaleFallback
|
||||
m_Width = reader.ReadInt32();
|
||||
m_Height = reader.ReadInt32();
|
||||
m_CompleteImageSize = reader.ReadInt32();
|
||||
m_TextureFormat = (TextureFormat)reader.ReadInt32();
|
||||
m_MipCount = reader.ReadInt32();
|
||||
m_IsReadable = reader.ReadBool();
|
||||
reader.Align();
|
||||
if ("2019.3.0f1" == type.Version) // type hash = [EE 6C 40 81 7D 29 51 92 9C DB 4F 5A 60 87 4F 5D]
|
||||
reader.ReadInt32(); // m_StreamingMipmapsPriority
|
||||
m_ImageCount = reader.ReadInt32();
|
||||
m_TextureDimension = reader.ReadInt32();
|
||||
m_FilterMode = reader.ReadInt32();
|
||||
m_Aniso = reader.ReadInt32();
|
||||
m_MipBias = reader.ReadFloat();
|
||||
m_WrapMode = reader.ReadInt32(); // m_WrapU
|
||||
reader.ReadInt32(); // m_WrapV
|
||||
reader.ReadInt32(); // m_WrapW
|
||||
reader.ReadInt32(); // m_LightmapFormat
|
||||
m_ColorSpace = reader.ReadInt32();
|
||||
m_DataLength = reader.ReadInt32();
|
||||
}
|
||||
|
||||
public void Load2017 (AssetReader reader)
|
||||
{
|
||||
m_Name = reader.ReadString();
|
||||
reader.Align();
|
||||
reader.ReadInt32(); // m_ForcedFallbackFormat
|
||||
|
75
ArcFormats/Winters/ArcCFP.cs
Normal file
75
ArcFormats/Winters/ArcCFP.cs
Normal file
@ -0,0 +1,75 @@
|
||||
//! \file ArcCFP.cs
|
||||
//! \date 2022 May 17
|
||||
//! \brief Winters resource archive.
|
||||
//
|
||||
// Copyright (C) 2022 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
// [110527][Winters] Kiss x 700 Kiss Tantei
|
||||
|
||||
namespace GameRes.Formats.Winters
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class CfpOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "CFP/CAPYBARA"; } }
|
||||
public override string Description { get { return "Winters resource archive"; } }
|
||||
public override uint Signature { get { return 0x59504143; } } // 'CAPYBARA DAT 002'
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.View.AsciiEqual (0, "CAPYBARA DAT 002"))
|
||||
return null;
|
||||
uint names_offset = file.View.ReadUInt32 (0x14);
|
||||
uint names_length = file.View.ReadUInt32 (0x18);
|
||||
uint index_offset = 0x20;
|
||||
var dir = new List<Entry>();
|
||||
using (var names = file.CreateStream (names_offset, names_length))
|
||||
using (var index = new StreamReader (names, Encodings.cp932))
|
||||
{
|
||||
string name;
|
||||
while (index_offset < names_offset && (name = index.ReadLine()) != null)
|
||||
{
|
||||
if (name.Length > 0)
|
||||
{
|
||||
var entry = FormatCatalog.Instance.Create<Entry> (name);
|
||||
entry.Offset = file.View.ReadUInt32 (index_offset);
|
||||
entry.Size = file.View.ReadUInt32 (index_offset+4);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
}
|
||||
index_offset += 0xC;
|
||||
}
|
||||
}
|
||||
if (0 == dir.Count)
|
||||
return null;
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
}
|
@ -57,6 +57,7 @@ namespace GameRes.Formats.Xuse
|
||||
{
|
||||
Extensions = new string[] { "wag", "4ag", "004" };
|
||||
Signatures = new uint[] { 0x40474157, 0x34464147 }; // 'GAF4'
|
||||
// ContainedFormats = new [] { "PNG", "P/4AG" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
@ -284,7 +285,7 @@ namespace GameRes.Formats.Xuse
|
||||
return entry;
|
||||
}
|
||||
|
||||
static readonly Regex DriveRe = new Regex (@"^(?:.+:)?\\+");
|
||||
static readonly Regex DriveRe = new Regex (@"^(?:.+:|\.\.)?\\+");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
64
ArcFormats/Xuse/ImageP.cs
Normal file
64
ArcFormats/Xuse/ImageP.cs
Normal file
@ -0,0 +1,64 @@
|
||||
//! \file ImageP.cs
|
||||
//! \date 2022 May 07
|
||||
//! \brief Obfuscated PNG file.
|
||||
//
|
||||
// Copyright (C) 2022 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.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace GameRes.Formats.Xuse
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class P4AGFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "P/4AG"; } }
|
||||
public override string Description { get { return "Xuse/Eternal obfuscated PNG image"; } }
|
||||
public override uint Signature { get { return 0x0A0D474E; } } // 'NG\n\r'
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
using (var input = OpenAsPng (file))
|
||||
return Png.ReadMetaData (input);
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
using (var input = OpenAsPng (file))
|
||||
return Png.Read (input, info);
|
||||
}
|
||||
|
||||
static readonly byte[] HeaderBytes = new byte[2] { PngFormat.HeaderBytes[0], PngFormat.HeaderBytes[1] };
|
||||
|
||||
internal IBinaryStream OpenAsPng (IBinaryStream file)
|
||||
{
|
||||
var input = new PrefixStream (HeaderBytes, file.AsStream, true);
|
||||
return new BinaryStream (input, file.Name);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("P4AGFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
}
|
142
ArcFormats/Zyx/ImageXMG.cs
Normal file
142
ArcFormats/Zyx/ImageXMG.cs
Normal file
@ -0,0 +1,142 @@
|
||||
//! \file ImageXMG.cs
|
||||
//! \date 2022 Jun 19
|
||||
//! \brief ZyX image format.
|
||||
//
|
||||
// Copyright (C) 2022 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using GameRes.Utility;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace GameRes.Formats.Ikura
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class XmgFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "XMG"; } }
|
||||
public override string Description { get { return "ZyX image format"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
if (!file.Name.HasExtension (".xmg"))
|
||||
return null;
|
||||
var header = file.ReadBytes (12);
|
||||
Decrypt (header);
|
||||
if (header[2] != 0 || header[3] != 0)
|
||||
return null;
|
||||
int width = LittleEndian.ToInt16 (header, 4);
|
||||
int height = LittleEndian.ToInt16 (header, 6);
|
||||
if (width <= 0 || height <= 0)
|
||||
return null;
|
||||
return new ImageMetaData {
|
||||
Width = (uint)width,
|
||||
Height = (uint)height,
|
||||
BPP = 8,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
file.Position = 12;
|
||||
var palette_data = file.ReadBytes (0x300);
|
||||
Decrypt (palette_data, (byte)(12 * 7));
|
||||
var palette = ConvertPalette (palette_data);
|
||||
var pixels = new byte[info.iWidth * info.iHeight];
|
||||
int dst = 0;
|
||||
for (int y = 0; y < info.iHeight; ++y)
|
||||
{
|
||||
int x = 0;
|
||||
while (x < info.iWidth)
|
||||
{
|
||||
byte ctl = file.ReadUInt8();
|
||||
int offset;
|
||||
int count;
|
||||
if ((ctl & 0xC0) != 0)
|
||||
{
|
||||
if ((ctl & 0x80) != 0)
|
||||
{
|
||||
offset = -((ctl << 8 | file.ReadUInt8()) & 0xFFF) - 1;
|
||||
count = (ctl & 0x70) >> 4;
|
||||
if (count != 0)
|
||||
count += 2;
|
||||
else
|
||||
count = file.ReadUInt8() + 10;
|
||||
Binary.CopyOverlapped (pixels, dst + offset, dst, count);
|
||||
}
|
||||
else
|
||||
{
|
||||
count = ctl & 0x3F;
|
||||
if (0 == count)
|
||||
count = 64 + file.ReadUInt8();
|
||||
count += 1;
|
||||
byte p = pixels[dst-1];
|
||||
for (int i = 0; i < count; ++i)
|
||||
pixels[dst+i] = p;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
count = ctl & 0x3F;
|
||||
if (0 == count)
|
||||
count = 64 + file.ReadUInt8();
|
||||
file.Read (pixels, dst, count);
|
||||
}
|
||||
x += count;
|
||||
dst += count;
|
||||
}
|
||||
}
|
||||
return ImageData.Create (info, PixelFormats.Indexed8, palette, pixels);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("xxxFormat.Write not implemented");
|
||||
}
|
||||
|
||||
internal static BitmapPalette ConvertPalette (byte[] palette_data)
|
||||
{
|
||||
const int colors = 0x100;
|
||||
var color_map = new Color[colors];
|
||||
int src = 0;
|
||||
for (int i = 0; i < colors; ++i)
|
||||
{
|
||||
color_map[i] = Color.FromRgb (palette_data[src+1], palette_data[src+2], palette_data[src]);
|
||||
src += 3;
|
||||
}
|
||||
return new BitmapPalette (color_map);
|
||||
}
|
||||
|
||||
internal static byte Decrypt (byte[] data, byte key = 0)
|
||||
{
|
||||
for (int i = 0; i < data.Length; ++i)
|
||||
{
|
||||
byte v = (byte)((data[i] - key) ^ 0xF3);
|
||||
data[i] = v;
|
||||
key += 7;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
@ -199,6 +199,9 @@
|
||||
<setting name="SJDatTitle" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="AFAEncodingCP" serializeAs="String">
|
||||
<value>932</value>
|
||||
</setting>
|
||||
</GameRes.Formats.Properties.Settings>
|
||||
</userSettings>
|
||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" /></startup>
|
||||
|
@ -40,7 +40,7 @@ namespace GameRes.Formats.Elf
|
||||
|
||||
public G24Format ()
|
||||
{
|
||||
Extensions = new string[] { "g24", "g16" };
|
||||
Extensions = new string[] { "g24", "g16", "g32" };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream input)
|
||||
@ -57,7 +57,8 @@ namespace GameRes.Formats.Elf
|
||||
Height = (uint)h,
|
||||
OffsetX = x,
|
||||
OffsetY = y,
|
||||
BPP = input.Name.HasExtension (".G16") ? 16 : 24
|
||||
BPP = input.Name.HasExtension (".G16") ? 16
|
||||
: input.Name.HasExtension (".G32") ? 32 : 24
|
||||
};
|
||||
}
|
||||
|
||||
@ -70,7 +71,9 @@ namespace GameRes.Formats.Elf
|
||||
{
|
||||
if (pixels.Length != reader.Read (pixels, 0, pixels.Length))
|
||||
throw new InvalidFormatException();
|
||||
var format = 24 == info.BPP ? PixelFormats.Bgr24 : PixelFormats.Bgr555;
|
||||
var format = 24 == info.BPP ? PixelFormats.Bgr24
|
||||
: 32 == info.BPP ? PixelFormats.Bgra32
|
||||
: PixelFormats.Bgr555;
|
||||
return ImageData.CreateFlipped (info, format, null, pixels, stride);
|
||||
}
|
||||
}
|
||||
|
@ -77,7 +77,13 @@ namespace GameRes.Formats.Rugp
|
||||
Size = node.Size
|
||||
};
|
||||
if (!dir.Any())
|
||||
return null;
|
||||
{
|
||||
var box_ref = nodes.FirstOrDefault (n => n.ClassName == "CBoxOcean");
|
||||
if (null == box_ref)
|
||||
return null;
|
||||
var box = reader.ReadObject (box_ref);
|
||||
nodes = reader.Arc.LoadArray.OfType<COceanNode>();
|
||||
}
|
||||
return new ArcFile (file, this, dir.ToList());
|
||||
}
|
||||
}
|
||||
@ -307,7 +313,7 @@ namespace GameRes.Formats.Rugp
|
||||
public int GetObjectSchema ()
|
||||
{
|
||||
int schema = m_objectSchema;
|
||||
m_objectSchema = -1;
|
||||
// m_objectSchema = -1;
|
||||
return schema;
|
||||
}
|
||||
|
||||
@ -329,9 +335,12 @@ namespace GameRes.Formats.Rugp
|
||||
|
||||
public CObject ReadObject (COceanNode node)
|
||||
{
|
||||
m_field_60 = false;
|
||||
var obj = CreateObject (node.Name);
|
||||
PopulateLoadArray();
|
||||
m_input.Position = ((long)node.Offset << m_shift);
|
||||
// if (node.Name != "CrelicUnitedGameProject")
|
||||
// m_input.Seek (3, SeekOrigin.Current);
|
||||
int f1 = ReadByte() & 3;
|
||||
int f2 = ReadByte();
|
||||
int f3 = ReadByte();
|
||||
@ -700,7 +709,7 @@ namespace GameRes.Formats.Rugp
|
||||
class_ref = LoadRuntimeClass (out schema);
|
||||
if (null == class_ref)
|
||||
throw new InvalidFormatException();
|
||||
// m_objectSchema = (int)schema;
|
||||
m_objectSchema = (int)schema;
|
||||
|
||||
m_LoadArray.Add (class_ref);
|
||||
}
|
||||
@ -957,6 +966,7 @@ namespace GameRes.Formats.Rugp
|
||||
{ "CrelicUnitedGameProject", new CObjectFactory<CrelicUnitedGameProject>() },
|
||||
{ "CStdb", new CObjectFactory<CStdb>() },
|
||||
{ "CObjectOcean", new CObjectFactory<CObjectOcean>() },
|
||||
{ "CBoxOcean", new CObjectFactory<CBoxOcean>() },
|
||||
};
|
||||
}
|
||||
|
||||
@ -1351,11 +1361,74 @@ namespace GameRes.Formats.Rugp
|
||||
|
||||
internal class CBoxOcean : CObject
|
||||
{
|
||||
public CObject field_10;
|
||||
public CObject field_10;
|
||||
public CObject field_14;
|
||||
public List<CObject> field_18 = new List<CObject>();
|
||||
public List<string> field_118 = new List<string>();
|
||||
public CObject field_198;
|
||||
public List<CObject> m_box_list = new List<CObject>();
|
||||
|
||||
static CObject static_cui = null;
|
||||
|
||||
public override void Deserialize (CRioArchive arc)
|
||||
{
|
||||
field_10 = arc.ReadRioReference ("CFrameBuffer");
|
||||
int schema = arc.GetObjectSchema();
|
||||
if (schema < 3)
|
||||
{
|
||||
for (int i = 0; i < 32; ++i)
|
||||
{
|
||||
var box = arc.ReadRioReference ("CBox");
|
||||
m_box_list.Add (box);
|
||||
}
|
||||
}
|
||||
if (schema >= 2)
|
||||
{
|
||||
field_14 = arc.ReadRioReference ("CSbm");
|
||||
if (schema < 6)
|
||||
{
|
||||
field_18.Add (arc.ReadRioReference ("CSbm"));
|
||||
}
|
||||
else
|
||||
{
|
||||
int ref_count = 15;
|
||||
int str_count = 0;
|
||||
if (schema >= 7)
|
||||
{
|
||||
ref_count = 32;
|
||||
str_count = 32;
|
||||
}
|
||||
for (int i = 0; i < ref_count; ++i)
|
||||
{
|
||||
field_18.Add (arc.ReadRioReference ("CSbm"));
|
||||
}
|
||||
for (int i = 0; i < str_count; ++i)
|
||||
{
|
||||
field_118.Add (arc.ReadString());
|
||||
}
|
||||
}
|
||||
field_198 = arc.ReadRioReference ("CUnitedMenu");
|
||||
if (schema >= 4)
|
||||
{
|
||||
ReadCui (arc);
|
||||
if (schema >= 5)
|
||||
{
|
||||
var cui = arc.ReadRioReference ("CUI");
|
||||
if (cui != static_cui)
|
||||
{
|
||||
ReadCui (arc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReadCui (CRioArchive arc)
|
||||
{
|
||||
while (arc.ReadByte() != 0)
|
||||
{
|
||||
var cui = arc.ReadRioReference ("CUI");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion ("1.0.16.40")]
|
||||
[assembly: AssemblyFileVersion ("1.0.16.40")]
|
||||
[assembly: AssemblyVersion ("1.0.16.42")]
|
||||
[assembly: AssemblyFileVersion ("1.0.16.42")]
|
||||
|
@ -35,6 +35,11 @@ namespace GameRes.Formats.RPGMaker
|
||||
public override uint Signature { get { return 0x4D475052; } } // 'RPGMV'
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public RpgmvoAudio ()
|
||||
{
|
||||
Extensions = new[] { "rpgmvo", "ogg_" };
|
||||
}
|
||||
|
||||
public override SoundInput TryOpen (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x14);
|
||||
|
@ -44,6 +44,11 @@ namespace GameRes.Formats.RPGMaker
|
||||
public override string Description { get { return "RPG Maker engine image format"; } }
|
||||
public override uint Signature { get { return 0x4D475052; } } // 'RPGMV'
|
||||
|
||||
public RpgmvpFormat ()
|
||||
{
|
||||
Extensions = new string[] { "rpgmvp", "png_" };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x14);
|
||||
@ -149,6 +154,8 @@ namespace GameRes.Formats.RPGMaker
|
||||
var dir_name = Path.GetDirectoryName (filename);
|
||||
yield return Path.Combine (dir_name, @"..\..\data\System.json");
|
||||
yield return Path.Combine (dir_name, @"..\..\..\www\data\System.json");
|
||||
yield return Path.Combine (dir_name, @"..\..\..\data\System.json");
|
||||
yield return Path.Combine (dir_name, @"..\..\..\..\data\System.json");
|
||||
yield return Path.Combine (dir_name, @"..\data\System.json");
|
||||
yield return Path.Combine (dir_name, @"data\System.json");
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ using System.Windows.Media;
|
||||
namespace GameRes
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
[ExportMetadata("Priority", 100)] // makes PNG first format in list
|
||||
public class PngFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "PNG"; } }
|
||||
|
@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion ("1.5.44.320")]
|
||||
[assembly: AssemblyFileVersion ("1.5.44.320")]
|
||||
[assembly: AssemblyVersion ("1.5.44.321")]
|
||||
[assembly: AssemblyFileVersion ("1.5.44.321")]
|
||||
|
@ -30,12 +30,51 @@ namespace GameRes
|
||||
{
|
||||
public override string Type { get { return "script"; } }
|
||||
|
||||
public abstract bool IsScript (IBinaryStream file);
|
||||
|
||||
public abstract Stream ConvertFrom (IBinaryStream file);
|
||||
public abstract Stream ConvertBack (IBinaryStream file);
|
||||
|
||||
public abstract ScriptData Read (string name, Stream file);
|
||||
public abstract void Write (Stream file, ScriptData script);
|
||||
|
||||
public static ScriptFormat FindFormat (IBinaryStream file)
|
||||
{
|
||||
foreach (var impl in FormatCatalog.Instance.FindFormats<ScriptFormat> (file.Name, file.Signature))
|
||||
{
|
||||
try
|
||||
{
|
||||
file.Position = 0;
|
||||
if (impl.IsScript (file))
|
||||
return impl;
|
||||
}
|
||||
catch (System.OperationCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class GenericScriptFormat : ScriptFormat
|
||||
{
|
||||
public override bool IsScript (IBinaryStream file)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override Stream ConvertFrom (IBinaryStream file)
|
||||
{
|
||||
return file.AsStream;
|
||||
}
|
||||
|
||||
public override Stream ConvertBack (IBinaryStream file)
|
||||
{
|
||||
return file.AsStream;
|
||||
}
|
||||
|
||||
public override ScriptData Read (string name, Stream file)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
|
@ -38,12 +38,23 @@ namespace GameRes.Formats.Airyu
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
static readonly uint[] ImageSizes = new[] { 0x96000u, 0x4B000u, 0x19000u };
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.Name.HasExtension (".chr"))
|
||||
return null;
|
||||
int count = (int)(file.MaxOffset / 0x96000);
|
||||
if (!IsSaneCount (count) || count * 0x96000 != file.MaxOffset)
|
||||
int count = 0;
|
||||
uint image_size = 1;
|
||||
for (int i = 0; i < ImageSizes.Length; ++i)
|
||||
{
|
||||
image_size = ImageSizes[i];
|
||||
count = (int)(file.MaxOffset / image_size);
|
||||
if (IsSaneCount (count) && count * image_size == file.MaxOffset)
|
||||
break;
|
||||
count = 0;
|
||||
}
|
||||
if (0 == count)
|
||||
return null;
|
||||
|
||||
uint offset = 0;
|
||||
@ -54,10 +65,10 @@ namespace GameRes.Formats.Airyu
|
||||
Name = i.ToString ("D5"),
|
||||
Type = "image",
|
||||
Offset = offset,
|
||||
Size = 0x96000,
|
||||
Size = image_size,
|
||||
};
|
||||
dir.Add (entry);
|
||||
offset += 0x96000;
|
||||
offset += image_size;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
@ -65,21 +76,32 @@ namespace GameRes.Formats.Airyu
|
||||
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
|
||||
{
|
||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
return new ChrImageDecoder (input);
|
||||
return new ChrImageDecoder (input, entry.Size);
|
||||
}
|
||||
}
|
||||
|
||||
internal class ChrImageDecoder : BinaryImageDecoder
|
||||
{
|
||||
public ChrImageDecoder (IBinaryStream input) : base (input)
|
||||
int m_image_size;
|
||||
int m_stride;
|
||||
|
||||
public ChrImageDecoder (IBinaryStream input, uint size) : base (input)
|
||||
{
|
||||
Info = new ImageMetaData { Width = 640, Height = 480, BPP = 16 };
|
||||
m_image_size = (int)size;
|
||||
switch (size)
|
||||
{
|
||||
case 0x19000: Info = new ImageMetaData { Width = 200, Height = 256, BPP = 16 }; break;
|
||||
case 0x4B000: Info = new ImageMetaData { Width = 320, Height = 480, BPP = 16 }; break;
|
||||
case 0x96000: Info = new ImageMetaData { Width = 640, Height = 480, BPP = 16 }; break;
|
||||
default: throw new InvalidFormatException ("Invalid image size.");
|
||||
}
|
||||
m_stride = Info.iWidth * 2;
|
||||
}
|
||||
|
||||
protected override ImageData GetImageData ()
|
||||
{
|
||||
var pixels = m_input.ReadBytes (0x96000);
|
||||
return ImageData.CreateFlipped (Info, PixelFormats.Bgr555, null, pixels, 640*2);
|
||||
var pixels = m_input.ReadBytes (m_image_size);
|
||||
return ImageData.CreateFlipped (Info, PixelFormats.Bgr555, null, pixels, m_stride);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
142
Legacy/BlackButterfly/ArcDAT.cs
Normal file
142
Legacy/BlackButterfly/ArcDAT.cs
Normal file
@ -0,0 +1,142 @@
|
||||
//! \file ArcDAT.cs
|
||||
//! \date 2022 Nov 09
|
||||
//! \brief Black Butterfly resource archive.
|
||||
//
|
||||
// Copyright (C) 2022 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using GameRes.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.BlackButterfly
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class DatOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "DAT/PITA"; } }
|
||||
public override string Description { get { return "Black Butterfly resource archive"; } }
|
||||
public override uint Signature { get { return 0x41544950; } } // 'PITA'
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (4);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
|
||||
uint index_pos = 0x10;
|
||||
uint next_offset = file.View.ReadUInt32 (index_pos);
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
index_pos += 4;
|
||||
var entry = new PackedEntry {
|
||||
Name = string.Format ("{0:D5}.bmp", i),
|
||||
Type = "image",
|
||||
Offset = next_offset,
|
||||
};
|
||||
next_offset = file.View.ReadUInt32 (index_pos);
|
||||
entry.Size = (uint)(next_offset - entry.Offset);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var pent = (PackedEntry)entry;
|
||||
if (!pent.IsPacked)
|
||||
{
|
||||
pent.IsPacked = true;
|
||||
pent.UnpackedSize = arc.File.View.ReadUInt32 (pent.Offset);
|
||||
}
|
||||
using (var input = arc.File.CreateStream (pent.Offset+4, pent.Size-4))
|
||||
{
|
||||
var data = new byte[pent.UnpackedSize];
|
||||
Unpack (input, data);
|
||||
return new BinMemoryStream (data, entry.Name);
|
||||
}
|
||||
}
|
||||
|
||||
void Unpack (IBinaryStream input, byte[] output)
|
||||
{
|
||||
int dst = 0;
|
||||
while (input.PeekByte() != -1)
|
||||
{
|
||||
byte ctl = input.ReadUInt8();
|
||||
if (0x7F == ctl)
|
||||
{
|
||||
if (input.PeekByte() == 0xFF)
|
||||
break;
|
||||
}
|
||||
int count;
|
||||
if (ctl <= 0x7F)
|
||||
{
|
||||
count = (ctl >> 2) + 2;
|
||||
int offset = (ctl & 3) << 8 | input.ReadUInt8();
|
||||
offset = (offset ^ 0x3FF) + 1;
|
||||
Binary.CopyOverlapped (output, dst - offset, dst, count);
|
||||
dst += count;
|
||||
}
|
||||
else if (ctl > 0xFE)
|
||||
{
|
||||
count = input.ReadUInt8() + 32;
|
||||
while (count --> 0)
|
||||
output[dst++] = 0;
|
||||
}
|
||||
else if (ctl > 0xDF)
|
||||
{
|
||||
count = (ctl & 0x1F) + 1;
|
||||
while (count --> 0)
|
||||
output[dst++] = 0;
|
||||
}
|
||||
else if (ctl > 0xBF)
|
||||
{
|
||||
count = (ctl & 0x1F) + 2;
|
||||
byte fill = input.ReadUInt8();
|
||||
while (count --> 0)
|
||||
output[dst++] = fill;
|
||||
}
|
||||
else if (ctl > 0x9F)
|
||||
{
|
||||
count = (ctl & 0x1F) + 1;
|
||||
while (count --> 0)
|
||||
{
|
||||
output[dst++] = 0;
|
||||
output[dst++] = input.ReadUInt8();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
count = (ctl & 0x1F) + 1;
|
||||
input.Read (output, dst, count);
|
||||
dst += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
204
Legacy/CottonClub/ImageLMG.cs
Normal file
204
Legacy/CottonClub/ImageLMG.cs
Normal file
@ -0,0 +1,204 @@
|
||||
//! \file ImageLMG.cs
|
||||
//! \date 2023 Aug 07
|
||||
//! \brief Cotton Club encrypted image.
|
||||
//
|
||||
// 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.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
// [071231][Cotton Club] Kureuta
|
||||
|
||||
namespace GameRes.Formats.CottonClub
|
||||
{
|
||||
public class LmgMetaData : ImageMetaData
|
||||
{
|
||||
public byte Method;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class LmgFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "LMG"; } }
|
||||
public override string Description { get { return "Cotton Club encrypted image"; } }
|
||||
public override uint Signature { get { return 0x03474D4C; } } // 'LMG'
|
||||
|
||||
public LmgFormat ()
|
||||
{
|
||||
Signatures = new uint[] { 0x03474D4C, 0x02474D4C, 0x01474D4C };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (12);
|
||||
return new LmgMetaData {
|
||||
Width = header.ToUInt32 (4),
|
||||
Height = header.ToUInt32 (8),
|
||||
BPP = header[3] == 2 ? 32 : 24,
|
||||
Method = header[3],
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var meta = (LmgMetaData)info;
|
||||
var data = new byte[file.Length - 12];
|
||||
file.Position = 12;
|
||||
file.Read (data, 0, data.Length);
|
||||
DecryptData (data, file.Name);
|
||||
if (3 == meta.Method)
|
||||
{
|
||||
using (var input = new BinMemoryStream (data))
|
||||
{
|
||||
var decoder = new JpegBitmapDecoder (input, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||
var frame = decoder.Frames[0];
|
||||
frame.Freeze();
|
||||
return new ImageData (frame, info);
|
||||
}
|
||||
}
|
||||
else if (2 == meta.Method)
|
||||
{
|
||||
var reader = new LmgReader (info, data);
|
||||
data = reader.Unpack();
|
||||
return ImageData.Create (info, PixelFormats.Bgra32, null, data, reader.Stride);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ImageData.Create (info, PixelFormats.Bgr24, null, data, info.iWidth * 3);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("LmgFormat.Write not implemented");
|
||||
}
|
||||
|
||||
internal void DecryptData (byte[] data, string filename)
|
||||
{
|
||||
filename = Path.GetFileName (filename).ToLowerInvariant();
|
||||
byte key = 0;
|
||||
for (int i = 0; i < filename.Length; ++i)
|
||||
{
|
||||
key ^= (byte)filename[i];
|
||||
}
|
||||
for (int i = 0; i < data.Length; ++i)
|
||||
{
|
||||
byte x = data[i];
|
||||
data[i] ^= key;
|
||||
key = x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class LmgReader
|
||||
{
|
||||
byte[] m_data;
|
||||
byte[] m_output;
|
||||
int m_stride;
|
||||
|
||||
public int Stride { get { return m_stride; } }
|
||||
|
||||
public LmgReader (ImageMetaData info, byte[] data)
|
||||
{
|
||||
m_data = data;
|
||||
m_stride = info.iWidth * 4;
|
||||
m_output = new byte[m_stride * info.iHeight];
|
||||
}
|
||||
|
||||
int m_src;
|
||||
int m_dst;
|
||||
|
||||
public byte[] Unpack ()
|
||||
{
|
||||
m_src = 0;
|
||||
m_dst = 0;
|
||||
while (m_src+1 < m_data.Length)
|
||||
{
|
||||
byte alpha = m_data[m_src++];
|
||||
if (0xFF == alpha)
|
||||
{
|
||||
int length = GetLength16();
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
m_output[m_dst++] = m_data[m_src++];
|
||||
m_output[m_dst++] = m_data[m_src++];
|
||||
m_output[m_dst++] = m_data[m_src++];
|
||||
m_output[m_dst++] = 0xFF;
|
||||
}
|
||||
}
|
||||
else if (0 == alpha)
|
||||
{
|
||||
int length = GetLength16();
|
||||
m_dst += length * 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
ReadARGB (alpha);
|
||||
}
|
||||
}
|
||||
return m_output;
|
||||
}
|
||||
|
||||
void ReadARGB (byte alpha)
|
||||
{
|
||||
int length = GetLength8();
|
||||
m_output[m_dst++] = m_data[m_src++];
|
||||
m_output[m_dst++] = m_data[m_src++];
|
||||
m_output[m_dst++] = m_data[m_src++];
|
||||
m_output[m_dst++] = alpha;
|
||||
while (--length > 0)
|
||||
{
|
||||
alpha = m_data[m_src++];
|
||||
m_output[m_dst++] = m_data[m_src++];
|
||||
m_output[m_dst++] = m_data[m_src++];
|
||||
m_output[m_dst++] = m_data[m_src++];
|
||||
m_output[m_dst++] = alpha;
|
||||
}
|
||||
}
|
||||
|
||||
int GetLength8 ()
|
||||
{
|
||||
int i = 0;
|
||||
while (m_data[m_src] == 0)
|
||||
{
|
||||
i += 0xFF;
|
||||
++m_src;
|
||||
}
|
||||
return i + m_data[m_src++];
|
||||
}
|
||||
|
||||
int GetLength16 ()
|
||||
{
|
||||
int i = 0;
|
||||
while (m_data[m_src] == 0 && m_data[m_src+1] == 0)
|
||||
{
|
||||
i += 0xFFFF;
|
||||
m_src += 2;
|
||||
}
|
||||
i += m_data.ToUInt16 (m_src);
|
||||
m_src += 2;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
@ -28,6 +28,7 @@ using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
// [000623][Marimo] Setsunai
|
||||
// [010316][Evolution] Bukkake Tenshi Silky & Milky
|
||||
|
||||
namespace GameRes.Formats.Dice
|
||||
{
|
||||
|
@ -30,6 +30,7 @@ using GameRes.Compression;
|
||||
|
||||
// [030725][Digital Monkey] Kono Sora ga Tsuieru Toki ni
|
||||
// [041112][Yumesta] Seikon ~Kiba to Nie to Kyouki no Yakata~
|
||||
// [050325][Supple Entertainment] Dark: Shitsuraku no Shimai Ningyou
|
||||
|
||||
namespace GameRes.Formats.DigitalMonkey
|
||||
{
|
||||
|
2090
Legacy/Giga/ArcALL.cs
Normal file
2090
Legacy/Giga/ArcALL.cs
Normal file
File diff suppressed because it is too large
Load Diff
73
Legacy/Herb/ArcPAK.cs
Normal file
73
Legacy/Herb/ArcPAK.cs
Normal file
@ -0,0 +1,73 @@
|
||||
//! \file ArcPAK.cs
|
||||
//! \date 2023 Aug 16
|
||||
//! \brief Herb Soft 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.IO;
|
||||
|
||||
namespace GameRes.Formats.Herb
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class PakOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "PAK/HERB"; } }
|
||||
public override string Description { get { return "Herb Soft resource archive"; } }
|
||||
public override uint Signature { get { return 0x2E; } }
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public PakOpener ()
|
||||
{
|
||||
ContainedFormats = new[] { "GRP/HERB", "WAV", "TXT" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
long base_offset = 0x20000;
|
||||
if (file.MaxOffset <= base_offset || !file.View.AsciiEqual (0x40, "..\0"))
|
||||
return null;
|
||||
long index_pos = 0x80;
|
||||
var dir = new List<Entry>();
|
||||
while (index_pos < base_offset && file.View.ReadByte (index_pos) != 0)
|
||||
{
|
||||
var name = file.View.ReadString (index_pos, 0x30);
|
||||
var entry = Create<Entry> (name);
|
||||
entry.Offset = file.View.ReadUInt32 (index_pos + 0x30) + base_offset;
|
||||
entry.Size = file.View.ReadUInt32 (index_pos + 0x38);
|
||||
if (entry.Size != 0)
|
||||
{
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
}
|
||||
index_pos += 0x40;
|
||||
}
|
||||
if (dir.Count == 0)
|
||||
return null;
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
}
|
91
Legacy/Herb/ImageGRP.cs
Normal file
91
Legacy/Herb/ImageGRP.cs
Normal file
@ -0,0 +1,91 @@
|
||||
//! \file ImageGRP.cs
|
||||
//! \date 2023 Aug 16
|
||||
//! \brief Herb Soft compressed image format.
|
||||
//
|
||||
// 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.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace GameRes.Formats.Herb
|
||||
{
|
||||
internal class GrpMetaData : ImageMetaData
|
||||
{
|
||||
public int Stride;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class GrpFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "GRP/HERB"; } }
|
||||
public override string Description { get { return "Herb Soft image format"; } }
|
||||
public override uint Signature { get { return 0x08; } }
|
||||
|
||||
public GrpFormat ()
|
||||
{
|
||||
Signatures = new[] { 0x20u, 0x18u, 0x08u };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x28);
|
||||
if (header.ToInt32 (4) != 0 || header.ToInt32 (8) != 1)
|
||||
return null;
|
||||
return new GrpMetaData {
|
||||
Width = header.ToUInt32 (0x20),
|
||||
Height = header.ToUInt32 (0x24),
|
||||
BPP = header[0] == 0x08 ? 8 : header[0] == 0x18 ? 16 : 24,
|
||||
Stride = header.ToInt32 (0x0C),
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var meta = (GrpMetaData)info;
|
||||
BitmapPalette palette = null;
|
||||
PixelFormat format = 8 == info.BPP ? PixelFormats.Indexed8
|
||||
: 16 == info.BPP ? PixelFormats.Bgr555
|
||||
: PixelFormats.Bgr24;
|
||||
if (8 == info.BPP)
|
||||
{
|
||||
file.Position = 0x28;
|
||||
palette = ReadPalette (file.AsStream, 0x100, PaletteFormat.RgbX);
|
||||
}
|
||||
int stride = meta.Stride;
|
||||
var pixels = new byte[stride * info.iHeight];
|
||||
file.Position = 0x428;
|
||||
using (var input = new ZLibStream (file.AsStream, CompressionMode.Decompress, true))
|
||||
{
|
||||
input.Read (pixels, 0, pixels.Length);
|
||||
}
|
||||
return ImageData.Create (info, format, palette, pixels, stride);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("GrpFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
}
|
71
Legacy/HyperWorks/ArcPAK.cs
Normal file
71
Legacy/HyperWorks/ArcPAK.cs
Normal file
@ -0,0 +1,71 @@
|
||||
//! \file ArcPAK.cs
|
||||
//! \date 2022 May 28
|
||||
//! \brief HyperWorks resource archive.
|
||||
//
|
||||
// Copyright (C) 2022 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.HyperWorks
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class PakOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "PAK/ACE"; } }
|
||||
public override string Description { get { return "HyperWorks resource archive"; } }
|
||||
public override uint Signature { get { return 0x4B434150; } } // 'PACK'
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int index_size = file.View.ReadInt32 (4);
|
||||
if (index_size >= file.MaxOffset - 4)
|
||||
return null;
|
||||
int count = index_size / 0x18;
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
|
||||
uint pos = 8;
|
||||
file.View.Reserve (pos, (uint)index_size);
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
uint name_length = file.View.ReadByte (pos+8);
|
||||
if (name_length > 15)
|
||||
return null;
|
||||
var name = file.View.ReadString (pos+9, name_length);
|
||||
var entry = Create<Entry> (name);
|
||||
entry.Offset = file.View.ReadUInt32 (pos);
|
||||
entry.Size = file.View.ReadUInt32 (pos+4);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
pos += 0x18;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
}
|
@ -23,10 +23,14 @@
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using GameRes.Utility;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
|
||||
// [980626][Love Gun] ACE OF SPADES 2
|
||||
|
||||
namespace GameRes.Formats.HyperWorks
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
@ -51,7 +55,8 @@ namespace GameRes.Formats.HyperWorks
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
return ImageData.Create (info, format, palette, pixels);
|
||||
var reader = new I24Decoder (file, info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
@ -59,4 +64,439 @@ namespace GameRes.Formats.HyperWorks
|
||||
throw new System.NotImplementedException ("I24Format.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal class I24Decoder
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
ImageMetaData m_info;
|
||||
|
||||
public I24Decoder (IBinaryStream input, ImageMetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
}
|
||||
|
||||
static readonly short[] shiftTable = new short[] {
|
||||
-1, 0, 0, 1, 1, 1, -1, 1, 2, 1, -2, 1, -2, 0, 0, 2, 1, 2, -1, 2, -3, 0
|
||||
};
|
||||
|
||||
bool cacheEmpty = true;
|
||||
int byteCount = 0;
|
||||
int bits = 0;
|
||||
int bitCount = 0;
|
||||
|
||||
class Node
|
||||
{
|
||||
public Node next;
|
||||
public int depth;
|
||||
public int token;
|
||||
}
|
||||
|
||||
struct DictRec
|
||||
{
|
||||
public Link link;
|
||||
public int bitSize;
|
||||
public int token;
|
||||
}
|
||||
|
||||
class Link
|
||||
{
|
||||
public Link[] children = new Link[2];
|
||||
public int token;
|
||||
}
|
||||
|
||||
DictRec[] dTable_1 = new DictRec[256];
|
||||
DictRec[] dTable_2 = new DictRec[256];
|
||||
DictRec[] dTable_3 = new DictRec[256];
|
||||
|
||||
Link[] tBuffer_1 = InitNodeList<Link> (684);
|
||||
Link[] tBuffer_2 = InitNodeList<Link> (22);
|
||||
Link[] tBuffer_3 = InitNodeList<Link> (502);
|
||||
|
||||
Node[] bTable_1 = InitNodeList<Node> (342);
|
||||
Node[] bTable_2 = InitNodeList<Node> (11);
|
||||
Node[] bTable_3 = InitNodeList<Node> (251);
|
||||
|
||||
static T[] InitNodeList<T>(int count) where T : new()
|
||||
{
|
||||
var list = new T[count];
|
||||
for (int i = 0; i < count; ++i)
|
||||
list[i] = new T();
|
||||
return list;
|
||||
}
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
m_input.Position = 0x18;
|
||||
int stride = m_info.iWidth * 4;
|
||||
var pixels = new byte[stride * m_info.iHeight];
|
||||
byte[][] line_buffer = new[] { new byte[stride], new byte[stride], new byte[stride] };
|
||||
int dst = 0;
|
||||
var shift_table = shiftTable.Clone() as short[];
|
||||
for (int y = 0; y < m_info.iHeight; ++y)
|
||||
{
|
||||
var line = line_buffer[2];
|
||||
line_buffer[2] = line_buffer[1];
|
||||
line_buffer[1] = line_buffer[0];
|
||||
line_buffer[0] = line;
|
||||
int x = 0;
|
||||
int p = 0;
|
||||
while (x < m_info.iWidth)
|
||||
{
|
||||
if (byteCount-- == 0)
|
||||
{
|
||||
if (cacheEmpty)
|
||||
{
|
||||
cacheEmpty = false;
|
||||
int val = ReadUInt8() << 8;
|
||||
val |= ReadUInt8();
|
||||
bits = val;
|
||||
bitCount = 8;
|
||||
}
|
||||
InitTree (bTable_1, 342);
|
||||
InitTree (bTable_2, 11);
|
||||
InitTree (bTable_3, 251);
|
||||
|
||||
RebuildTree(bTable_1, dTable_1, tBuffer_1, 342);
|
||||
RebuildTree(bTable_2, dTable_2, tBuffer_2, 11);
|
||||
RebuildTree(bTable_3, dTable_3, tBuffer_3, 251);
|
||||
byteCount = 0x3FFF;
|
||||
}
|
||||
int color_token = GetToken (dTable_1);
|
||||
int shift_token = GetToken (dTable_2);
|
||||
|
||||
int shift_idx = 2 * shift_token;
|
||||
short s1 = shift_table[shift_idx];
|
||||
short s2 = shift_table[shift_idx + 1];
|
||||
if (shift_token != 0)
|
||||
{
|
||||
while (shift_token --> 0)
|
||||
{
|
||||
shift_table[shift_idx] = shift_table[shift_idx - 2];
|
||||
shift_table[shift_idx+1] = shift_table[shift_idx - 1];
|
||||
shift_idx -= 2;
|
||||
}
|
||||
shift_table[0] = s1;
|
||||
shift_table[1] = s2;
|
||||
}
|
||||
int src = 4 * (x + s1);
|
||||
if (color_token >= 216)
|
||||
{
|
||||
int count = color_token - 214;
|
||||
x += count;
|
||||
while (count --> 0)
|
||||
{
|
||||
line[p++] = line_buffer[s2][src++];
|
||||
line[p++] = line_buffer[s2][src++];
|
||||
line[p++] = line_buffer[s2][src++];
|
||||
line[p++] = line_buffer[s2][src++];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sbyte r = shift_R[color_token];
|
||||
if (r == -3)
|
||||
r = (sbyte)(GetToken (dTable_3) + 3);
|
||||
line[p+2] = (byte)(line_buffer[s2][src+2] - r);
|
||||
|
||||
sbyte g = shift_G[color_token];
|
||||
if (g == -3)
|
||||
g = (sbyte)(GetToken(dTable_3) + 3);
|
||||
line[p+1] = (byte)(line_buffer[s2][src+1] - g);
|
||||
|
||||
sbyte b = shift_B[color_token];
|
||||
if (b == -3)
|
||||
b = (sbyte)(GetToken(dTable_3) + 3);
|
||||
line[p] = (byte)(line_buffer[s2][src] - b);
|
||||
line[p+3] = 0;
|
||||
p += 4;
|
||||
++x;
|
||||
}
|
||||
}
|
||||
Buffer.BlockCopy (line_buffer[0], 0, pixels, dst, stride);
|
||||
dst += stride;
|
||||
}
|
||||
return ImageData.Create (m_info, PixelFormats.Bgr32, null, pixels, stride);
|
||||
}
|
||||
|
||||
private void InitTree (Node[] tree, int count) // sub_408FB0
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
tree[i].next = null;
|
||||
tree[i].depth = 0;
|
||||
tree[i].token = i;
|
||||
}
|
||||
int length = GetBitLength();
|
||||
if (length <= 1)
|
||||
return;
|
||||
length -= 1;
|
||||
int fieldWidth = GetBits (3);
|
||||
int tIdx = 0;
|
||||
while (length > 0)
|
||||
{
|
||||
if (GetNextBit() != 0)
|
||||
{
|
||||
tree[tIdx++].depth = GetBits (fieldWidth);
|
||||
--length;
|
||||
}
|
||||
else
|
||||
{
|
||||
int step = GetBitLength();
|
||||
if (0 == step)
|
||||
tIdx++;
|
||||
else
|
||||
tIdx += step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RebuildTree (Node[] tree, DictRec[] dict, Link[] links, int count)
|
||||
{
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
dict[i].token = 0;
|
||||
dict[i].link = null;
|
||||
}
|
||||
int next_idx = 0; // var node = tree;
|
||||
count -= 1;
|
||||
while (0 == tree[next_idx].depth)
|
||||
{
|
||||
if (--count <= 0)
|
||||
break;
|
||||
++next_idx;
|
||||
}
|
||||
if (0 == count)
|
||||
return;
|
||||
|
||||
var node = tree[next_idx++];
|
||||
while (count --> 0)
|
||||
{
|
||||
int depth = tree[next_idx].depth;
|
||||
if (depth != 0)
|
||||
{
|
||||
if (node != null)
|
||||
{
|
||||
if (node.depth <= depth)
|
||||
{
|
||||
var prev = node;
|
||||
var ptr = node.next;
|
||||
while (ptr != null)
|
||||
{
|
||||
if (ptr.depth > depth)
|
||||
break;
|
||||
prev = ptr;
|
||||
ptr = ptr.next;
|
||||
}
|
||||
prev.next = tree[next_idx];
|
||||
tree[next_idx].next = ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
tree[next_idx].next = node;
|
||||
node = tree[next_idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
++next_idx;
|
||||
}
|
||||
|
||||
int bit_size = 0;
|
||||
int t3_i = 0;
|
||||
int dict_idx = 0;
|
||||
while (node != null)
|
||||
{
|
||||
if (node.depth > bit_size)
|
||||
{
|
||||
dict_idx <<= node.depth - bit_size;
|
||||
bit_size = node.depth;
|
||||
}
|
||||
if (bit_size >= 8)
|
||||
{
|
||||
if (bit_size == 8)
|
||||
{
|
||||
dict[dict_idx].bitSize = 8;
|
||||
dict[dict_idx].token = node.token;
|
||||
dict[dict_idx].link = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
int d1 = bit_size - 8;
|
||||
int d2 = dict_idx >> d1;
|
||||
int d3 = dict_idx << (32 - (bit_size - 8));
|
||||
dict[d2].bitSize = 0;
|
||||
var ptr_t2 = d2; // &dict[d2];
|
||||
var link = dict[ptr_t2].link;
|
||||
if (null == link)
|
||||
{
|
||||
var t3_ptr = links[t3_i];
|
||||
t3_ptr.children[0] = null;
|
||||
t3_ptr.children[1] = null;
|
||||
t3_ptr.token = 0;
|
||||
link = t3_ptr;
|
||||
dict[ptr_t2].link = t3_ptr;
|
||||
t3_i++;
|
||||
}
|
||||
while (d1 --> 0)
|
||||
{
|
||||
int v26 = (d3 >> 31) & 1;
|
||||
d3 <<= 1;
|
||||
if (null == link.children[v26])
|
||||
{
|
||||
var t3_ptr = links[t3_i];
|
||||
t3_ptr.children[0] = null;
|
||||
t3_ptr.children[1] = null;
|
||||
t3_ptr.token = 0;
|
||||
link.children[v26] = t3_ptr;
|
||||
t3_i++;
|
||||
}
|
||||
link = link.children[v26];
|
||||
}
|
||||
link.token = node.token;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int d1 = dict_idx << (8 - bit_size);
|
||||
int d2 = 1 << (8 - bit_size);
|
||||
while (d2 --> 0)
|
||||
{
|
||||
dict[d1].bitSize = bit_size;
|
||||
dict[d1].token = node.token;
|
||||
d1++;
|
||||
}
|
||||
}
|
||||
++dict_idx;
|
||||
node = node.next;
|
||||
}
|
||||
}
|
||||
|
||||
int GetToken (DictRec[] table)
|
||||
{
|
||||
var table_ptr = table[(bits >> 8) & 0xFF];
|
||||
int count = table_ptr.bitSize;
|
||||
if (count != 0)
|
||||
{
|
||||
if (count >= bitCount)
|
||||
{
|
||||
bits <<= bitCount;
|
||||
count -= bitCount;
|
||||
bits |= ReadUInt8();
|
||||
bitCount = 8;
|
||||
}
|
||||
bits <<= count;
|
||||
bitCount -= count;
|
||||
return table_ptr.token;
|
||||
}
|
||||
else
|
||||
{
|
||||
bits = ReadUInt8() | bits << bitCount;
|
||||
bits <<= 8 - bitCount;
|
||||
Link link = table_ptr.link;
|
||||
do
|
||||
{
|
||||
int path = GetNextBit() & 1;
|
||||
link = link.children[path];
|
||||
if (null == link)
|
||||
throw new InvalidFormatException ("Invalid tree path");
|
||||
}
|
||||
while (link.children[0] != null);
|
||||
return link.token;
|
||||
}
|
||||
}
|
||||
|
||||
private byte ReadUInt8()
|
||||
{
|
||||
return (byte)m_input.ReadByte();
|
||||
}
|
||||
|
||||
private int GetNextBit ()
|
||||
{
|
||||
bits <<= 1;
|
||||
if (0 == --bitCount)
|
||||
{
|
||||
bits |= ReadUInt8();
|
||||
bitCount = 8;
|
||||
}
|
||||
return (bits >> 16) & 1;
|
||||
}
|
||||
|
||||
private int GetBitLength ()
|
||||
{
|
||||
if (GetNextBit() != 0)
|
||||
return 0;
|
||||
int i = 0;
|
||||
do
|
||||
{
|
||||
++i;
|
||||
}
|
||||
while (0 == GetNextBit());
|
||||
return (1 << i) | GetBits (i);
|
||||
}
|
||||
|
||||
private int GetBits (int count)
|
||||
{
|
||||
int n = count;
|
||||
if (count >= bitCount)
|
||||
{
|
||||
bits <<= bitCount;
|
||||
n = count - bitCount;
|
||||
bits |= ReadUInt8();
|
||||
bitCount = 8;
|
||||
}
|
||||
if (n >= 8)
|
||||
{
|
||||
bits <<= 8;
|
||||
bits |= ReadUInt8();
|
||||
n -= 8;
|
||||
}
|
||||
bits <<= n;
|
||||
bitCount -= n;
|
||||
return (bits >> 16) & bitMask[count];
|
||||
}
|
||||
|
||||
static readonly int[] bitMask = new[] {
|
||||
0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F,
|
||||
0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF,
|
||||
0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF,
|
||||
0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF
|
||||
};
|
||||
|
||||
static readonly sbyte[] shift_R = new sbyte[] {
|
||||
-3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
|
||||
-3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2,
|
||||
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
|
||||
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
};
|
||||
static readonly sbyte[] shift_G = new sbyte[] {
|
||||
-3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0,
|
||||
0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, -3, -3, -3, -3, -3, -3, -2, -2, -2,
|
||||
-2, -2, -2, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2,
|
||||
2, 2, 2, 2, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1,
|
||||
-1, -1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, -3, -3, -3, -3,
|
||||
-3, -3, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 1,
|
||||
1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2,
|
||||
-2, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
|
||||
2, 2, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1,
|
||||
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2,
|
||||
};
|
||||
static readonly sbyte[] shift_B = new sbyte[] {
|
||||
-3, -2, -1, 0, 1, 2, -3, -2, -1, 0, 1, 2, -3, -2, -1, 0, 1, 2, -3, -2, -1, 0, 1, 2,
|
||||
-3, -2, -1, 0, 1, 2, -3, -2, -1, 0, 1, 2, -3, -2, -1, 0, 1, 2, -3, -2, -1, 0,
|
||||
1, 2, -3, -2, -1, 0, 1, 2, -3, -2, -1, 0, 1, 2, -3, -2, -1, 0, 1, 2, -3, -2,
|
||||
-1, 0, 1, 2, -3, -2, -1, 0, 1, 2, -3, -2, -1, 0, 1, 2, -3, -2, -1, 0, 1, 2,
|
||||
-3, -2, -1, 0, 1, 2, -3, -2, -1, 0, 1, 2, -3, -2, -1, 0, 1, 2, -3, -2, -1, 0,
|
||||
1, 2, -3, -2, -1, 0, 1, 2, -3, -2, -1, 0, 1, 2, -3, -2, -1, 0, 1, 2, -3, -2,
|
||||
-1, 0, 1, 2, -3, -2, -1, 0, 1, 2, -3, -2, -1, 0, 1, 2, -3, -2, -1, 0, 1, 2,
|
||||
-3, -2, -1, 0, 1, 2, -3, -2, -1, 0, 1, 2, -3, -2, -1, 0, 1, 2, -3, -2, -1, 0,
|
||||
1, 2, -3, -2, -1, 0, 1, 2, -3, -2, -1, 0, 1, 2, -3, -2, -1, 0, 1, 2, -3, -2,
|
||||
-1, 0, 1, 2, -3, -2, -1, 0, 1, 2, -3, -2, -1, 0, 1, 2,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
133
Legacy/James/ImageJMG.cs
Normal file
133
Legacy/James/ImageJMG.cs
Normal file
@ -0,0 +1,133 @@
|
||||
//! \file ImageJMG.cs
|
||||
//! \date 2022 Jun 11
|
||||
//! \brief JAMES engine encrypted image.
|
||||
//
|
||||
// Copyright (C) 2022 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using GameRes.Utility;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
// [981105][Berserker] Situation
|
||||
// [991109][Berserker] Situation 2
|
||||
|
||||
namespace GameRes.Formats.James
|
||||
{
|
||||
public enum Obfuscation
|
||||
{
|
||||
None,
|
||||
RotateWords,
|
||||
ReverseBits
|
||||
}
|
||||
|
||||
public class JmgMetaData : ImageMetaData
|
||||
{
|
||||
public Obfuscation Method;
|
||||
public ImageMetaData BmpInfo;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class JmgFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "JMG"; } }
|
||||
public override string Description { get { return "JAMES engine obfuscated bitmap"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (2);
|
||||
Obfuscation method;
|
||||
if (header[0] == 0xD4 && header[1] == 0x24)
|
||||
method = Obfuscation.RotateWords;
|
||||
else if (header[0] == 0xB2 && header[1] == 0x42)
|
||||
method = Obfuscation.ReverseBits;
|
||||
else
|
||||
return null;
|
||||
using (var input = OpenAsBitmap (file, 0x40, method))
|
||||
{
|
||||
var info = Bmp.ReadMetaData (input);
|
||||
if (null == info)
|
||||
return null;
|
||||
return new JmgMetaData
|
||||
{
|
||||
Width = info.Width,
|
||||
Height = info.Height,
|
||||
BPP = info.BPP,
|
||||
Method = method,
|
||||
BmpInfo = info,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
IBinaryStream OpenAsBitmap (IBinaryStream input, int length, Obfuscation method)
|
||||
{
|
||||
input.Position = 0;
|
||||
var data = input.ReadBytes (length);
|
||||
Deobfuscate (data, 0, length, method);
|
||||
return new BinMemoryStream (data, input.Name);
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var meta = (JmgMetaData)info;
|
||||
using (var bmp = OpenAsBitmap (file, (int)file.Length, meta.Method))
|
||||
return Bmp.Read (bmp, meta.BmpInfo);
|
||||
}
|
||||
|
||||
void Deobfuscate (byte[] data, int pos, int length, Obfuscation method)
|
||||
{
|
||||
switch (method)
|
||||
{
|
||||
case Obfuscation.RotateWords: RotateWords (data, pos, length); break;
|
||||
case Obfuscation.ReverseBits: ReverseBits (data, pos, length); break;
|
||||
}
|
||||
}
|
||||
|
||||
void RotateWords (byte[] data, int pos, int length)
|
||||
{
|
||||
for (int i = 0; i < length; i += 2)
|
||||
{
|
||||
ushort w = LittleEndian.ToUInt16 (data, pos+i);
|
||||
w = (ushort)(w >> 12 | w << 4);
|
||||
LittleEndian.Pack (w, data, pos+i);
|
||||
}
|
||||
}
|
||||
|
||||
void ReverseBits (byte[] data, int pos, int length)
|
||||
{
|
||||
for (int i = 0; i < length; i += 2)
|
||||
{
|
||||
int v = LittleEndian.ToUInt16 (data, pos+i);
|
||||
v = (v & 0xAAAA) >> 1 | (v & 0x5555) << 1;
|
||||
v = (v & 0xCCCC) >> 2 | (v & 0x3333) << 2;
|
||||
v = (v & 0xF0F0) >> 4 | (v & 0x0F0F) << 4;
|
||||
v = (v & 0xFF00) >> 8 | (v & 0x00FF) << 8;
|
||||
LittleEndian.Pack ((ushort)v, data, pos+i);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("JmgFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user