Compare commits

...

19 Commits

Author SHA1 Message Date
Crsky
f63834c4bf Merge pull request #66 from mos9527/master
Add Support "The Song Of Saya (Steam)"
2024-12-23 16:17:36 +08:00
Crsky
8f96a1bccd Merge pull request #67 from Manicsteiner/feat_pacf
add support of F Fanatic PC version archive
2024-12-22 09:47:52 +08:00
ManicSteiner
f9def97321 add support of F Fanatic PC version archive 2024-12-21 22:49:01 +08:00
mos9527
301b795de6 Add Support "The Song Of Saya (Steam)" 2024-12-21 11:25:19 +08:00
Crsky
cd8fcea992 Merge pull request #65 from YeLikesss/master
[kirikiri] 夢幻のティル・ナ・ノーグ
2024-12-20 17:21:01 +08:00
YeLike
ddbeddd862 [kirikiri] 夢幻のティル・ナ・ノーグ 2024-12-20 16:04:12 +08:00
Crsky
82d44aeae2 Merge pull request #62 from YeLikesss/master
[Musica] Legacy Support(Wind a breath of heart Series 2002 Ver.)
2024-12-12 19:28:27 +08:00
YeLike
a7021686e9 [Musica] Legacy Game Support 2024-12-12 16:17:54 +08:00
Crsky
cdd4e0161c Merge pull request #61 from Manicsteiner/feat_pacps2
Add support Digital Works PS2 PAC
2024-12-09 13:58:56 +08:00
ManicSteiner
8424d75eab Add support Digital Works PS2 PAC 2024-12-09 10:58:11 +08:00
Crsky
9cb5ec89b3 Merge pull request #59 from YeLikesss/master
[Musica] Fix SqzFile Parser
2024-12-03 19:28:27 +08:00
YeLike
ac063d7eb5 [Musica] Fix SqzFile Parser 2024-12-03 18:19:05 +08:00
Crsky
7210ff5ff3 Merge pull request #53 from Dir-A/master
(Valkyria): support "MICOCG02"
2024-10-01 13:09:55 +08:00
Dir-A
ccc7c8032b (Valkyria): support "MICOCG02"
tested game: https://vndb.org/v48766
2024-10-01 06:49:25 +08:00
Crsky
499ac02c64 Add support "Hinadori no Ochiru Oto" 2024-09-24 15:48:37 +08:00
Crsky
a20aa9434c Merge pull request #50 from Dir-A/master
Update ArcISA.cs
2024-09-24 15:44:44 +08:00
Dir-A
b967d505ec Update ImageSeraph.cs
fix image format check
2024-09-24 04:17:30 +08:00
Dir-A
4554b82be5 Update ArcISA.cs
Add support "Sisters: Last Day of Summer" English Version
https://vndb.org/r109826
2024-09-24 01:58:42 +08:00
Crsky
e642644845 Fix line breaks. 2024-09-15 04:01:02 +08:00
10 changed files with 458 additions and 137 deletions

View File

@@ -131,6 +131,7 @@
<Compile Include="Artemis\ImageNekoPNG.cs" />
<Compile Include="CsWare\AudioWAV.cs" />
<Compile Include="CsWare\ImageGDT.cs" />
<Compile Include="DigitalWorks\ArcPACPS2.cs" />
<Compile Include="DxLib\HuffmanDecoder.cs" />
<Compile Include="DxLib\WidgetDXA.xaml.cs">
<DependentUpon>WidgetDXA.xaml</DependentUpon>
@@ -191,6 +192,7 @@
<Compile Include="MAGES\ArcLoveOnce.cs" />
<Compile Include="MAGES\ImageBIN.cs" />
<Compile Include="Mugi\ArcBIN.cs" />
<Compile Include="Musica\ArcPAK.cs" />
<Compile Include="NipponIchi\ArcCASN.cs" />
<Compile Include="NipponIchi\ArcPSFS.cs" />
<Compile Include="NipponIchi\ImageNMT.cs" />
@@ -1335,4 +1337,4 @@ xcopy "$(ProjectDir)\Resources\Formats.dat" "$(TargetDir)\GameData\" /D /Y &gt;N
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

View File

@@ -1,53 +1,53 @@
//! \file ArcGXP.cs
//! \date Thu Jun 30 08:17:12 2016
//! \brief Astronauts resource archive.
//
// Copyright (C) 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 System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Text;
using GameRes.Utility;
namespace GameRes.Formats.Astronauts
{
[Export(typeof(ArchiveFormat))]
public class PakOpener : ArchiveFormat
{
public override string Tag { get { return "GXP"; } }
public override string Description { get { return "Astronauts resource archive"; } }
public override uint Signature { get { return 0x505847; } } // 'GXP'
public override bool IsHierarchic { get { return true; } }
public override bool CanWrite { get { return false; } }
private bool UseNameAsKey = false;
static readonly byte[] KnownKey = {
0x40, 0x21, 0x28, 0x38, 0xA6, 0x6E, 0x43, 0xA5, 0x40, 0x21, 0x28, 0x38, 0xA6, 0x43, 0xA5, 0x64,
0x3E, 0x65, 0x24, 0x20, 0x46, 0x6E, 0x74,
};
//! \file ArcGXP.cs
//! \date Thu Jun 30 08:17:12 2016
//! \brief Astronauts resource archive.
//
// Copyright (C) 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 System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Text;
using GameRes.Utility;
namespace GameRes.Formats.Astronauts
{
[Export(typeof(ArchiveFormat))]
public class PakOpener : ArchiveFormat
{
public override string Tag { get { return "GXP"; } }
public override string Description { get { return "Astronauts resource archive"; } }
public override uint Signature { get { return 0x505847; } } // 'GXP'
public override bool IsHierarchic { get { return true; } }
public override bool CanWrite { get { return false; } }
private bool UseNameAsKey = false;
static readonly byte[] KnownKey = {
0x40, 0x21, 0x28, 0x38, 0xA6, 0x6E, 0x43, 0xA5, 0x40, 0x21, 0x28, 0x38, 0xA6, 0x43, 0xA5, 0x64,
0x3E, 0x65, 0x24, 0x20, 0x46, 0x6E, 0x74,
};
public override ArcFile TryOpen(ArcView file)
{
UseNameAsKey = false;
@@ -58,76 +58,76 @@ namespace GameRes.Formats.Astronauts
arc = TryOpenGxpFile(file);
}
return arc;
}
private ArcFile TryOpenGxpFile (ArcView file)
{
int count = file.View.ReadInt32 (0x18);
if (!IsSaneCount (count))
}
private ArcFile TryOpenGxpFile (ArcView file)
{
int count = file.View.ReadInt32 (0x18);
if (!IsSaneCount (count))
return null;
string arcname = Path.GetFileName(file.Name);
byte[] arcname_bytes = Encoding.ASCII.GetBytes(arcname);
long base_offset = file.View.ReadInt64 (0x28);
uint entry_key = KnownKey[0] | (1u ^ KnownKey[1]) << 8 | (2u ^ KnownKey[2]) << 16 | (3u ^ KnownKey[3]) << 24;
string arcname = Path.GetFileName(file.Name);
byte[] arcname_bytes = Encoding.ASCII.GetBytes(arcname);
long base_offset = file.View.ReadInt64 (0x28);
uint entry_key = KnownKey[0] | (1u ^ KnownKey[1]) << 8 | (2u ^ KnownKey[2]) << 16 | (3u ^ KnownKey[3]) << 24;
if (UseNameAsKey)
{
uint arcname_key = (uint)(arcname_bytes[0] | (arcname_bytes[1 % arcname_bytes.Length]) << 8 | (arcname_bytes[2 % arcname_bytes.Length]) << 16 | (arcname_bytes[3 % arcname_bytes.Length]) << 24);
entry_key ^= arcname_key;
}
uint index_offset = 0x30;
var entry_buffer = new byte[0x100];
var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i)
{
var entry_length = file.View.ReadUInt32 (index_offset) ^ entry_key;
if (entry_length < 0x20 || entry_length > 0x1000)
return null;
if (entry_length > entry_buffer.Length)
entry_buffer = new byte[entry_length];
if (entry_length != file.View.Read (index_offset, entry_buffer, 0, entry_length))
return null;
}
uint index_offset = 0x30;
var entry_buffer = new byte[0x100];
var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i)
{
var entry_length = file.View.ReadUInt32 (index_offset) ^ entry_key;
if (entry_length < 0x20 || entry_length > 0x1000)
return null;
if (entry_length > entry_buffer.Length)
entry_buffer = new byte[entry_length];
if (entry_length != file.View.Read (index_offset, entry_buffer, 0, entry_length))
return null;
if (UseNameAsKey)
Decrypt(entry_buffer, entry_length, arcname_bytes);
else
Decrypt(entry_buffer, entry_length);
int name_length = LittleEndian.ToInt32 (entry_buffer, 0xC) * 2; // length in characters
if (name_length >= entry_length)
return null;
var name = Encoding.Unicode.GetString (entry_buffer, 0x20, name_length);
var entry = FormatCatalog.Instance.Create<Entry> (name);
entry.Offset = base_offset + LittleEndian.ToInt64 (entry_buffer, 0x18);
entry.Size = LittleEndian.ToUInt32 (entry_buffer, 4); // length is 64-bit actually
if (!entry.CheckPlacement (file.MaxOffset))
return null;
dir.Add (entry);
index_offset += entry_length;
}
return new ArcFile (file, this, dir);
}
public override Stream OpenEntry (ArcFile arc, Entry entry)
{
var data = arc.File.View.ReadBytes (entry.Offset, entry.Size);
Decrypt(entry_buffer, entry_length);
int name_length = LittleEndian.ToInt32 (entry_buffer, 0xC) * 2; // length in characters
if (name_length >= entry_length)
return null;
var name = Encoding.Unicode.GetString (entry_buffer, 0x20, name_length);
var entry = FormatCatalog.Instance.Create<Entry> (name);
entry.Offset = base_offset + LittleEndian.ToInt64 (entry_buffer, 0x18);
entry.Size = LittleEndian.ToUInt32 (entry_buffer, 4); // length is 64-bit actually
if (!entry.CheckPlacement (file.MaxOffset))
return null;
dir.Add (entry);
index_offset += entry_length;
}
return new ArcFile (file, this, dir);
}
public override Stream OpenEntry (ArcFile arc, Entry entry)
{
var data = arc.File.View.ReadBytes (entry.Offset, entry.Size);
if (UseNameAsKey)
{
string arcname = Path.GetFileName(arc.File.Name);
byte[] arcname_bytes = Encoding.ASCII.GetBytes(arcname);
Decrypt(data, entry.Size, arcname_bytes);
}
}
else
Decrypt(data, entry.Size);
return new BinMemoryStream (data, entry.Name);
}
static void Decrypt (byte[] data, uint length, byte[] key=null)
{
for (uint i = 0; i < length; ++i)
{
byte xorkey = (byte)(i ^ KnownKey[i % KnownKey.Length]);
Decrypt(data, entry.Size);
return new BinMemoryStream (data, entry.Name);
}
static void Decrypt (byte[] data, uint length, byte[] key=null)
{
for (uint i = 0; i < length; ++i)
{
byte xorkey = (byte)(i ^ KnownKey[i % KnownKey.Length]);
if(null != key)
xorkey ^= key[i % key.Length];
data[i] ^= xorkey;
}
}
}
}
xorkey ^= key[i % key.Length];
data[i] ^= xorkey;
}
}
}
}

View File

@@ -35,20 +35,20 @@ namespace GameRes.Formats.DigitalWorks
{
public override string Tag { get { return "PAC/HED"; } }
public override string Description { get { return "Digital Works resource archive"; } }
public override uint Signature { get { return 0x43415050; } } // 'PPAC-PAC'
public override uint Signature { get { return 0; } } // 'PPAC-PAC'
public override bool IsHierarchic { get { return false; } }
public override bool CanWrite { get { return false; } }
public override ArcFile TryOpen (ArcView file)
{
if (!file.View.AsciiEqual (4, "-PAC"))
if (!file.View.AsciiEqual (0, "PPAC-PAC") && !file.View.AsciiEqual(0, "FANA_V1.0.0.0"))
return null;
var hed_name = Path.ChangeExtension (file.Name, "hed");
if (!VFS.FileExists (hed_name))
return null;
using (var hed = VFS.OpenView (hed_name))
{
if (!hed.View.AsciiEqual (0, "PPAC-HED"))
if (!hed.View.AsciiEqual (0, "PPAC-HED") && !hed.View.AsciiEqual(0, "FANA_V1.0.0.0"))
return null;
uint index_offset = 0x10;
const uint data_offset = 0x10;

View File

@@ -0,0 +1,98 @@
//! \file ArcPACPS2.cs
//! \date 2018 Sep 18
//! \brief Digital Works PS2 resource archive.
//
// Copyright (C) 2018 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.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using GameRes.Compression;
namespace GameRes.Formats.DigitalWorks
{
[Export(typeof(ArchiveFormat))]
public class PacPS2Opener : ArchiveFormat
{
public override string Tag { get { return "PAC/PS2"; } }
public override string Description { get { return "Digital Works PS2 resource archive"; } }
public override uint Signature { get { return 0x434150; } } // 'PAC'
public override bool IsHierarchic { get { return false; } }
public override bool CanWrite { get { return false; } }
/**
Target games:
Cafe Little Wish SLPM-65294
F Fanatic SLPM-65296
*/
public override ArcFile TryOpen (ArcView file)
{
uint filename_index = file.View.ReadUInt32(4);
int count = file.View.ReadInt32(8);
uint index_offset = 0x0C;
if (!IsSaneCount (count))
return null;
var dir = new List<Entry> (count);
int i = 0;
while (i < count)
{
var name = file.View.ReadString (filename_index + i * 0x40, 0x40);
var entry = FormatCatalog.Instance.Create<PackedEntry> (name);
entry.Offset = file.View.ReadUInt32 (index_offset + i * 8);
entry.Size = file.View.ReadUInt32 (index_offset + i * 8 + 4);
if (!entry.CheckPlacement (file.MaxOffset))
return null;
dir.Add (entry);
i++;
}
return new ArcFile (file, this, dir);
}
public override Stream OpenEntry (ArcFile arc, Entry entry)
{
var pent = entry as PackedEntry;
if (null == pent)
return base.OpenEntry (arc, entry);
if (!pent.IsPacked)
{
if (!arc.File.View.AsciiEqual (entry.Offset, "LZS\0"))
return base.OpenEntry (arc, entry);
pent.IsPacked = true;
pent.UnpackedSize = arc.File.View.ReadUInt32 (entry.Offset+4);
}
var input = arc.File.CreateStream (entry.Offset+8, entry.Size-8);
bool embedded_lzs = (input.Signature & ~0xF0u) == 0x535A4C0F; // 'LZS'
var lzs = new LzssStream (input);
if (embedded_lzs)
{
var header = new byte[8];
lzs.Read (header, 0, 8);
pent.UnpackedSize = header.ToUInt32 (4);
lzs = new LzssStream (lzs);
}
return lzs;
}
}
}

View File

@@ -44,7 +44,7 @@ namespace GameRes.Formats.ISM
public override ArcFile TryOpen (ArcView file)
{
if (!file.View.AsciiEqual (4, "ARCHIVED"))
if (!file.View.AsciiEqual (4, "ARCHIVED") && !file.View.AsciiEqual(4, "ENGLISH "))
return null;
int count = file.View.ReadInt16 (0x0C);
if (!IsSaneCount (count))

173
ArcFormats/Musica/ArcPAK.cs Normal file
View File

@@ -0,0 +1,173 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Text;
namespace GameRes.Formats.Musica
{
[Export(typeof(ArchiveFormat))]
public class PakOpener : ArchiveFormat
{
public override string Tag { get; } = "PAK";
public override string Description { get; } = "Musica engine legacy resource archive";
public override uint Signature { get; } = 0;
public override bool IsHierarchic { get; } = true;
public override bool CanWrite { get; } = false;
public PakOpener()
{
Extensions = new[] { "pak" };
ContainedFormats = new string[] { "PNG", "OGG" };
}
static readonly HashSet<string> PakImageNames = new HashSet<string>()
{
"bg", "st",
};
static readonly HashSet<string> PakAudioNames = new HashSet<string>()
{
"bgm", "se", "voice",
};
private string GetType(string pakName, string entryName)
{
if (PakImageNames.Contains(pakName))
{
return "image";
}
if (PakAudioNames.Contains(pakName))
{
return "audio";
}
return FormatCatalog.Instance.GetTypeFromName(entryName, ContainedFormats);
}
public override ArcFile TryOpen(ArcView view)
{
Stream input = view.CreateStream();
using(input = new NegStream(input))
{
using(ArcView.Reader reader = new ArcView.Reader(input))
{
int count = reader.ReadInt32();
if (count <= 0)
{
return null;
}
List<Entry> entries = new List<Entry>(count);
for(int i = 0; i < count; ++i)
{
uint indexLen = reader.ReadUInt32();
long indexStart = input.Position;
string name = input.ReadCString();
if (string.IsNullOrWhiteSpace(name))
{
return null;
}
uint size = reader.ReadUInt32();
uint offset = reader.ReadUInt32();
Entry entry = new Entry() { Name = name, Offset = offset, Size = size };
if (!entry.CheckPlacement(view.MaxOffset))
{
return null;
}
entry.Type = this.GetType(Path.GetFileNameWithoutExtension(view.Name), entry.Name);
entries.Add(entry);
input.Position = indexStart + indexLen;
}
return new PakArchive(view, this, entries, input.Position);
}
}
}
public override Stream OpenEntry(ArcFile arc, Entry entry)
{
if (!(arc is PakArchive pakArc))
{
return base.OpenEntry(arc, entry);
}
return new NegStream(base.OpenEntry(arc, pakArc.GetEntry(entry)));
}
}
internal class PakArchive : ArcFile
{
private long m_IndexSize;
public PakArchive(ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, long indexSize) : base(arc, impl, dir)
{
m_IndexSize = indexSize;
}
public Entry GetEntry(Entry e)
{
return new Entry
{
Name = e.Name,
Offset = e.Offset + m_IndexSize,
Size = e.Size,
Type = e.Type,
};
}
}
//neg reg8
public class NegStream : ProxyStream
{
public NegStream(Stream stream, bool leave_open = false) : base(stream, leave_open)
{
}
public override int Read(byte[] buffer, int offset, int count)
{
int read = BaseStream.Read(buffer, offset, count);
for (int i = 0; i < read; ++i)
{
buffer[offset + i] = (byte)-buffer[offset + i];
}
return read;
}
public override int ReadByte()
{
int b = BaseStream.ReadByte();
if (-1 != b)
{
b = (byte)-b;
}
return b;
}
byte[] write_buf;
public override void Write(byte[] buffer, int offset, int count)
{
if (null == write_buf)
write_buf = new byte[81920];
while (count > 0)
{
int chunk = Math.Min(write_buf.Length, count);
for (int i = 0; i < chunk; ++i)
{
write_buf[i] = (byte)-buffer[offset + i];
}
BaseStream.Write(write_buf, 0, chunk);
offset += chunk;
count -= chunk;
}
}
public override void WriteByte(byte value)
{
BaseStream.WriteByte((byte)-value);
}
}
}

View File

@@ -34,11 +34,13 @@ namespace GameRes.Formats.Musica
internal class SqzArchive : ArcFile
{
public readonly ImageMetaData Info;
public readonly int FPS;
public SqzArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, ImageMetaData info)
public SqzArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, ImageMetaData info, int fps)
: base (arc, impl, dir)
{
Info = info;
FPS = fps;
}
}
@@ -53,7 +55,7 @@ namespace GameRes.Formats.Musica
public override ArcFile TryOpen (ArcView file)
{
int count = file.View.ReadInt32 (0x10) * 2;
int count = file.View.ReadInt32 (4);
if (!IsSaneCount (count))
return null;
@@ -62,8 +64,8 @@ namespace GameRes.Formats.Musica
Width = file.View.ReadUInt32 (8),
Height = file.View.ReadUInt32 (0xC),
BPP = 32,
// BPP = file.View.ReadInt32 (4),
};
int fps = file.View.ReadInt32(0x10);
var base_name = Path.GetFileNameWithoutExtension (file.Name);
uint index_offset = 0x14;
var dir = new List<Entry> (count);
@@ -80,7 +82,7 @@ namespace GameRes.Formats.Musica
dir.Add (entry);
index_offset += 8;
}
return new SqzArchive (file, this, dir, info);
return new SqzArchive (file, this, dir, info, fps);
}
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)

View File

Binary file not shown.

View File

@@ -54,7 +54,8 @@ namespace GameRes.Formats.Seraphim
public override ImageMetaData ReadMetaData (IBinaryStream stream)
{
var header = stream.ReadHeader (0x10);
if ('C' != header[0] || 'F' != header[1] || 0 != header[3])
uint sig = header.ToUInt16(0);
if (sig != Signature)
return null;
int packed_size = header.ToInt32 (12);
if (packed_size <= 0 || packed_size > stream.Length-0x10)

View File

@@ -41,7 +41,7 @@ namespace GameRes.Formats.Valkyria
internal interface IMg2Scheme
{
Mg2EncryptedStream CreateStream (Stream main, int offset, int length);
Mg2EncryptedStream CreateStream (Stream main, int offset, int length, int key);
ImageData CreateImage (BitmapSource bitmap, ImageMetaData info);
}
@@ -52,17 +52,17 @@ namespace GameRes.Formats.Valkyria
public override string Description { get { return "Valkyria image format"; } }
public override uint Signature { get { return 0x4F43494D; } } // 'MICO'
static readonly IMg2Scheme[] KnownSchemes = { new Mg2SchemeV1(), new Mg2SchemeV2() };
static readonly IMg2Scheme[] KnownSchemes = { new Mg2SchemeV1(), new Mg2SchemeV2(), new Mg2SchemeV3() };
public override ImageMetaData ReadMetaData (IBinaryStream file)
{
var header = file.ReadHeader (0x10);
if (!header.AsciiEqual (4, "CG01"))
if (!header.AsciiEqual (4, "CG01") && !header.AsciiEqual (4, "CG02"))
return null;
int length = header.ToInt32 (8);
foreach (var scheme in KnownSchemes)
{
using (var input = scheme.CreateStream (file.AsStream, 0x10, length))
using (var input = scheme.CreateStream (file.AsStream, 0x10, length, length))
using (var img = new BinaryStream (input, file.Name))
{
ImageFormat format;
@@ -102,7 +102,7 @@ namespace GameRes.Formats.Valkyria
BitmapSource ReadBitmapSource (Stream file, Mg2MetaData meta)
{
BitmapSource frame;
using (var input = meta.Scheme.CreateStream (file, 0x10, meta.ImageLength))
using (var input = meta.Scheme.CreateStream (file, 0x10, meta.ImageLength, meta.ImageLength))
using (var img = new BinaryStream (input, meta.FileName))
{
var image = meta.Format.Read (img, meta);
@@ -116,7 +116,7 @@ namespace GameRes.Formats.Valkyria
var pixels = new byte[stride * (int)meta.Height];
frame.CopyPixels (pixels, stride, 0);
using (var input = meta.Scheme.CreateStream (file, 0x10+meta.ImageLength, meta.AlphaLength))
using (var input = meta.Scheme.CreateStream (file, 0x10+meta.ImageLength, meta.AlphaLength, meta.ImageLength))
{
var decoder = BitmapDecoder.Create (input, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
BitmapSource alpha_frame = decoder.Frames[0];
@@ -148,32 +148,53 @@ namespace GameRes.Formats.Valkyria
internal class Mg2EncryptedStream : StreamRegion
{
readonly byte m_version;
readonly int m_threshold;
readonly byte m_key;
readonly byte m_key0;
readonly byte m_key1;
protected Mg2EncryptedStream (Stream main, int offset, int length, int threshold, byte key)
protected Mg2EncryptedStream (Stream main, int offset, int length, byte version, int threshold, byte key0, byte key1)
: base (main, offset, length, true)
{
m_version = version;
m_threshold = threshold;
m_key = key;
m_key0 = key0;
m_key1 = key1;
}
public static Mg2EncryptedStream CreateV1 (Stream main, int offset, int length)
{
return new Mg2EncryptedStream (main, offset, length, length / 5, 0);
return new Mg2EncryptedStream(main, offset, length, 1, length / 5, 0, 0);
}
public static Mg2EncryptedStream CreateV2 (Stream main, int offset, int length)
{
return new Mg2EncryptedStream (main, offset, length, Math.Min (25, length), (byte)length);
return new Mg2EncryptedStream( main, offset, length, 2, Math.Min(25, length), (byte)length, 0);
}
public static Mg2EncryptedStream CreateV3 (Stream main, int offset, int length, int key)
{
return new Mg2EncryptedStream(main, offset, length, 3, 0, (byte)(key >> 1), (byte)((key & 1) + (key >> 3)));
}
public override int Read (byte[] buffer, int offset, int count)
{
int pos = (int)Position;
int read = base.Read (buffer, offset, count);
for (int i = 0; i < read && pos < m_threshold; ++i)
buffer[offset+i] ^= (byte)(m_key + pos++);
long pos = Position;
int read = base.Read(buffer, offset, count);
if (m_version == 3)
{
for (int i = 0; i < read; ++i)
{
buffer[offset+i] ^= (byte)((pos >> 4) ^ (pos + m_key0) ^ m_key1);
pos++;
}
}
else
{
for (int i = 0; i < read && pos < m_threshold; ++i)
buffer[offset+i] ^= (byte)(m_key0 + pos++);
}
return read;
}
@@ -181,15 +202,24 @@ namespace GameRes.Formats.Valkyria
{
long pos = Position;
int b = base.ReadByte();
if (b != -1 && pos < m_threshold)
b ^= (byte)(m_key + pos);
if (m_version == 3)
{
b ^= (byte)((pos >> 4) ^ (pos + m_key0) ^ m_key1);
}
else
{
if (b != -1 && pos < m_threshold)
b ^= (byte)(m_key0 + pos);
}
return b;
}
}
internal class Mg2SchemeV1 : IMg2Scheme
{
public Mg2EncryptedStream CreateStream (Stream main, int offset, int length)
public Mg2EncryptedStream CreateStream (Stream main, int offset, int length, int key)
{
return Mg2EncryptedStream.CreateV1 (main, offset, length);
}
@@ -203,7 +233,7 @@ namespace GameRes.Formats.Valkyria
internal class Mg2SchemeV2 : IMg2Scheme
{
public Mg2EncryptedStream CreateStream (Stream main, int offset, int length)
public Mg2EncryptedStream CreateStream (Stream main, int offset, int length, int key)
{
return Mg2EncryptedStream.CreateV2 (main, offset, length);
}
@@ -215,4 +245,19 @@ namespace GameRes.Formats.Valkyria
return new ImageData (frame, info);
}
}
internal class Mg2SchemeV3 : IMg2Scheme
{
public Mg2EncryptedStream CreateStream (Stream main, int offset, int length, int key)
{
return Mg2EncryptedStream.CreateV3 (main, offset, length, key);
}
public ImageData CreateImage (BitmapSource frame, ImageMetaData info)
{
frame = new TransformedBitmap(frame, new ScaleTransform { ScaleY = -1 });
frame.Freeze();
return new ImageData(frame, info);
}
}
}