mirror of
https://github.com/crskycode/GARbro.git
synced 2026-06-06 21:58:53 +08:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3db6d9a732 | ||
|
|
3c5b8230e4 | ||
|
|
da438b6575 | ||
|
|
b03d2fdecc | ||
|
|
267353e1fc | ||
|
|
f49598532d | ||
|
|
89674e9d58 | ||
|
|
99b8faab23 | ||
|
|
25f9285181 | ||
|
|
10ff1cfcf9 | ||
|
|
16c0daf643 | ||
|
|
1b6d27061e | ||
|
|
cd2e43d61c | ||
|
|
872842d408 | ||
|
|
7c82b909e0 | ||
|
|
dd85a7a618 | ||
|
|
afe9159853 | ||
|
|
e1e252884b | ||
|
|
01b6d41f82 | ||
|
|
089d5f2fd6 | ||
|
|
49016837c6 | ||
|
|
02a591c83a | ||
|
|
ddf928f02a | ||
|
|
08e5a6c69a | ||
|
|
526485c720 | ||
|
|
653ee4685d | ||
|
|
d8eb3aed1a | ||
|
|
1d5d74ce93 | ||
|
|
ccd4424b1b | ||
|
|
987d57a4db | ||
|
|
2df8145f83 | ||
|
|
f6f989dd2f | ||
|
|
dc1647c5bf | ||
|
|
4929d1efd1 | ||
|
|
8b9ddad110 | ||
|
|
e6ca959ded | ||
|
|
c5ac986cde |
@@ -80,11 +80,7 @@ namespace GameRes.Formats.AdvSys
|
||||
else
|
||||
{
|
||||
var res = AutoEntry.DetectFileType (signature);
|
||||
if (res != null)
|
||||
{
|
||||
entry.Type = res.Type;
|
||||
entry.Name = Path.ChangeExtension (entry.Name, res.Extensions.FirstOrDefault());
|
||||
}
|
||||
entry.ChangeType (res);
|
||||
}
|
||||
dir.Add (entry);
|
||||
current_offset += size;
|
||||
|
||||
@@ -57,6 +57,8 @@ namespace GameRes.Formats.Ags
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.Name.EndsWith (".ani", StringComparison.InvariantCultureIgnoreCase))
|
||||
return null;
|
||||
uint first_offset = file.View.ReadUInt32 (0);
|
||||
if (first_offset < 4 || file.MaxOffset > int.MaxValue || first_offset >= file.MaxOffset || 0 != (first_offset & 3))
|
||||
return null;
|
||||
|
||||
@@ -90,7 +90,19 @@
|
||||
<Compile Include="Ankh\AudioPCM.cs" />
|
||||
<Compile Include="Aoi\ImageAGF.cs" />
|
||||
<Compile Include="ArcARCX.cs" />
|
||||
<Compile Include="ArcCG.cs" />
|
||||
<Compile Include="BlackRainbow\ArcCCF.cs" />
|
||||
<Compile Include="BlackRainbow\ArcIMP.cs" />
|
||||
<Compile Include="BlackRainbow\ArcSPPAK.cs" />
|
||||
<Compile Include="Cadath\ArcDAF.cs" />
|
||||
<Compile Include="Cadath\ImageCGF.cs" />
|
||||
<Compile Include="Foster\ArcC24.cs" />
|
||||
<Compile Include="Foster\ArcFA2.cs" />
|
||||
<Compile Include="Foster\ImageC24.cs" />
|
||||
<Compile Include="GameSystem\ArcPureMail.cs" />
|
||||
<Compile Include="GameSystem\AudioADP4.cs" />
|
||||
<Compile Include="Qlie\DelphiDeserializer.cs" />
|
||||
<Compile Include="RealLive\ArcKOE.cs" />
|
||||
<Compile Include="Software House Parsley\ArcCG.cs" />
|
||||
<Compile Include="Artemis\ArcPFS.cs" />
|
||||
<Compile Include="AudioWMA.cs" />
|
||||
<Compile Include="BlackRainbow\ArcDAT.cs" />
|
||||
@@ -488,7 +500,10 @@
|
||||
<Compile Include="ShiinaRio\ArcS25.cs" />
|
||||
<Compile Include="ArcSAF.cs" />
|
||||
<Compile Include="BlackCyc\ArcVPK.cs" />
|
||||
<Compile Include="Valkyria\ArcDAT.cs" />
|
||||
<Compile Include="Valkyria\ArcODN.cs" />
|
||||
<Compile Include="Valkyria\ImageMAL.cs" />
|
||||
<Compile Include="Valkyria\ImageMG2.cs" />
|
||||
<Compile Include="Vitamin\ImageMFC.cs" />
|
||||
<Compile Include="Vitamin\ImageSBI.cs" />
|
||||
<Compile Include="VnEngine\ArcAXR.cs" />
|
||||
|
||||
76
ArcFormats/BlackRainbow/ArcCCF.cs
Normal file
76
ArcFormats/BlackRainbow/ArcCCF.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
//! \file ArcCCF.cs
|
||||
//! \date Sun Feb 19 22:05:59 2017
|
||||
//! \brief Black Rainbow audio archive.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.BlackRainbow
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class CcfOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "CCF"; } }
|
||||
public override string Description { get { return "BlackRainbow audio archive"; } }
|
||||
public override uint Signature { get { return 0x22664343; } } // 'CCf"'
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public CcfOpener ()
|
||||
{
|
||||
Extensions = new string[] { "pak" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (4);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
|
||||
var base_name = Path.GetFileNameWithoutExtension (file.Name);
|
||||
uint index_offset = 8;
|
||||
long base_offset = index_offset + count * 4;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
uint offset = file.View.ReadUInt32 (index_offset);
|
||||
index_offset += 4;
|
||||
var name = string.Format ("{0}#{1:D4}", base_name, i);
|
||||
var entry = AutoEntry.Create (file, base_offset+offset, name);
|
||||
if (entry.Offset >= file.MaxOffset)
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
}
|
||||
for (int i = 1; i < dir.Count; ++i)
|
||||
{
|
||||
dir[i-1].Size = (uint)(dir[i].Offset - dir[i-1].Offset);
|
||||
}
|
||||
var last_entry = dir[dir.Count-1];
|
||||
last_entry.Size = (uint)(file.MaxOffset - last_entry.Offset);
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
147
ArcFormats/BlackRainbow/ArcIMP.cs
Normal file
147
ArcFormats/BlackRainbow/ArcIMP.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
//! \file ArcIMP.cs
|
||||
//! \date Sun Feb 19 22:23:48 2017
|
||||
//! \brief Black Rainbow image archive.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
using GameRes.Compression;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.BlackRainbow
|
||||
{
|
||||
internal class ImpArchive : ArcFile
|
||||
{
|
||||
public readonly uint Key;
|
||||
|
||||
public ImpArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, uint key)
|
||||
: base (arc, impl, dir)
|
||||
{
|
||||
Key = key;
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class ImpOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "IMP"; } }
|
||||
public override string Description { get { return "BlackRainbow image archive"; } }
|
||||
public override uint Signature { get { return 0x3D66; } }
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public ImpOpener ()
|
||||
{
|
||||
Signatures = KnownSchemes.Keys;
|
||||
}
|
||||
|
||||
static readonly Dictionary<uint, uint> KnownSchemes = new Dictionary<uint, uint>
|
||||
{
|
||||
{ 0x3D66, 0xCE032ADB }, // Kannagi
|
||||
{ 0x59E8, 0xD36050EC }, // From M
|
||||
};
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
uint key = KnownSchemes[file.View.ReadUInt32 (0)];
|
||||
var base_name = Path.GetFileNameWithoutExtension (file.Name);
|
||||
uint base_offset = 0x404;
|
||||
uint offset = file.View.ReadUInt32 (4);
|
||||
uint index_offset = 8;
|
||||
var dir = new List<Entry>();
|
||||
for (int i = 0; i < 0xFF; ++i)
|
||||
{
|
||||
uint next_offset = file.View.ReadUInt32 (index_offset);
|
||||
uint size = next_offset - offset;
|
||||
if (size > 0x10)
|
||||
{
|
||||
var entry = new Entry {
|
||||
Name = string.Format ("{0}#{1:D3}", base_name, i),
|
||||
Type = "image",
|
||||
Offset = base_offset + offset,
|
||||
Size = size,
|
||||
};
|
||||
dir.Add (entry);
|
||||
}
|
||||
index_offset += 4;
|
||||
offset = next_offset;
|
||||
}
|
||||
if (0 == dir.Count)
|
||||
return null;
|
||||
return new ImpArchive (file, this, dir, key);
|
||||
}
|
||||
|
||||
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
|
||||
{
|
||||
var imp_arc = (ImpArchive)arc;
|
||||
var offset = entry.Offset;
|
||||
var info = new ImpMetaData
|
||||
{
|
||||
Width = arc.File.View.ReadUInt32 (offset),
|
||||
Height = arc.File.View.ReadUInt32 (offset+4),
|
||||
BPP = 32,
|
||||
Key = imp_arc.Key,
|
||||
HasAlpha = arc.File.View.ReadUInt32 (offset+12) != 0,
|
||||
};
|
||||
uint packed_size = arc.File.View.ReadUInt32 (offset+8);
|
||||
var input = arc.File.CreateStream (offset, packed_size+0x10);
|
||||
return new ImpDecoder (input, info);
|
||||
}
|
||||
}
|
||||
|
||||
internal class ImpMetaData : ImageMetaData
|
||||
{
|
||||
public uint Key;
|
||||
public bool HasAlpha;
|
||||
}
|
||||
|
||||
internal sealed class ImpDecoder : BinaryImageDecoder
|
||||
{
|
||||
byte[] m_key;
|
||||
bool m_has_alpha;
|
||||
|
||||
public ImpDecoder (IBinaryStream input, ImpMetaData info) : base (input, info)
|
||||
{
|
||||
m_has_alpha = info.HasAlpha;
|
||||
m_key = new byte[4];
|
||||
LittleEndian.Pack (info.Key, m_key, 0);
|
||||
}
|
||||
|
||||
protected override ImageData GetImageData ()
|
||||
{
|
||||
m_input.Position = 0x10;
|
||||
var pixels = new byte[Info.Width * Info.Height * 4];
|
||||
using (var lzs = new ByteStringEncryptedStream (m_input.AsStream, m_key, true))
|
||||
using (var input = new LzssStream (lzs))
|
||||
{
|
||||
if (pixels.Length != input.Read (pixels, 0, pixels.Length))
|
||||
throw new InvalidFormatException();
|
||||
var format = m_has_alpha ? PixelFormats.Bgra32 : PixelFormats.Bgr32;
|
||||
return ImageData.Create (Info, format, null, pixels);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
104
ArcFormats/BlackRainbow/ArcSPPAK.cs
Normal file
104
ArcFormats/BlackRainbow/ArcSPPAK.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
//! \file ArcSPPAK.cs
|
||||
//! \date Mon Feb 20 07:45:17 2017
|
||||
//! \brief Black Rainbow script archive.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.BlackRainbow
|
||||
{
|
||||
internal class SpArchive : ArcFile
|
||||
{
|
||||
public readonly byte Key;
|
||||
|
||||
public SpArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, byte key)
|
||||
: base (arc, impl, dir)
|
||||
{
|
||||
Key = key;
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class SpPakOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "PAK/SP"; } }
|
||||
public override string Description { get { return "BlackRainbow script archive"; } }
|
||||
public override uint Signature { get { return 0x69695669; } }
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public SpPakOpener ()
|
||||
{
|
||||
Signatures = KnownSchemes.Keys;
|
||||
}
|
||||
|
||||
static readonly Dictionary<uint, byte> KnownSchemes = new Dictionary<uint, byte>
|
||||
{
|
||||
{ 0x69695669, 0x07 }, // Kannagi
|
||||
{ 0x8492E36F, 0x9C }, // From M
|
||||
};
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (4);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
byte key = KnownSchemes[file.View.ReadUInt32 (0)];
|
||||
|
||||
var base_name = Path.GetFileNameWithoutExtension (file.Name);
|
||||
uint index_offset = 8;
|
||||
long base_offset = index_offset + 4 * count;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var name = string.Format ("{0}#{1:D4}", base_name, i);
|
||||
var entry = FormatCatalog.Instance.Create<Entry> (name);
|
||||
entry.Offset = base_offset + file.View.ReadUInt32 (index_offset);
|
||||
index_offset += 4;
|
||||
dir.Add (entry);
|
||||
}
|
||||
for (int i = 1; i < dir.Count; ++i)
|
||||
{
|
||||
dir[i-1].Size = (uint)(dir[i].Offset - dir[i-1].Offset);
|
||||
}
|
||||
var last_entry = dir[dir.Count-1];
|
||||
last_entry.Size = (uint)(file.MaxOffset - last_entry.Offset);
|
||||
return new SpArchive (file, this, dir, key);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var sp_arc = (SpArchive)arc;
|
||||
var key = sp_arc.Key;
|
||||
var data = arc.File.View.ReadBytes (entry.Offset, entry.Size);
|
||||
for (int i = 0; i < data.Length; ++i)
|
||||
{
|
||||
data[i] = Binary.RotByteR ((byte)(data[i] ^ key), 2);
|
||||
}
|
||||
return new BinMemoryStream (data, entry.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
104
ArcFormats/Cadath/ArcDAF.cs
Normal file
104
ArcFormats/Cadath/ArcDAF.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
//! \file ArcDAF.cs
|
||||
//! \date Thu Feb 23 01:26:17 2017
|
||||
//! \brief Cadath resource archive format.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using GameRes.Compression;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Cadath
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class DafOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "ARC/DAF"; } }
|
||||
public override string Description { get { return "Cadath resource archive"; } }
|
||||
public override uint Signature { get { return 0x1A464144; } } // 'DAF'
|
||||
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_offset = 8;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
uint offset = file.View.ReadUInt32 (index_offset);
|
||||
uint size = file.View.ReadUInt32 (index_offset+4);
|
||||
var name = file.View.ReadString (index_offset+8, 0x18);
|
||||
var entry = FormatCatalog.Instance.Create<Entry> (name);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
entry.Offset = offset;
|
||||
entry.Size = size;
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
index_offset += 0x20;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
if (!entry.Name.EndsWith (".snr", System.StringComparison.InvariantCultureIgnoreCase)
|
||||
|| !arc.File.View.AsciiEqual (entry.Offset, "SNR\x1A"))
|
||||
return base.OpenEntry (arc, entry);
|
||||
try
|
||||
{
|
||||
var data = arc.File.View.ReadBytes (entry.Offset+12, entry.Size-12);
|
||||
DecryptSnr (data);
|
||||
CgfDecoder.Decrypt (data, data.Length);
|
||||
// uint checksum = LittleEndian.ToUInt32 (data, 0);
|
||||
// uint crc = Crc32Normal.Compute (data, 4, data.Length-4);
|
||||
var input = new MemoryStream (data, 4, data.Length-4);
|
||||
return new ZLibStream (input, CompressionMode.Decompress);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return base.OpenEntry (arc, entry);
|
||||
}
|
||||
}
|
||||
|
||||
void DecryptSnr (byte[] data)
|
||||
{
|
||||
byte key = 0x84;
|
||||
for (int i = 0; i < data.Length; ++i)
|
||||
{
|
||||
data[i] -= key;
|
||||
for (int count = ((i & 0xF) + 2) / 3; count > 0; --count)
|
||||
{
|
||||
key += 0x99;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
179
ArcFormats/Cadath/ImageCGF.cs
Normal file
179
ArcFormats/Cadath/ImageCGF.cs
Normal file
@@ -0,0 +1,179 @@
|
||||
//! \file ImageCGF.cs
|
||||
//! \date Thu Feb 23 15:12:04 2017
|
||||
//! \brief Cadath image format.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
using GameRes.Compression;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Cadath
|
||||
{
|
||||
internal class CgfMetaData : ImageMetaData
|
||||
{
|
||||
public int Method;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class CgfFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "CGF/CADATH"; } }
|
||||
public override string Description { get { return "Cadath image format"; } }
|
||||
public override uint Signature { get { return 0x1A464743; } } // 'CGF'
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (10);
|
||||
int method = header[4];
|
||||
if (method != 1 && method != 2)
|
||||
return null;
|
||||
return new CgfMetaData
|
||||
{
|
||||
Width = header.ToUInt16 (6),
|
||||
Height = header.ToUInt16 (8),
|
||||
BPP = header[5],
|
||||
Method = method,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new CgfDecoder (file, (CgfMetaData)info);
|
||||
var pixels = reader.Unpack();
|
||||
return ImageData.Create (info, reader.Format, null, pixels);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("CgfFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class CgfDecoder
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
CgfMetaData m_info;
|
||||
byte[] m_output;
|
||||
|
||||
public PixelFormat Format { get; private set; }
|
||||
|
||||
public CgfDecoder (IBinaryStream input, CgfMetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
m_output = new byte[(int)m_info.Width * (int)m_info.Height * m_info.BPP / 8];
|
||||
if (32 == m_info.BPP)
|
||||
Format = PixelFormats.Bgra32;
|
||||
else
|
||||
Format = PixelFormats.Bgr24;
|
||||
}
|
||||
|
||||
public byte[] Unpack ()
|
||||
{
|
||||
m_input.Position = 10;
|
||||
int pixel_size = m_info.BPP / 8;
|
||||
Action<byte[], int, byte[]> unpack_channel;
|
||||
if (1 == m_info.Method)
|
||||
unpack_channel = UnpackZLib;
|
||||
else if (2 == m_info.Method)
|
||||
unpack_channel = UnpackRle;
|
||||
else
|
||||
throw new NotSupportedException();
|
||||
var channel = new byte[m_info.Width * m_info.Height];
|
||||
byte[] buffer = null;
|
||||
for (int i = 0; i < pixel_size; ++i)
|
||||
{
|
||||
int channel_length = m_input.ReadInt32();
|
||||
if (channel_length < 0)
|
||||
throw new InvalidFormatException();
|
||||
if (null == buffer || channel_length > buffer.Length)
|
||||
buffer = new byte[channel_length];
|
||||
if (channel_length != m_input.Read (buffer, 0, channel_length))
|
||||
throw new EndOfStreamException();
|
||||
unpack_channel (buffer, channel_length, channel);
|
||||
int dst = i;
|
||||
for (int j = 0; j < channel.Length; ++j)
|
||||
{
|
||||
m_output[dst] = channel[j];
|
||||
dst += pixel_size;
|
||||
}
|
||||
}
|
||||
return m_output;
|
||||
}
|
||||
|
||||
void UnpackZLib (byte[] input, int length, byte[] output)
|
||||
{
|
||||
Decrypt (input, length);
|
||||
using (var zinput = new MemoryStream (input, 4, length - 4))
|
||||
using (var z = new ZLibStream (zinput, CompressionMode.Decompress))
|
||||
z.Read (output, 0, output.Length);
|
||||
}
|
||||
|
||||
void UnpackRle (byte[] input, int length, byte[] output)
|
||||
{
|
||||
int unpacked_length = LittleEndian.ToInt32 (input, 0);
|
||||
if (unpacked_length < 0 || unpacked_length > output.Length)
|
||||
throw new InvalidFormatException();
|
||||
int src = 4;
|
||||
int dst = 0;
|
||||
byte last_byte = 0;
|
||||
while (dst < unpacked_length)
|
||||
{
|
||||
byte b = input[src++];
|
||||
output[dst++] = b;
|
||||
if (b == last_byte)
|
||||
{
|
||||
int count = input[src++];
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
output[dst++] = b;
|
||||
}
|
||||
}
|
||||
last_byte = b;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe internal static void Decrypt (byte[] data, int length)
|
||||
{
|
||||
if (length < 4)
|
||||
return;
|
||||
if (length > data.Length)
|
||||
throw new ArgumentOutOfRangeException ("length");
|
||||
fixed (byte* data8 = data)
|
||||
{
|
||||
uint* data32 = (uint*)data8;
|
||||
const uint seed = 0x3977141B;
|
||||
uint key = seed;
|
||||
for (int i = 0; i < length; i += 4)
|
||||
{
|
||||
key = Binary.RotL (key, 3);
|
||||
*data32++ ^= key;
|
||||
key += seed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,13 +39,20 @@ namespace GameRes.Formats.Cromwell
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public GraphicPakOpener ()
|
||||
{
|
||||
Signatures = new uint[] { 0x70617247, 0x63696F56 };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.View.AsciiEqual (4, "hic PackData"))
|
||||
bool is_graphic = file.View.AsciiEqual (0, "Graphic PackData");
|
||||
if (!is_graphic && !file.View.AsciiEqual (0, "Voice PackData. "))
|
||||
return null;
|
||||
int count = file.View.ReadInt32 (0x10);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
string type = is_graphic ? "image" : "audio";
|
||||
|
||||
uint index_offset = 0x14;
|
||||
var dir = new List<Entry> (count);
|
||||
@@ -55,7 +62,7 @@ namespace GameRes.Formats.Cromwell
|
||||
if (string.IsNullOrWhiteSpace (name))
|
||||
return null;
|
||||
index_offset += 0xC;
|
||||
var entry = FormatCatalog.Instance.Create<PackedEntry> (name);
|
||||
var entry = new PackedEntry { Name = name, Type = type };
|
||||
entry.Offset = file.View.ReadUInt32 (index_offset);
|
||||
if (entry.Offset >= file.MaxOffset)
|
||||
return null;
|
||||
|
||||
@@ -74,29 +74,31 @@ namespace GameRes.Formats.Emote
|
||||
{
|
||||
foreach (var key in KnownKeys)
|
||||
{
|
||||
if (reader.Parse (key))
|
||||
try
|
||||
{
|
||||
var dir = reader.GetTextures();
|
||||
if (null == dir)
|
||||
dir = reader.GetLayers();
|
||||
if (null == dir || 0 == dir.Count)
|
||||
return null;
|
||||
return new ArcFile (file, this, dir);
|
||||
if (reader.Parse (key))
|
||||
return OpenArcFile (reader, file);
|
||||
if (!reader.IsEncrypted)
|
||||
break;
|
||||
}
|
||||
if (!reader.IsEncrypted)
|
||||
break;
|
||||
catch { /* ignore parse errors caused by invalid key */ }
|
||||
}
|
||||
if (reader.ParseNonEncrypted())
|
||||
{
|
||||
var dir = reader.GetLayers();
|
||||
if (null == dir)
|
||||
return null;
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
return OpenArcFile (reader, file);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
ArcFile OpenArcFile (PsbReader reader, ArcView file)
|
||||
{
|
||||
var dir = reader.GetTextures();
|
||||
if (null == dir)
|
||||
dir = reader.GetLayers();
|
||||
if (null == dir || 0 == dir.Count)
|
||||
return null;
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
|
||||
{
|
||||
var tex = (TexEntry)entry;
|
||||
@@ -246,26 +248,64 @@ namespace GameRes.Formats.Emote
|
||||
var item_value = item.Value as IDictionary;
|
||||
if (null == item_value)
|
||||
continue;
|
||||
var texture = item_value["texture"] as IDictionary;
|
||||
if (null == texture)
|
||||
continue;
|
||||
var pixel = texture["pixel"] as EmChunk;
|
||||
if (item_value.Contains ("texture"))
|
||||
{
|
||||
AddTextureEntry (dir, item.Key, item_value["texture"] as IDictionary);
|
||||
}
|
||||
else if (item_value.Contains ("icon"))
|
||||
{
|
||||
AddIconEntry (dir, item.Key, item_value["icon"] as IDictionary);
|
||||
}
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
void AddTextureEntry (List<Entry> dir, object name, IDictionary texture)
|
||||
{
|
||||
if (null == texture)
|
||||
return;
|
||||
var pixel = texture["pixel"] as EmChunk;
|
||||
if (null == pixel)
|
||||
return;
|
||||
var entry = new TexEntry {
|
||||
Name = name.ToString(),
|
||||
Type = "image",
|
||||
Offset = DataOffset + pixel.Offset,
|
||||
Size = (uint)pixel.Length,
|
||||
TexType = texture["type"].ToString(),
|
||||
Width = Convert.ToInt32 (texture["width"]),
|
||||
Height = Convert.ToInt32 (texture["height"]),
|
||||
TruncatedWidth = Convert.ToInt32 (texture["truncated_width"]),
|
||||
TruncatedHeight = Convert.ToInt32 (texture["truncated_height"]),
|
||||
};
|
||||
dir.Add (entry);
|
||||
}
|
||||
|
||||
void AddIconEntry (List<Entry> dir, object name, IDictionary icon_list)
|
||||
{
|
||||
if (null == icon_list)
|
||||
return;
|
||||
foreach (DictionaryEntry icon in icon_list)
|
||||
{
|
||||
var layer = icon.Value as IDictionary;
|
||||
var pixel = layer["pixel"] as EmChunk;
|
||||
if (null == pixel)
|
||||
continue;
|
||||
var entry = new TexEntry {
|
||||
Name = item.Key.ToString(),
|
||||
Type = "image",
|
||||
Offset = DataOffset + pixel.Offset,
|
||||
Size = (uint)pixel.Length,
|
||||
TexType = texture["type"].ToString(),
|
||||
Width = Convert.ToInt32 (texture["width"]),
|
||||
Height = Convert.ToInt32 (texture["height"]),
|
||||
TruncatedWidth = Convert.ToInt32 (texture["truncated_width"]),
|
||||
TruncatedHeight = Convert.ToInt32 (texture["truncated_height"]),
|
||||
Name = name.ToString()+'#'+icon.Key.ToString(),
|
||||
Type = "image",
|
||||
Offset = DataOffset + pixel.Offset,
|
||||
Size = (uint)pixel.Length,
|
||||
Width = Convert.ToInt32 (layer["width"]),
|
||||
Height = Convert.ToInt32 (layer["height"]),
|
||||
OffsetX = Convert.ToInt32 (layer["originX"]),
|
||||
OffsetY = Convert.ToInt32 (layer["originY"]),
|
||||
TexType = layer.Contains ("compress") ? layer["compress"].ToString() : "RGBA8",
|
||||
};
|
||||
entry.TruncatedWidth = entry.Width;
|
||||
entry.TruncatedHeight = entry.Height;
|
||||
dir.Add (entry);
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
int m_names;
|
||||
@@ -651,6 +691,8 @@ namespace GameRes.Formats.Emote
|
||||
ReadA8L8 (pixels, stride);
|
||||
else if ("RGBA4444" == m_info.TexType)
|
||||
ReadRgba4444 (pixels, stride);
|
||||
else if ("RL" == m_info.TexType)
|
||||
ReadRle (pixels, stride);
|
||||
else
|
||||
throw new NotImplementedException (string.Format ("PSB texture format '{0}' not implemented", m_info.TexType));
|
||||
return ImageData.Create (m_info, PixelFormats.Bgra32, null, pixels, stride);
|
||||
@@ -734,5 +776,28 @@ namespace GameRes.Formats.Emote
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReadRle (byte[] output, int dst_stride)
|
||||
{
|
||||
const int pixel_size = 4;
|
||||
m_input.Position = 0;
|
||||
int dst = 0;
|
||||
while (dst < output.Length)
|
||||
{
|
||||
int count = m_input.ReadUInt8();
|
||||
if (0 == (count & 0x80))
|
||||
{
|
||||
count = pixel_size * (count + 1);
|
||||
dst += m_input.Read (output, dst, count);
|
||||
}
|
||||
else
|
||||
{
|
||||
count = pixel_size * ((count & 0x7F) + 3);
|
||||
m_input.Read (output, dst, pixel_size);
|
||||
Binary.CopyOverlapped (output, dst, dst+pixel_size, count-pixel_size);
|
||||
dst += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,9 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using GameRes.Formats.Properties;
|
||||
using GameRes.Formats.Strings;
|
||||
using GameRes.Utility;
|
||||
@@ -136,8 +138,12 @@ namespace GameRes.Formats.Entis
|
||||
string GetArcPassword (string arc_name)
|
||||
{
|
||||
var title = FormatCatalog.Instance.LookupGame (arc_name, @"..\*.exe");
|
||||
string password = null;
|
||||
if (string.IsNullOrEmpty (title) || !KnownKeys.ContainsKey (title))
|
||||
{
|
||||
password = ExtractNoaPassword (arc_name);
|
||||
if (password != null)
|
||||
return password;
|
||||
var options = Query<NoaOptions> (arcStrings.ArcEncryptedNotice);
|
||||
if (!string.IsNullOrEmpty (options.PassPhrase))
|
||||
{
|
||||
@@ -145,7 +151,6 @@ namespace GameRes.Formats.Entis
|
||||
}
|
||||
title = options.Scheme;
|
||||
}
|
||||
string password = null;
|
||||
if (!string.IsNullOrEmpty (title))
|
||||
{
|
||||
Dictionary<string, string> filemap;
|
||||
@@ -158,6 +163,51 @@ namespace GameRes.Formats.Entis
|
||||
return password;
|
||||
}
|
||||
|
||||
string ExtractNoaPassword (string arc_name)
|
||||
{
|
||||
if (VFS.IsVirtual)
|
||||
return null;
|
||||
var dir = VFS.GetDirectoryName (arc_name);
|
||||
var noa_name = Path.GetFileName (arc_name);
|
||||
var parent_dir = Directory.GetParent (dir).FullName;
|
||||
var exe_files = VFS.GetFiles (VFS.CombinePath (parent_dir, "*.exe")).Concat (VFS.GetFiles (VFS.CombinePath (dir, "*.exe")));
|
||||
foreach (var exe_entry in exe_files)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var exe = new ExeFile.ResourceAccessor (exe_entry.Name))
|
||||
{
|
||||
var cotomi = exe.GetResource ("IDR_COTOMI", "#10");
|
||||
if (null == cotomi)
|
||||
continue;
|
||||
using (var res = new MemoryStream (cotomi))
|
||||
using (var input = DecodeNemesis (res))
|
||||
{
|
||||
var xml = new XmlDocument();
|
||||
xml.Load (input);
|
||||
var password = XmlFindArchiveKey (xml, noa_name);
|
||||
if (password != null)
|
||||
return password;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { /* ignore errors */ }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
string XmlFindArchiveKey (XmlDocument xml, string filename)
|
||||
{
|
||||
foreach (XmlNode archive in xml.DocumentElement.SelectNodes ("archive[@path and @key]"))
|
||||
{
|
||||
var attr = archive.Attributes;
|
||||
var path = attr["path"].Value;
|
||||
if (VFS.IsPathEqualsToFileName (path, filename))
|
||||
return attr["key"].Value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Stream DecodeNemesis (Stream input)
|
||||
{
|
||||
var decoder = new NemesisDecodeContext();
|
||||
|
||||
92
ArcFormats/Foster/ArcC24.cs
Normal file
92
ArcFormats/Foster/ArcC24.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
//! \file ArcC24.cs
|
||||
//! \date Sun Feb 19 14:21:21 2017
|
||||
//! \brief Foster's game engine multi-frame image.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.Foster
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class C24Opener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "C24"; } }
|
||||
public override string Description { get { return "Foster game engine multi-image"; } }
|
||||
public override uint Signature { get { return 0x00343243; } } // 'C24'
|
||||
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;
|
||||
|
||||
var base_name = Path.GetFileNameWithoutExtension (file.Name);
|
||||
var dir = new List<Entry> (count);
|
||||
uint index_offset = 8;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
uint offset = file.View.ReadUInt32 (index_offset);
|
||||
index_offset += 4;
|
||||
if (offset > 0 && offset <= file.MaxOffset)
|
||||
{
|
||||
var entry = new Entry
|
||||
{
|
||||
Name = string.Format ("{0}@{1:D4}", base_name, i),
|
||||
Type = "image",
|
||||
Offset = offset,
|
||||
};
|
||||
dir.Add (entry);
|
||||
}
|
||||
}
|
||||
dir.Sort ((a, b) => (int)(a.Offset - b.Offset));
|
||||
for (int i = 1; i < dir.Count; ++i)
|
||||
{
|
||||
dir[i-1].Size = (uint)(dir[i].Offset - dir[i-1].Offset);
|
||||
}
|
||||
var last_entry = dir[dir.Count-1];
|
||||
last_entry.Size = (uint)(file.MaxOffset - last_entry.Offset);
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
|
||||
{
|
||||
var offset = entry.Offset;
|
||||
var info = new C24MetaData
|
||||
{
|
||||
Width = arc.File.View.ReadUInt32 (offset),
|
||||
Height = arc.File.View.ReadUInt32 (offset+4),
|
||||
OffsetX = arc.File.View.ReadInt32 (offset+8),
|
||||
OffsetY = arc.File.View.ReadInt32 (offset+12),
|
||||
BPP = 24,
|
||||
DataOffset = (uint)(offset + 0x10),
|
||||
};
|
||||
var input = arc.File.CreateStream (0, (uint)arc.File.MaxOffset);
|
||||
return new C24Decoder (input, info);
|
||||
}
|
||||
}
|
||||
}
|
||||
246
ArcFormats/Foster/ArcFA2.cs
Normal file
246
ArcFormats/Foster/ArcFA2.cs
Normal file
@@ -0,0 +1,246 @@
|
||||
//! \file ArcFA2.cs
|
||||
//! \date Sun Feb 19 10:46:57 2017
|
||||
//! \brief Foster game engine resource archive.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Foster
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class Fa2Opener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "FA2"; } }
|
||||
public override string Description { get { return "Foster game engine resource archive"; } }
|
||||
public override uint Signature { get { return 0x00324146; } } // 'FA2'
|
||||
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 (0xC);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
bool is_packed = (file.View.ReadByte (4) & 1) != 0;
|
||||
uint index_offset = file.View.ReadUInt32 (8);
|
||||
byte[] index;
|
||||
using (var input = file.CreateStream (index_offset))
|
||||
{
|
||||
if (is_packed)
|
||||
index = Decompress (input, (uint)count * 0x20);
|
||||
else
|
||||
index = file.View.ReadBytes (index_offset, (uint)(file.MaxOffset - index_offset));
|
||||
}
|
||||
|
||||
uint data_offset = 0x10;
|
||||
int index_pos = 0;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var name = Binary.GetCString (index, index_pos, 0xF);
|
||||
index_pos += 0xF;
|
||||
var entry = FormatCatalog.Instance.Create<PackedEntry> (name);
|
||||
entry.IsPacked = (index[index_pos] & 2) != 0;
|
||||
entry.Offset = data_offset;
|
||||
index_pos += 9;
|
||||
entry.UnpackedSize = index.ToUInt32 (index_pos);
|
||||
entry.Size = index.ToUInt32 (index_pos+4);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
index_pos += 8;
|
||||
data_offset += (entry.Size + 0xFu) & ~0xFu;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
var pent = entry as PackedEntry;
|
||||
if (null == pent || !pent.IsPacked)
|
||||
return input;
|
||||
var data = Decompress (input, pent.UnpackedSize);
|
||||
return new BinMemoryStream (data, entry.Name);
|
||||
}
|
||||
|
||||
byte[] Decompress (IBinaryStream input, uint unpacked_size)
|
||||
{
|
||||
var comp = new Fa2Compression (input, unpacked_size);
|
||||
return comp.Unpack();
|
||||
}
|
||||
}
|
||||
|
||||
internal class Fa2Compression
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
byte[] m_output;
|
||||
|
||||
public Fa2Compression (IBinaryStream input, uint unpacked_size)
|
||||
{
|
||||
m_input = input;
|
||||
m_output = new byte[unpacked_size];
|
||||
}
|
||||
|
||||
public byte[] Unpack ()
|
||||
{
|
||||
m_bit_count = 0;
|
||||
int dst = 0;
|
||||
while (dst < m_output.Length)
|
||||
{
|
||||
if (GetNextBit() != 0)
|
||||
{
|
||||
m_output[dst++] = m_input.ReadUInt8();
|
||||
continue;
|
||||
}
|
||||
int offset;
|
||||
if (GetNextBit() != 0)
|
||||
{
|
||||
if (GetNextBit() != 0)
|
||||
{
|
||||
offset = m_input.ReadUInt8() << 3;
|
||||
offset |= GetBits (3);
|
||||
offset += 0x100;
|
||||
if (offset >= 0x8FF)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = m_input.ReadUInt8();
|
||||
}
|
||||
m_output[dst ] = m_output[dst-offset-1];
|
||||
m_output[dst+1] = m_output[dst-offset ];
|
||||
dst += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GetNextBit() != 0)
|
||||
{
|
||||
offset = m_input.ReadUInt8() << 1;
|
||||
offset |= GetNextBit();
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = 0x100;
|
||||
if (GetNextBit() != 0)
|
||||
{
|
||||
offset |= m_input.ReadUInt8();
|
||||
offset <<= 1;
|
||||
offset |= GetNextBit();
|
||||
}
|
||||
else if (GetNextBit() != 0)
|
||||
{
|
||||
offset |= m_input.ReadUInt8();
|
||||
offset <<= 2;
|
||||
offset |= GetBits (2);
|
||||
}
|
||||
else if (GetNextBit() != 0)
|
||||
{
|
||||
offset |= m_input.ReadUInt8();
|
||||
offset <<= 3;
|
||||
offset |= GetBits (3);
|
||||
}
|
||||
else
|
||||
{
|
||||
offset |= m_input.ReadUInt8();
|
||||
offset <<= 4;
|
||||
offset |= GetBits (4);
|
||||
}
|
||||
}
|
||||
int count = 0;
|
||||
if (GetNextBit() != 0)
|
||||
{
|
||||
count = 3;
|
||||
}
|
||||
else if (GetNextBit() != 0)
|
||||
{
|
||||
count = 4;
|
||||
}
|
||||
else if (GetNextBit() != 0)
|
||||
{
|
||||
count = 5 + GetNextBit();
|
||||
}
|
||||
else if (GetNextBit() != 0)
|
||||
{
|
||||
count = 7 + GetBits (2);
|
||||
}
|
||||
else if (GetNextBit() != 0)
|
||||
{
|
||||
count = 11 + GetBits (4);
|
||||
}
|
||||
else
|
||||
{
|
||||
count = 27 + m_input.ReadUInt8();
|
||||
}
|
||||
Binary.CopyOverlapped (m_output, dst - offset - 1, dst, count);
|
||||
dst += count;
|
||||
}
|
||||
}
|
||||
return m_output;
|
||||
}
|
||||
|
||||
uint m_bits;
|
||||
int m_bit_count;
|
||||
|
||||
void FetchBits ()
|
||||
{
|
||||
m_bits = m_input.ReadUInt32();
|
||||
m_bit_count = 32;
|
||||
}
|
||||
|
||||
int GetNextBit ()
|
||||
{
|
||||
if (0 == m_bit_count)
|
||||
FetchBits();
|
||||
int bit = (int)((m_bits >> 31) & 1);
|
||||
m_bits <<= 1;
|
||||
--m_bit_count;
|
||||
return bit;
|
||||
}
|
||||
|
||||
int GetBits (int count)
|
||||
{
|
||||
uint bits = 0;
|
||||
int avail_bits = Math.Min (count, m_bit_count);
|
||||
if (avail_bits > 0)
|
||||
{
|
||||
bits = m_bits >> (32 - avail_bits);
|
||||
m_bits <<= avail_bits;
|
||||
m_bit_count -= avail_bits;
|
||||
count -= avail_bits;
|
||||
}
|
||||
if (count > 0)
|
||||
{
|
||||
FetchBits();
|
||||
bits = bits << count | m_bits >> (32 - count);
|
||||
m_bits <<= count;
|
||||
m_bit_count -= count;
|
||||
}
|
||||
return (int)bits;
|
||||
}
|
||||
}
|
||||
}
|
||||
162
ArcFormats/Foster/ImageC24.cs
Normal file
162
ArcFormats/Foster/ImageC24.cs
Normal file
@@ -0,0 +1,162 @@
|
||||
//! \file ImageC24.cs
|
||||
//! \date Sun Feb 19 13:13:16 2017
|
||||
//! \brief Foster game engine image format.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace GameRes.Formats.Foster
|
||||
{
|
||||
internal class C24MetaData : ImageMetaData
|
||||
{
|
||||
public uint DataOffset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ShiinaRio S25 predecessor.
|
||||
/// </summary>
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class C24Format : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "C24"; } }
|
||||
public override string Description { get { return "Foster game engine image format"; } }
|
||||
public override uint Signature { get { return 0x00343243; } } // 'C24'
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (12);
|
||||
int count = header.ToInt32 (4);
|
||||
if (count <= 0)
|
||||
return null;
|
||||
file.Position = header.ToUInt32 (8);
|
||||
var info = new C24MetaData { BPP = 24 };
|
||||
info.Width = file.ReadUInt32();
|
||||
info.Height = file.ReadUInt32();
|
||||
info.OffsetX = file.ReadInt32();
|
||||
info.OffsetY = file.ReadInt32();
|
||||
info.DataOffset = (uint)file.Position;
|
||||
return info;
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
using (var reader = new C24Decoder (file, (C24MetaData)info, true))
|
||||
return reader.Image;
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("C24Format.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class C24Decoder : IImageDecoder
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
C24MetaData m_info;
|
||||
byte[] m_output;
|
||||
bool m_should_dispose;
|
||||
ImageData m_image;
|
||||
|
||||
public Stream Source { get { m_input.Position = 0; return m_input.AsStream; } }
|
||||
public ImageFormat SourceFormat { get { return null; } }
|
||||
public ImageMetaData Info { get { return m_info; } }
|
||||
|
||||
public ImageData Image
|
||||
{
|
||||
get
|
||||
{
|
||||
if (null == m_image)
|
||||
{
|
||||
var pixels = Unpack();
|
||||
m_image = ImageData.Create (m_info, PixelFormats.Bgr24, null, pixels);
|
||||
}
|
||||
return m_image;
|
||||
}
|
||||
}
|
||||
|
||||
public C24Decoder (IBinaryStream file, C24MetaData info, bool leave_open = false)
|
||||
{
|
||||
m_input = file;
|
||||
m_info = info;
|
||||
m_output = new byte[3 * m_info.Width * m_info.Height];
|
||||
m_should_dispose = !leave_open;
|
||||
}
|
||||
|
||||
public byte[] Unpack ()
|
||||
{
|
||||
m_input.Position = m_info.DataOffset;
|
||||
var rows = new uint[m_info.Height];
|
||||
for (int i = 0; i < rows.Length; ++i)
|
||||
rows[i] = m_input.ReadUInt32();
|
||||
int dst = 0;
|
||||
int width = (int)m_info.Width;
|
||||
foreach (uint row_offset in rows)
|
||||
{
|
||||
m_input.Position = row_offset;
|
||||
bool rle = false;
|
||||
for (int x = 0; x < width; )
|
||||
{
|
||||
int count = m_input.ReadUInt8();
|
||||
if (!rle)
|
||||
{
|
||||
if (0xFF == count)
|
||||
count = m_input.ReadUInt16();
|
||||
int byte_count = count * 3;
|
||||
for (int i = 0; i < byte_count; ++i)
|
||||
m_output[dst++] = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (0 == count)
|
||||
count = m_input.ReadUInt16();
|
||||
int byte_count = count * 3;
|
||||
m_input.Read (m_output, dst, byte_count);
|
||||
dst += byte_count;
|
||||
}
|
||||
x += count;
|
||||
rle = !rle;
|
||||
}
|
||||
}
|
||||
return m_output;
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
bool m_disposed = false;
|
||||
public void Dispose ()
|
||||
{
|
||||
if (!m_disposed)
|
||||
{
|
||||
if (m_should_dispose)
|
||||
{
|
||||
m_input.Dispose();
|
||||
}
|
||||
m_disposed = true;
|
||||
}
|
||||
System.GC.SuppressFinalize (this);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -96,7 +96,7 @@ namespace GameRes.Formats.GameSystem
|
||||
var cent = (ChrEntry)entry;
|
||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
if (cent.Info is ChrMetaData)
|
||||
return new ChrDecoder (input, cent.Info);
|
||||
return new ChrDecoder (input, cent.Info as ChrMetaData);
|
||||
else
|
||||
return new ChrFrameDecoder (input, cent.Info);
|
||||
}
|
||||
@@ -109,16 +109,15 @@ namespace GameRes.Formats.GameSystem
|
||||
public ImageMetaData Info;
|
||||
}
|
||||
|
||||
internal class ChrDecoder : BinaryImageDecoder
|
||||
internal class ChrDecoder : ChrReader
|
||||
{
|
||||
public ChrDecoder (IBinaryStream input, ImageMetaData info) : base (input, info)
|
||||
public ChrDecoder (IBinaryStream input, ChrMetaData info) : base (input, info)
|
||||
{ }
|
||||
|
||||
protected override ImageData GetImageData ()
|
||||
{
|
||||
var reader = new ChrReader (m_input, (ChrMetaData)Info);
|
||||
var pixels = reader.UnpackBaseline();
|
||||
return ImageData.CreateFlipped (Info, PixelFormats.Bgra32, null, pixels, reader.Stride);
|
||||
var pixels = UnpackBaseline();
|
||||
return ImageData.CreateFlipped (Info, PixelFormats.Bgra32, null, pixels, Stride);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,8 @@ namespace GameRes.Formats.GameSystem
|
||||
long next_offset = (long)file.View.ReadUInt32 (index_offset+12) << 9;
|
||||
if (next_offset < offset || next_offset > file.MaxOffset)
|
||||
return null;
|
||||
if (name.EndsWith (".CRGB") || name.EndsWith (".CHAR"))
|
||||
entry.Type = "image";
|
||||
entry.Offset = offset;
|
||||
entry.Size = (uint)(next_offset - offset);
|
||||
dir.Add (entry);
|
||||
@@ -101,5 +103,25 @@ namespace GameRes.Formats.GameSystem
|
||||
var ext = Encoding.ASCII.GetString (name_buf, 12, ext_end-12);
|
||||
return name + '.' + ext;
|
||||
}
|
||||
|
||||
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
|
||||
{
|
||||
if (entry.Name.EndsWith (".BGD") || entry.Name.EndsWith (".CRGB"))
|
||||
{
|
||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
var info = new ImageMetaData { Width = 800, Height = 600, BPP = 24 };
|
||||
return new CgdReader (input, info);
|
||||
}
|
||||
else if (entry.Name.EndsWith (".CHAR"))
|
||||
{
|
||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
var info = new ChrMetaData {
|
||||
Width = 800, Height = 600, BPP = 32,
|
||||
DataOffset = 0, RgbSize = (int)input.Length,
|
||||
};
|
||||
return new ChrReader (input, info);
|
||||
}
|
||||
return base.OpenImage (arc, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
201
ArcFormats/GameSystem/ArcPureMail.cs
Normal file
201
ArcFormats/GameSystem/ArcPureMail.cs
Normal file
@@ -0,0 +1,201 @@
|
||||
//! \file ArcPureMail.cs
|
||||
//! \date Sun Feb 12 04:18:56 2017
|
||||
//! \brief 0verflow Game System resource archive.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace GameRes.Formats.GameSystem
|
||||
{
|
||||
internal class PmDatEntry : PackedEntry
|
||||
{
|
||||
public bool StoredSize;
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class PmDatOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "DAT/PUREMAIL"; } }
|
||||
public override string Description { get { return "PureMail 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 ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (file.MaxOffset <= 12 || file.MaxOffset > uint.MaxValue)
|
||||
return null;
|
||||
uint packed_size = file.View.ReadUInt32 (file.MaxOffset-12) ^ 0xF0F0F0F0;
|
||||
uint unpacked_size = file.View.ReadUInt32 (file.MaxOffset-8) ^ 0xF0F0F0F0;
|
||||
const uint entry_record_size = 0x50;
|
||||
int count = (int)(unpacked_size / entry_record_size);
|
||||
if (unpacked_size % entry_record_size != 0 || packed_size >= file.MaxOffset || !IsSaneCount (count))
|
||||
return null;
|
||||
|
||||
var unpacked = new byte[unpacked_size];
|
||||
using (var packed = file.CreateStream (file.MaxOffset-12-packed_size, packed_size))
|
||||
LzUnpack (packed, unpacked);
|
||||
using (var index = new BinMemoryStream (unpacked))
|
||||
{
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
int flags = index.ReadInt32();
|
||||
var name = index.ReadCString (0x40);
|
||||
var entry = FormatCatalog.Instance.Create<PmDatEntry> (name);
|
||||
entry.Offset = index.ReadUInt32();
|
||||
entry.Size = index.ReadUInt32();
|
||||
entry.UnpackedSize = index.ReadUInt32();
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
if (entry.Name.EndsWith (".CRGB", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
entry.Name.EndsWith (".CHAR", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
entry.Name.EndsWith (".rol", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
entry.Name.EndsWith (".edg", StringComparison.InvariantCultureIgnoreCase))
|
||||
entry.Type = "image";
|
||||
entry.IsPacked = (flags & 0xFF0000) != 0;
|
||||
entry.StoredSize = (flags & 0x2000000) != 0;
|
||||
dir.Add (entry);
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var pent = entry as PmDatEntry;
|
||||
if (null == pent || !pent.IsPacked)
|
||||
return base.OpenEntry (arc, entry);
|
||||
using (var input = arc.File.CreateStream (entry.Offset, entry.Size))
|
||||
{
|
||||
var unpacked_size = pent.UnpackedSize;
|
||||
if (pent.StoredSize)
|
||||
{
|
||||
unpacked_size = input.ReadUInt32();
|
||||
pent.UnpackedSize = unpacked_size;
|
||||
}
|
||||
var output = new byte[unpacked_size];
|
||||
LzUnpack (input, output);
|
||||
return new BinMemoryStream (output);
|
||||
}
|
||||
}
|
||||
|
||||
void LzUnpack (IBinaryStream input, byte[] output)
|
||||
{
|
||||
var frame = new byte[0x1000];
|
||||
int dst = 0;
|
||||
int bits = 0;
|
||||
int mask = 0;
|
||||
int frame_pos = 0xFEE;
|
||||
while (dst < output.Length)
|
||||
{
|
||||
mask >>= 1;
|
||||
if (0 == mask)
|
||||
{
|
||||
bits = input.ReadByte();
|
||||
if (-1 == bits)
|
||||
break;
|
||||
mask = 0x80;
|
||||
}
|
||||
if (0 == (bits & mask))
|
||||
{
|
||||
int b = input.ReadByte();
|
||||
if (-1 == b)
|
||||
break;
|
||||
output[dst++] = (byte)b;
|
||||
frame[frame_pos++ & 0xFFF] = (byte)b;
|
||||
}
|
||||
else
|
||||
{
|
||||
int offset = input.ReadUInt16();
|
||||
int count = (offset & 0xF) + 3;
|
||||
offset >>= 4;
|
||||
while (count --> 0 && dst < output.Length)
|
||||
{
|
||||
byte v = frame[offset++ & 0xFFF];
|
||||
frame[frame_pos++ & 0xFFF] = v;
|
||||
output[dst++] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
|
||||
{
|
||||
if (entry.Name.EndsWith (".BGD", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
entry.Name.EndsWith (".CRGB", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
var input = OpenEntry (arc, entry);
|
||||
var info = new ImageMetaData { Width = 800, Height = 600, BPP = 24 };
|
||||
return new CgdReader (BinaryStream.FromStream (input, entry.Name), info);
|
||||
}
|
||||
else if (entry.Name.EndsWith (".edg", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
var input = OpenEntry (arc, entry);
|
||||
var info = new ImageMetaData { Width = 460, Height = 345, BPP = 24 };
|
||||
return new ImgReader (BinaryStream.FromStream (input, entry.Name), info);
|
||||
}
|
||||
else if (entry.Name.EndsWith (".rol", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
var input = OpenEntry (arc, entry);
|
||||
uint width = 202;
|
||||
uint height = (uint)input.Length / (((width * 3u) + 3u) & ~3u);
|
||||
var info = new ImageMetaData { Width = width, Height = height, BPP = 24 };
|
||||
return new ImgReader (BinaryStream.FromStream (input, entry.Name), info);
|
||||
}
|
||||
else if (entry.Name.EndsWith (".CHAR", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
var input = OpenEntry (arc, entry);
|
||||
var info = new ChrMetaData {
|
||||
Width = 800, Height = 600, BPP = 32,
|
||||
DataOffset = 0, RgbSize = (int)input.Length,
|
||||
};
|
||||
return new ChrReader (BinaryStream.FromStream (input, entry.Name), info);
|
||||
}
|
||||
return base.OpenImage (arc, entry);
|
||||
}
|
||||
}
|
||||
|
||||
internal class ImgReader : BinaryImageDecoder
|
||||
{
|
||||
public ImgReader (IBinaryStream input, ImageMetaData info) : base (input, info)
|
||||
{
|
||||
}
|
||||
|
||||
protected override ImageData GetImageData ()
|
||||
{
|
||||
m_input.Position = 0;
|
||||
int stride = (int)Info.Width * Info.BPP / 8;
|
||||
stride = (stride + 3) & ~3;
|
||||
int total = stride * (int)Info.Height;
|
||||
var pixels = m_input.ReadBytes (total);
|
||||
if (pixels.Length != total)
|
||||
throw new EndOfStreamException();
|
||||
return ImageData.CreateFlipped (Info, PixelFormats.Bgr24, null, pixels, stride);
|
||||
}
|
||||
}
|
||||
}
|
||||
331
ArcFormats/GameSystem/AudioADP4.cs
Normal file
331
ArcFormats/GameSystem/AudioADP4.cs
Normal file
@@ -0,0 +1,331 @@
|
||||
//! \file AudioADP4.cs
|
||||
//! \date Fri Feb 24 14:14:17 2017
|
||||
//! \brief 'Game System' compressed audio format.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.GameSystem
|
||||
{
|
||||
[Export(typeof(AudioFormat))]
|
||||
public class Adp4Audio : AudioFormat
|
||||
{
|
||||
public override string Tag { get { return "ADP4"; } }
|
||||
public override string Description { get { return "'GameSystem' compressed audio"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public override SoundInput TryOpen (IBinaryStream file)
|
||||
{
|
||||
bool is_adp4 = file.Name.EndsWith (".adp4", StringComparison.InvariantCultureIgnoreCase);
|
||||
if (!is_adp4 || file.Length <= 4)
|
||||
return null;
|
||||
var decoder = new AdpDecoder (file);
|
||||
var pcm = decoder.Decode();
|
||||
var format = new WaveFormat {
|
||||
FormatTag = 1,
|
||||
Channels = 2,
|
||||
SamplesPerSecond = 44100, // XXX varies
|
||||
BlockAlign = 4,
|
||||
BitsPerSample = 16,
|
||||
};
|
||||
format.SetBPS();
|
||||
var input = new MemoryStream (pcm);
|
||||
return new RawPcmInput (input, format);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class AdpDecoder
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
|
||||
public AdpDecoder (IBinaryStream input)
|
||||
{
|
||||
m_input = input;
|
||||
}
|
||||
|
||||
byte[] m_output;
|
||||
|
||||
public byte[] Decode ()
|
||||
{
|
||||
m_input.Position = 0;
|
||||
int sample_count = m_input.ReadInt32();
|
||||
m_output = new byte[sample_count * 8];
|
||||
DecodeBits (sample_count);
|
||||
return m_output;
|
||||
}
|
||||
|
||||
void DecodeBits (int sample_count)
|
||||
{
|
||||
bool copy_stereo = true;
|
||||
uint last_sample = 0;
|
||||
short sample = 0;
|
||||
uint key = 0;
|
||||
int dst = 0;
|
||||
int bits = 0;
|
||||
while (dst < m_output.Length)
|
||||
{
|
||||
if (bits < 0x100)
|
||||
{
|
||||
bits = m_input.ReadUInt8() | 0x8000;
|
||||
}
|
||||
int count = m_input.ReadUInt8();
|
||||
if (count < 0x80)
|
||||
count = m_input.ReadUInt8() | count << 8;
|
||||
else
|
||||
count &= 0x7F;
|
||||
count++;
|
||||
if (0 == (bits & 1))
|
||||
{
|
||||
do
|
||||
{
|
||||
if (dst >= m_output.Length)
|
||||
return;
|
||||
uint data_bits = m_input.ReadUInt8() ^ key++;
|
||||
|
||||
last_sample += data_bits & 0xF;
|
||||
int s = AdpSamples[last_sample] + sample;
|
||||
last_sample = AdpAdjust[last_sample];
|
||||
sample = Clamp (s);
|
||||
LittleEndian.Pack (sample, m_output, dst);
|
||||
dst += 2;
|
||||
if (copy_stereo)
|
||||
LittleEndian.Pack (sample, m_output, dst);
|
||||
dst += 2;
|
||||
|
||||
last_sample += (data_bits >> 4) & 0xF;
|
||||
s = AdpSamples[last_sample] + sample;
|
||||
last_sample = AdpAdjust[last_sample];
|
||||
sample = Clamp (s);
|
||||
LittleEndian.Pack (sample, m_output, dst);
|
||||
dst += 2;
|
||||
if (copy_stereo)
|
||||
LittleEndian.Pack (sample, m_output, dst);
|
||||
dst += 2;
|
||||
}
|
||||
while (--count > 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
last_sample = 0;
|
||||
sample = 0;
|
||||
dst += count * 8;
|
||||
}
|
||||
bits >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
static short Clamp (int sample)
|
||||
{
|
||||
if (sample > 0x7FFF)
|
||||
sample = 0x7FFF;
|
||||
else if (sample < -32768)
|
||||
sample = -32768;
|
||||
return (short)sample;
|
||||
}
|
||||
|
||||
static readonly int[] AdpSamples = {
|
||||
0, 2, 4, 6, 7, 9, 11, 13, 0, -2, -4, -6, -7, -9, -11, -13,
|
||||
1, 3, 5, 7, 9, 11, 13, 15, -1, -3, -5, -7, -9, -11, -13, -15,
|
||||
1, 3, 5, 7, 10, 12, 14, 16, -1, -3, -5, -7, -10, -12, -14, -16,
|
||||
1, 3, 6, 8, 11, 13, 16, 18, -1, -3, -6, -8, -11, -13, -16, -18,
|
||||
1, 4, 6, 9, 12, 15, 17, 20, -1, -4, -6, -9, -12, -15, -17, -20,
|
||||
1, 4, 7, 10, 13, 16, 19, 22, -1, -4, -7, -10, -13, -16, -19, -22,
|
||||
1, 4, 8, 11, 14, 17, 21, 24, -1, -4, -8, -11, -14, -17, -21, -24,
|
||||
1, 5, 8, 12, 15, 19, 22, 26, -1, -5, -8, -12, -15, -19, -22, -26,
|
||||
2, 6, 10, 14, 18, 22, 26, 30, -2, -6, -10, -14, -18, -22, -26, -30,
|
||||
2, 6, 10, 14, 19, 23, 27, 31, -2, -6, -10, -14, -19, -23, -27, -31,
|
||||
2, 7, 11, 16, 21, 26, 30, 35, -2, -7, -11, -16, -21, -26, -30, -35,
|
||||
2, 7, 13, 18, 23, 28, 34, 39, -2, -7, -13, -18, -23, -28, -34, -39,
|
||||
2, 8, 14, 20, 25, 31, 37, 43, -2, -8, -14, -20, -25, -31, -37, -43,
|
||||
3, 9, 15, 21, 28, 34, 40, 46, -3, -9, -15, -21, -28, -34, -40, -46,
|
||||
3, 10, 17, 24, 31, 38, 45, 52, -3, -10, -17, -24, -31, -38, -45, -52,
|
||||
3, 11, 19, 27, 34, 42, 50, 58, -3, -11, -19, -27, -34, -42, -50, -58,
|
||||
4, 12, 21, 29, 38, 46, 55, 63, -4, -12, -21, -29, -38, -46, -55, -63,
|
||||
4, 13, 23, 32, 41, 50, 60, 69, -4, -13, -23, -32, -41, -50, -60, -69,
|
||||
5, 15, 25, 35, 46, 56, 66, 76, -5, -15, -25, -35, -46, -56, -66, -76,
|
||||
5, 16, 28, 39, 50, 61, 73, 84, -5, -16, -28, -39, -50, -61, -73, -84,
|
||||
6, 18, 31, 43, 56, 68, 81, 93, -6, -18, -31, -43, -56, -68, -81, -93,
|
||||
6, 20, 34, 48, 61, 75, 89, 103, -6, -20, -34, -48, -61, -75, -89, -103,
|
||||
7, 22, 37, 52, 67, 82, 97, 112, -7, -22, -37, -52, -67, -82, -97, -112,
|
||||
8, 24, 41, 57, 74, 90, 107, 123, -8, -24, -41, -57, -74, -90, -107, -123,
|
||||
9, 27, 45, 63, 82, 100, 118, 136, -9, -27, -45, -63, -82, -100, -118, -136,
|
||||
10, 30, 50, 70, 90, 110, 130, 150, -10, -30, -50, -70, -90, -110, -130, -150,
|
||||
11, 33, 55, 77, 99, 121, 143, 165, -11, -33, -55, -77, -99, -121, -143, -165,
|
||||
12, 36, 60, 84, 109, 133, 157, 181, -12, -36, -60, -84, -109, -133, -157, -181,
|
||||
13, 40, 66, 93, 120, 147, 173, 200, -13, -40, -66, -93, -120, -147, -173, -200,
|
||||
14, 44, 73, 103, 132, 162, 191, 221, -14, -44, -73, -103, -132, -162, -191, -221,
|
||||
16, 48, 81, 113, 146, 178, 211, 243, -16, -48, -81, -113, -146, -178, -211, -243,
|
||||
17, 53, 89, 125, 160, 196, 232, 268, -17, -53, -89, -125, -160, -196, -232, -268,
|
||||
19, 58, 98, 137, 176, 215, 255, 294, -19, -58, -98, -137, -176, -215, -255, -294,
|
||||
21, 64, 108, 151, 194, 237, 281, 324, -21, -64, -108, -151, -194, -237, -281, -324,
|
||||
23, 71, 118, 166, 213, 261, 308, 356, -23, -71, -118, -166, -213, -261, -308, -356,
|
||||
26, 78, 130, 182, 235, 287, 339, 391, -26, -78, -130, -182, -235, -287, -339, -391,
|
||||
28, 86, 143, 201, 258, 316, 373, 431, -28, -86, -143, -201, -258, -316, -373, -431,
|
||||
31, 94, 158, 221, 284, 347, 411, 474, -31, -94, -158, -221, -284, -347, -411, -474,
|
||||
34, 104, 174, 244, 313, 383, 453, 523, -34, -104, -174, -244, -313, -383, -453, -523,
|
||||
38, 115, 191, 268, 345, 422, 498, 575, -38, -115, -191, -268, -345, -422, -498, -575,
|
||||
42, 126, 210, 294, 379, 463, 547, 631, -42, -126, -210, -294, -379, -463, -547, -631,
|
||||
46, 139, 231, 324, 417, 510, 602, 695, -46, -139, -231, -324, -417, -510, -602, -695,
|
||||
51, 153, 255, 357, 459, 561, 663, 765, -51, -153, -255, -357, -459, -561, -663, -765,
|
||||
56, 168, 280, 392, 505, 617, 729, 841, -56, -168, -280, -392, -505, -617, -729, -841,
|
||||
61, 185, 308, 432, 555, 679, 802, 926, -61, -185, -308, -432, -555, -679, -802, -926,
|
||||
68, 204, 340, 476, 612, 748, 884, 1020, -68, -204, -340, -476, -612, -748, -884, -1020,
|
||||
74, 224, 373, 523, 672, 822, 971, 1121, -74, -224, -373, -523, -672, -822, -971, -1121,
|
||||
82, 246, 411, 575, 740, 904, 1069, 1233, -82, -246, -411, -575, -740, -904, -1069, -1233,
|
||||
90, 271, 452, 633, 814, 995, 1176, 1357, -90, -271, -452, -633, -814, -995, -1176, -1357,
|
||||
99, 298, 497, 696, 895, 1094, 1293, 1492, -99, -298, -497, -696, -895, -1094, -1293, -1492,
|
||||
109, 328, 547, 766, 985, 1204, 1423, 1642, -109, -328, -547, -766, -985, -1204, -1423, -1642,
|
||||
120, 361, 601, 842, 1083, 1324, 1564, 1805, -120, -361, -601, -842, -1083, -1324, -1564, -1805,
|
||||
132, 397, 662, 927, 1192, 1457, 1722, 1987, -132, -397, -662, -927, -1192, -1457, -1722, -1987,
|
||||
145, 437, 728, 1020, 1311, 1603, 1894, 2186, -145, -437, -728, -1020, -1311, -1603, -1894, -2186,
|
||||
160, 480, 801, 1121, 1442, 1762, 2083, 2403, -160, -480, -801, -1121, -1442, -1762, -2083, -2403,
|
||||
176, 529, 881, 1234, 1587, 1940, 2292, 2645, -176, -529, -881, -1234, -1587, -1940, -2292, -2645,
|
||||
194, 582, 970, 1358, 1746, 2134, 2522, 2910, -194, -582, -970, -1358, -1746, -2134, -2522, -2910,
|
||||
213, 640, 1066, 1493, 1920, 2347, 2773, 3200, -213, -640, -1066, -1493, -1920, -2347, -2773, -3200,
|
||||
234, 704, 1173, 1643, 2112, 2582, 3051, 3521, -234, -704, -1173, -1643, -2112, -2582, -3051, -3521,
|
||||
258, 774, 1291, 1807, 2324, 2840, 3357, 3873, -258, -774, -1291, -1807, -2324, -2840, -3357, -3873,
|
||||
284, 852, 1420, 1988, 2556, 3124, 3692, 4260, -284, -852, -1420, -1988, -2556, -3124, -3692, -4260,
|
||||
312, 937, 1561, 2186, 2811, 3436, 4060, 4685, -312, -937, -1561, -2186, -2811, -3436, -4060, -4685,
|
||||
343, 1030, 1718, 2405, 3092, 3779, 4467, 5154, -343, -1030, -1718, -2405, -3092, -3779, -4467, -5154,
|
||||
378, 1134, 1890, 2646, 3402, 4158, 4914, 5670, -378, -1134, -1890, -2646, -3402, -4158, -4914, -5670,
|
||||
415, 1247, 2079, 2911, 3742, 4574, 5406, 6238, -415, -1247, -2079, -2911, -3742, -4574, -5406, -6238,
|
||||
457, 1372, 2287, 3202, 4117, 5032, 5947, 6862, -457, -1372, -2287, -3202, -4117, -5032, -5947, -6862,
|
||||
503, 1509, 2516, 3522, 4529, 5535, 6542, 7548, -503, -1509, -2516, -3522, -4529, -5535, -6542, -7548,
|
||||
553, 1660, 2767, 3874, 4981, 6088, 7195, 8302, -553, -1660, -2767, -3874, -4981, -6088, -7195, -8302,
|
||||
608, 1826, 3044, 4262, 5479, 6697, 7915, 9133, -608, -1826, -3044, -4262, -5479, -6697, -7915, -9133,
|
||||
669, 2009, 3348, 4688, 6027, 7367, 8706, 10046, -669, -2009, -3348, -4688, -6027, -7367, -8706, -10046,
|
||||
736, 2210, 3683, 5157, 6630, 8104, 9577, 11051, -736, -2210, -3683, -5157, -6630, -8104, -9577, -11051,
|
||||
810, 2431, 4052, 5673, 7294, 8915, 10536, 12157, -810, -2431, -4052, -5673, -7294, -8915, -10536, -12157,
|
||||
891, 2674, 4457, 6240, 8023, 9806, 11589, 13372, -891, -2674, -4457, -6240, -8023, -9806, -11589, -13372,
|
||||
980, 2941, 4903, 6864, 8825, 10786, 12748, 14709, -980, -2941, -4903, -6864, -8825, -10786, -12748, -14709,
|
||||
1078, 3236, 5393, 7551, 9708, 11866, 14023, 16181, -1078, -3236, -5393, -7551, -9708, -11866, -14023, -16181,
|
||||
1186, 3559, 5933, 8306, 10679, 13052, 15426, 17799, -1186, -3559, -5933, -8306, -10679, -13052, -15426, -17799,
|
||||
1305, 3915, 6526, 9136, 11747, 14357, 16968, 19578, -1305, -3915, -6526, -9136, -11747, -14357, -16968, -19578,
|
||||
1435, 4307, 7179, 10051, 12922, 15794, 18666, 21538, -1435, -4307, -7179, -10051, -12922, -15794, -18666, -21538,
|
||||
1579, 4738, 7896, 11055, 14214, 17373, 20531, 23690, -1579, -4738, -7896, -11055, -14214, -17373, -20531, -23690,
|
||||
1737, 5212, 8686, 12161, 15636, 19111, 22585, 26060, -1737, -5212, -8686, -12161, -15636, -19111, -22585, -26060,
|
||||
1911, 5733, 9555, 13377, 17200, 21022, 24844, 28666, -1911, -5733, -9555, -13377, -17200, -21022, -24844, -28666,
|
||||
2102, 6306, 10511, 14715, 18920, 23124, 27329, 31533, -2102, -6306, -10511, -14715, -18920, -23124, -27329, -31533,
|
||||
2312, 6937, 11562, 16187, 20812, 25437, 30062, 34687, -2312, -6937, -11562, -16187, -20812, -25437, -30062, -34687,
|
||||
2543, 7631, 12718, 17806, 22893, 27981, 33068, 38156, -2543, -7631, -12718, -17806, -22893, -27981, -33068, -38156,
|
||||
2798, 8394, 13990, 19586, 25183, 30779, 36375, 41971, -2798, -8394, -13990, -19586, -25183, -30779, -36375, -41971,
|
||||
3077, 9233, 15389, 21545, 27700, 33856, 40012, 46168, -3077, -9233, -15389, -21545, -27700, -33856, -40012, -46168,
|
||||
3385, 10157, 16928, 23700, 30471, 37243, 44014, 50786, -3385, -10157, -16928, -23700, -30471, -37243, -44014, -50786,
|
||||
3724, 11172, 18621, 26069, 33518, 40966, 48415, 55863, -3724, -11172, -18621, -26069, -33518, -40966, -48415, -55863,
|
||||
};
|
||||
static readonly ushort[] AdpAdjust = {
|
||||
0, 0, 0, 0, 32, 64, 96, 128, 0, 0, 0, 0, 32, 64, 96, 128,
|
||||
0, 0, 0, 0, 48, 80, 112, 144, 0, 0, 0, 0, 48, 80, 112, 144,
|
||||
16, 16, 16, 16, 64, 96, 128, 160, 16, 16, 16, 16, 64, 96, 128, 160,
|
||||
32, 32, 32, 32, 80, 112, 144, 176, 32, 32, 32, 32, 80, 112, 144, 176,
|
||||
48, 48, 48, 48, 96, 128, 160, 192, 48, 48, 48, 48, 96, 128, 160, 192,
|
||||
64, 64, 64, 64, 112, 144, 176, 208, 64, 64, 64, 64, 112, 144, 176, 208,
|
||||
80, 80, 80, 80, 128, 160, 192, 224, 80, 80, 80, 80, 128, 160, 192, 224,
|
||||
96, 96, 96, 96, 144, 176, 208, 240, 96, 96, 96, 96, 144, 176, 208, 240,
|
||||
112, 112, 112, 112, 160, 192, 224, 256, 112, 112, 112, 112, 160, 192, 224, 256,
|
||||
128, 128, 128, 128, 176, 208, 240, 272, 128, 128, 128, 128, 176, 208, 240, 272,
|
||||
144, 144, 144, 144, 192, 224, 256, 288, 144, 144, 144, 144, 192, 224, 256, 288,
|
||||
160, 160, 160, 160, 208, 240, 272, 304, 160, 160, 160, 160, 208, 240, 272, 304,
|
||||
176, 176, 176, 176, 224, 256, 288, 320, 176, 176, 176, 176, 224, 256, 288, 320,
|
||||
192, 192, 192, 192, 240, 272, 304, 336, 192, 192, 192, 192, 240, 272, 304, 336,
|
||||
208, 208, 208, 208, 256, 288, 320, 352, 208, 208, 208, 208, 256, 288, 320, 352,
|
||||
224, 224, 224, 224, 272, 304, 336, 368, 224, 224, 224, 224, 272, 304, 336, 368,
|
||||
240, 240, 240, 240, 288, 320, 352, 384, 240, 240, 240, 240, 288, 320, 352, 384,
|
||||
256, 256, 256, 256, 304, 336, 368, 400, 256, 256, 256, 256, 304, 336, 368, 400,
|
||||
272, 272, 272, 272, 320, 352, 384, 416, 272, 272, 272, 272, 320, 352, 384, 416,
|
||||
288, 288, 288, 288, 336, 368, 400, 432, 288, 288, 288, 288, 336, 368, 400, 432,
|
||||
304, 304, 304, 304, 352, 384, 416, 448, 304, 304, 304, 304, 352, 384, 416, 448,
|
||||
320, 320, 320, 320, 368, 400, 432, 464, 320, 320, 320, 320, 368, 400, 432, 464,
|
||||
336, 336, 336, 336, 384, 416, 448, 480, 336, 336, 336, 336, 384, 416, 448, 480,
|
||||
352, 352, 352, 352, 400, 432, 464, 496, 352, 352, 352, 352, 400, 432, 464, 496,
|
||||
368, 368, 368, 368, 416, 448, 480, 512, 368, 368, 368, 368, 416, 448, 480, 512,
|
||||
384, 384, 384, 384, 432, 464, 496, 528, 384, 384, 384, 384, 432, 464, 496, 528,
|
||||
400, 400, 400, 400, 448, 480, 512, 544, 400, 400, 400, 400, 448, 480, 512, 544,
|
||||
416, 416, 416, 416, 464, 496, 528, 560, 416, 416, 416, 416, 464, 496, 528, 560,
|
||||
432, 432, 432, 432, 480, 512, 544, 576, 432, 432, 432, 432, 480, 512, 544, 576,
|
||||
448, 448, 448, 448, 496, 528, 560, 592, 448, 448, 448, 448, 496, 528, 560, 592,
|
||||
464, 464, 464, 464, 512, 544, 576, 608, 464, 464, 464, 464, 512, 544, 576, 608,
|
||||
480, 480, 480, 480, 528, 560, 592, 624, 480, 480, 480, 480, 528, 560, 592, 624,
|
||||
496, 496, 496, 496, 544, 576, 608, 640, 496, 496, 496, 496, 544, 576, 608, 640,
|
||||
512, 512, 512, 512, 560, 592, 624, 656, 512, 512, 512, 512, 560, 592, 624, 656,
|
||||
528, 528, 528, 528, 576, 608, 640, 672, 528, 528, 528, 528, 576, 608, 640, 672,
|
||||
544, 544, 544, 544, 592, 624, 656, 688, 544, 544, 544, 544, 592, 624, 656, 688,
|
||||
560, 560, 560, 560, 608, 640, 672, 704, 560, 560, 560, 560, 608, 640, 672, 704,
|
||||
576, 576, 576, 576, 624, 656, 688, 720, 576, 576, 576, 576, 624, 656, 688, 720,
|
||||
592, 592, 592, 592, 640, 672, 704, 736, 592, 592, 592, 592, 640, 672, 704, 736,
|
||||
608, 608, 608, 608, 656, 688, 720, 752, 608, 608, 608, 608, 656, 688, 720, 752,
|
||||
624, 624, 624, 624, 672, 704, 736, 768, 624, 624, 624, 624, 672, 704, 736, 768,
|
||||
640, 640, 640, 640, 688, 720, 752, 784, 640, 640, 640, 640, 688, 720, 752, 784,
|
||||
656, 656, 656, 656, 704, 736, 768, 800, 656, 656, 656, 656, 704, 736, 768, 800,
|
||||
672, 672, 672, 672, 720, 752, 784, 816, 672, 672, 672, 672, 720, 752, 784, 816,
|
||||
688, 688, 688, 688, 736, 768, 800, 832, 688, 688, 688, 688, 736, 768, 800, 832,
|
||||
704, 704, 704, 704, 752, 784, 816, 848, 704, 704, 704, 704, 752, 784, 816, 848,
|
||||
720, 720, 720, 720, 768, 800, 832, 864, 720, 720, 720, 720, 768, 800, 832, 864,
|
||||
736, 736, 736, 736, 784, 816, 848, 880, 736, 736, 736, 736, 784, 816, 848, 880,
|
||||
752, 752, 752, 752, 800, 832, 864, 896, 752, 752, 752, 752, 800, 832, 864, 896,
|
||||
768, 768, 768, 768, 816, 848, 880, 912, 768, 768, 768, 768, 816, 848, 880, 912,
|
||||
784, 784, 784, 784, 832, 864, 896, 928, 784, 784, 784, 784, 832, 864, 896, 928,
|
||||
800, 800, 800, 800, 848, 880, 912, 944, 800, 800, 800, 800, 848, 880, 912, 944,
|
||||
816, 816, 816, 816, 864, 896, 928, 960, 816, 816, 816, 816, 864, 896, 928, 960,
|
||||
832, 832, 832, 832, 880, 912, 944, 976, 832, 832, 832, 832, 880, 912, 944, 976,
|
||||
848, 848, 848, 848, 896, 928, 960, 992, 848, 848, 848, 848, 896, 928, 960, 992,
|
||||
864, 864, 864, 864, 912, 944, 976, 1008, 864, 864, 864, 864, 912, 944, 976, 1008,
|
||||
880, 880, 880, 880, 928, 960, 992, 1024, 880, 880, 880, 880, 928, 960, 992, 1024,
|
||||
896, 896, 896, 896, 944, 976, 1008, 1040, 896, 896, 896, 896, 944, 976, 1008, 1040,
|
||||
912, 912, 912, 912, 960, 992, 1024, 1056, 912, 912, 912, 912, 960, 992, 1024, 1056,
|
||||
928, 928, 928, 928, 976, 1008, 1040, 1072, 928, 928, 928, 928, 976, 1008, 1040, 1072,
|
||||
944, 944, 944, 944, 992, 1024, 1056, 1088, 944, 944, 944, 944, 992, 1024, 1056, 1088,
|
||||
960, 960, 960, 960, 1008, 1040, 1072, 1104, 960, 960, 960, 960, 1008, 1040, 1072, 1104,
|
||||
976, 976, 976, 976, 1024, 1056, 1088, 1120, 976, 976, 976, 976, 1024, 1056, 1088, 1120,
|
||||
992, 992, 992, 992, 1040, 1072, 1104, 1136, 992, 992, 992, 992, 1040, 1072, 1104, 1136,
|
||||
1008, 1008, 1008, 1008, 1056, 1088, 1120, 1152, 1008, 1008, 1008, 1008, 1056, 1088, 1120, 1152,
|
||||
1024, 1024, 1024, 1024, 1072, 1104, 1136, 1168, 1024, 1024, 1024, 1024, 1072, 1104, 1136, 1168,
|
||||
1040, 1040, 1040, 1040, 1088, 1120, 1152, 1184, 1040, 1040, 1040, 1040, 1088, 1120, 1152, 1184,
|
||||
1056, 1056, 1056, 1056, 1104, 1136, 1168, 1200, 1056, 1056, 1056, 1056, 1104, 1136, 1168, 1200,
|
||||
1072, 1072, 1072, 1072, 1120, 1152, 1184, 1216, 1072, 1072, 1072, 1072, 1120, 1152, 1184, 1216,
|
||||
1088, 1088, 1088, 1088, 1136, 1168, 1200, 1232, 1088, 1088, 1088, 1088, 1136, 1168, 1200, 1232,
|
||||
1104, 1104, 1104, 1104, 1152, 1184, 1216, 1248, 1104, 1104, 1104, 1104, 1152, 1184, 1216, 1248,
|
||||
1120, 1120, 1120, 1120, 1168, 1200, 1232, 1264, 1120, 1120, 1120, 1120, 1168, 1200, 1232, 1264,
|
||||
1136, 1136, 1136, 1136, 1184, 1216, 1248, 1280, 1136, 1136, 1136, 1136, 1184, 1216, 1248, 1280,
|
||||
1152, 1152, 1152, 1152, 1200, 1232, 1264, 1296, 1152, 1152, 1152, 1152, 1200, 1232, 1264, 1296,
|
||||
1168, 1168, 1168, 1168, 1216, 1248, 1280, 1312, 1168, 1168, 1168, 1168, 1216, 1248, 1280, 1312,
|
||||
1184, 1184, 1184, 1184, 1232, 1264, 1296, 1328, 1184, 1184, 1184, 1184, 1232, 1264, 1296, 1328,
|
||||
1200, 1200, 1200, 1200, 1248, 1280, 1312, 1344, 1200, 1200, 1200, 1200, 1248, 1280, 1312, 1344,
|
||||
1216, 1216, 1216, 1216, 1264, 1296, 1328, 1360, 1216, 1216, 1216, 1216, 1264, 1296, 1328, 1360,
|
||||
1232, 1232, 1232, 1232, 1280, 1312, 1344, 1376, 1232, 1232, 1232, 1232, 1280, 1312, 1344, 1376,
|
||||
1248, 1248, 1248, 1248, 1296, 1328, 1360, 1392, 1248, 1248, 1248, 1248, 1296, 1328, 1360, 1392,
|
||||
1264, 1264, 1264, 1264, 1312, 1344, 1376, 1392, 1264, 1264, 1264, 1264, 1312, 1344, 1376, 1392,
|
||||
1280, 1280, 1280, 1280, 1328, 1360, 1392, 1392, 1280, 1280, 1280, 1280, 1328, 1360, 1392, 1392,
|
||||
1296, 1296, 1296, 1296, 1344, 1376, 1392, 1392, 1296, 1296, 1296, 1296, 1344, 1376, 1392, 1392,
|
||||
1312, 1312, 1312, 1312, 1360, 1392, 1392, 1392, 1312, 1312, 1312, 1312, 1360, 1392, 1392, 1392,
|
||||
1328, 1328, 1328, 1328, 1376, 1392, 1392, 1392, 1328, 1328, 1328, 1328, 1376, 1392, 1392, 1392,
|
||||
1344, 1344, 1344, 1344, 1392, 1392, 1392, 1392, 1344, 1344, 1344, 1344, 1392, 1392, 1392, 1392,
|
||||
1360, 1360, 1360, 1360, 1392, 1392, 1392, 1392, 1360, 1360, 1360, 1360, 1392, 1392, 1392, 1392,
|
||||
1376, 1376, 1376, 1376, 1392, 1392, 1392, 1392, 1376, 1376, 1376, 1376, 1392, 1392, 1392, 1392,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,11 @@ namespace GameRes.Formats.GameSystem
|
||||
public override string Description { get { return "'GameSystem' CG image format"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
|
||||
public CgdFormat ()
|
||||
{
|
||||
Extensions = new string[] { "cgd", "crgb" };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
if (file.Signature != file.Length)
|
||||
@@ -55,9 +60,9 @@ namespace GameRes.Formats.GameSystem
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
file.Position = 0x10;
|
||||
var reader = new CgdReader (file, info);
|
||||
var pixels = reader.Unpack();
|
||||
return ImageData.CreateFlipped (info, reader.Format, null, pixels, reader.Stride);
|
||||
return reader.Image;
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
@@ -66,29 +71,27 @@ namespace GameRes.Formats.GameSystem
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class CgdReader
|
||||
internal sealed class CgdReader : BinaryImageDecoder
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
int m_width;
|
||||
int m_height;
|
||||
byte[] m_output;
|
||||
|
||||
public int Stride { get; private set; }
|
||||
public PixelFormat Format { get { return PixelFormats.Bgr24; } }
|
||||
public byte[] Data { get { return m_output; } }
|
||||
|
||||
public CgdReader (IBinaryStream input, ImageMetaData info)
|
||||
public CgdReader (IBinaryStream input, ImageMetaData info) : base (input, info)
|
||||
{
|
||||
m_input = input;
|
||||
m_width = (int)info.Width;
|
||||
m_height = (int)info.Height;
|
||||
Stride = 3 * m_width;
|
||||
m_output = new byte[Stride * m_height];
|
||||
Stride = 3 * (int)info.Width;
|
||||
m_output = new byte[Stride * (int)info.Height];
|
||||
}
|
||||
|
||||
public byte[] Unpack ()
|
||||
protected override ImageData GetImageData ()
|
||||
{
|
||||
var pixels = Unpack();
|
||||
return ImageData.CreateFlipped (Info, Format, null, pixels, Stride);
|
||||
}
|
||||
|
||||
byte[] Unpack ()
|
||||
{
|
||||
m_input.Position = 0x10;
|
||||
int dst = 0;
|
||||
byte r = 0, g = 0, b = 0;
|
||||
while (dst < m_output.Length)
|
||||
@@ -131,7 +134,7 @@ namespace GameRes.Formats.GameSystem
|
||||
return m_output;
|
||||
}
|
||||
|
||||
static readonly byte[,] ColorTable = InitColorTable();
|
||||
internal static readonly byte[,] ColorTable = InitColorTable();
|
||||
|
||||
private static byte[,] InitColorTable ()
|
||||
{
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace GameRes.Formats.GameSystem
|
||||
{
|
||||
internal class ChrMetaData : ImageMetaData
|
||||
{
|
||||
public uint DataOffset;
|
||||
public int RgbSize;
|
||||
}
|
||||
|
||||
@@ -65,14 +66,14 @@ namespace GameRes.Formats.GameSystem
|
||||
OffsetY = y,
|
||||
BPP = 32,
|
||||
RgbSize = rgb_size,
|
||||
DataOffset = 0x20,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new ChrReader (file, (ChrMetaData)info);
|
||||
var pixels = reader.Unpack();
|
||||
return ImageData.CreateFlipped (info, PixelFormats.Bgra32, null, pixels, reader.Stride);
|
||||
return reader.Image;
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
@@ -81,9 +82,8 @@ namespace GameRes.Formats.GameSystem
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ChrReader
|
||||
internal class ChrReader : BinaryImageDecoder
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
ChrMetaData m_info;
|
||||
byte[] m_output;
|
||||
int m_stride;
|
||||
@@ -91,14 +91,19 @@ namespace GameRes.Formats.GameSystem
|
||||
public byte[] Data { get { return m_output; } }
|
||||
public int Stride { get { return m_stride; } }
|
||||
|
||||
public ChrReader (IBinaryStream input, ChrMetaData info)
|
||||
public ChrReader (IBinaryStream input, ChrMetaData info) : base (input, info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
m_stride = (int)m_info.Width * 4;
|
||||
m_output = new byte[m_stride * (int)m_info.Height];
|
||||
}
|
||||
|
||||
protected override ImageData GetImageData ()
|
||||
{
|
||||
var pixels = Unpack();
|
||||
return ImageData.CreateFlipped (Info, PixelFormats.Bgra32, null, pixels, Stride);
|
||||
}
|
||||
|
||||
public byte[] Unpack ()
|
||||
{
|
||||
UnpackBaseline();
|
||||
@@ -114,7 +119,7 @@ namespace GameRes.Formats.GameSystem
|
||||
|
||||
public byte[] UnpackBaseline ()
|
||||
{
|
||||
m_input.Position = 0x20;
|
||||
m_input.Position = m_info.DataOffset;
|
||||
UnpackRgb ((int)m_info.Height);
|
||||
return m_output;
|
||||
}
|
||||
|
||||
@@ -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.29.1269")]
|
||||
[assembly: AssemblyFileVersion ("1.2.29.1269")]
|
||||
[assembly: AssemblyVersion ("1.2.30.1292")]
|
||||
[assembly: AssemblyFileVersion ("1.2.30.1292")]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//! \date Mon Jun 15 04:03:18 2015
|
||||
//! \brief QLIE engine archives implementation.
|
||||
//
|
||||
// Copyright (C) 2015 by morkt
|
||||
// Copyright (C) 2015-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
|
||||
@@ -32,6 +32,7 @@ using System.Linq;
|
||||
using GameRes.Utility;
|
||||
using GameRes.Formats.Properties;
|
||||
using GameRes.Formats.Strings;
|
||||
using GameRes.Formats.Borland;
|
||||
|
||||
namespace GameRes.Formats.Qlie
|
||||
{
|
||||
@@ -136,7 +137,7 @@ namespace GameRes.Formats.Qlie
|
||||
use_pack_keyfile = key_file != null;
|
||||
// currently, user is prompted to choose encryption scheme only if there's 'key.fkey' file found.
|
||||
if (use_pack_keyfile)
|
||||
arc_key = QueryEncryption();
|
||||
arc_key = QueryEncryption (file);
|
||||
// use_pack_keyfile = null != arc_key;
|
||||
|
||||
var key_data = file.View.ReadBytes (file.MaxOffset-0x41C, 0x100);
|
||||
@@ -423,10 +424,20 @@ namespace GameRes.Formats.Qlie
|
||||
return new GUI.WidgetQLIE();
|
||||
}
|
||||
|
||||
byte[] QueryEncryption ()
|
||||
byte[] QueryEncryption (ArcView file)
|
||||
{
|
||||
var options = Query<QlieOptions> (arcStrings.ArcEncryptedNotice);
|
||||
return options.GameKeyData;
|
||||
var title = FormatCatalog.Instance.LookupGame (file.Name, @"..\*.exe");
|
||||
byte[] key = null;
|
||||
if (!string.IsNullOrEmpty (title))
|
||||
key = GetKeyData (title);
|
||||
if (null == key)
|
||||
key = GuessKeyData (file.Name);
|
||||
if (null == key)
|
||||
{
|
||||
var options = Query<QlieOptions> (arcStrings.ArcEncryptedNotice);
|
||||
key = options.GameKeyData;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
static byte[] GetKeyData (string scheme)
|
||||
@@ -457,5 +468,46 @@ namespace GameRes.Formats.Qlie
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] GuessKeyData (string arc_name)
|
||||
{
|
||||
if (VFS.IsVirtual)
|
||||
return null;
|
||||
// XXX add button to query dialog like with CatSystem?
|
||||
var pattern = VFS.CombinePath (VFS.GetDirectoryName (arc_name), @"..\*.exe");
|
||||
foreach (var file in VFS.GetFiles (pattern))
|
||||
{
|
||||
try
|
||||
{
|
||||
var key = GetKeyDataFromExe (file.Name);
|
||||
if (key != null)
|
||||
return key;
|
||||
}
|
||||
catch { /* ignore errors */ }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static byte[] GetKeyDataFromExe (string filename)
|
||||
{
|
||||
using (var exe = new ExeFile.ResourceAccessor (filename))
|
||||
{
|
||||
var tform = exe.GetResource ("TFORM1", "#10");
|
||||
if (null == tform || !tform.AsciiEqual (0, "TPF0"))
|
||||
return null;
|
||||
using (var input = new BinMemoryStream (tform))
|
||||
{
|
||||
var deserializer = new DelphiDeserializer (input);
|
||||
var form = deserializer.Deserialize();
|
||||
var image = form.Contents.FirstOrDefault (n => n.Name == "IconKeyImage");
|
||||
if (null == image)
|
||||
return null;
|
||||
var icon = image.Props["Picture.Data"] as byte[];
|
||||
if (null == icon || icon.Length < 0x106 || !icon.AsciiEqual (0, "\x05TIcon"))
|
||||
return null;
|
||||
return new CowArray<byte> (icon, 6, 0x100).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
147
ArcFormats/Qlie/DelphiDeserializer.cs
Normal file
147
ArcFormats/Qlie/DelphiDeserializer.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
//! \file DelphiDeserializer.cs
|
||||
//! \date Wed Feb 22 15:40:33 2017
|
||||
//! \brief Borland Delphi binary data deserializer.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace GameRes.Formats.Borland
|
||||
{
|
||||
public sealed class DelphiDeserializer
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
|
||||
public Encoding Encoding { get; set; }
|
||||
|
||||
public DelphiDeserializer (IBinaryStream input)
|
||||
{
|
||||
m_input = input;
|
||||
Encoding = Encodings.cp932;
|
||||
}
|
||||
|
||||
public DelphiObject Deserialize ()
|
||||
{
|
||||
if (m_input.ReadUInt32() != 0x30465054) // 'TPF0'
|
||||
return null;
|
||||
return DeserializeNode();
|
||||
}
|
||||
|
||||
DelphiObject DeserializeNode ()
|
||||
{
|
||||
int type_len = m_input.ReadByte();
|
||||
if (type_len <= 0)
|
||||
return null;
|
||||
var node = new DelphiObject();
|
||||
node.Type = ReadString (type_len);
|
||||
node.Name = ReadString();
|
||||
int key_length;
|
||||
while ((key_length = m_input.ReadUInt8()) > 0)
|
||||
{
|
||||
var key = ReadString (key_length);
|
||||
node.Props[key] = ReadValue();
|
||||
}
|
||||
DelphiObject child;
|
||||
while ((child = DeserializeNode()) != null)
|
||||
{
|
||||
node.Contents.Add (child);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
object ReadValue ()
|
||||
{
|
||||
int type = m_input.ReadUInt8();
|
||||
switch (type)
|
||||
{
|
||||
case 2: return (int)m_input.ReadUInt8();
|
||||
case 3: return (int)m_input.ReadUInt16();
|
||||
case 5: return ReadLongDouble();
|
||||
case 6:
|
||||
case 7: return ReadString();
|
||||
case 8:
|
||||
case 9: return true;
|
||||
case 10: return ReadByteString();
|
||||
case 11: return ReadStringArray();
|
||||
case 18: return ReadUnicodeString();
|
||||
default: throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
string ReadString ()
|
||||
{
|
||||
return ReadString (m_input.ReadUInt8());
|
||||
}
|
||||
|
||||
string ReadString (int length)
|
||||
{
|
||||
return m_input.ReadCString (length, Encoding);
|
||||
}
|
||||
|
||||
string ReadUnicodeString ()
|
||||
{
|
||||
int length = m_input.ReadInt32();
|
||||
if (length < 0)
|
||||
throw new InvalidFormatException();
|
||||
if (0 == length)
|
||||
return "";
|
||||
var bytes = m_input.ReadBytes (length * 2);
|
||||
return Encoding.Unicode.GetString (bytes);
|
||||
}
|
||||
|
||||
byte[] ReadByteString ()
|
||||
{
|
||||
int length = m_input.ReadInt32();
|
||||
if (length < 0)
|
||||
throw new InvalidFormatException();
|
||||
if (0 == length)
|
||||
return new byte[0];
|
||||
return m_input.ReadBytes (length);
|
||||
}
|
||||
|
||||
IList<string> ReadStringArray ()
|
||||
{
|
||||
var list = new List<string>();
|
||||
int length;
|
||||
while ((length = m_input.ReadUInt8()) > 0)
|
||||
{
|
||||
list.Add (ReadString (length));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
object ReadLongDouble ()
|
||||
{
|
||||
return m_input.ReadBytes (10); // long double deserialization not implemented
|
||||
}
|
||||
}
|
||||
|
||||
public class DelphiObject
|
||||
{
|
||||
public string Type;
|
||||
public string Name;
|
||||
public IDictionary Props = new Hashtable();
|
||||
public IList<DelphiObject> Contents = new List<DelphiObject>();
|
||||
}
|
||||
}
|
||||
227
ArcFormats/RealLive/ArcKOE.cs
Normal file
227
ArcFormats/RealLive/ArcKOE.cs
Normal file
@@ -0,0 +1,227 @@
|
||||
//! \file ArcKOE.cs
|
||||
//! \date Sun Feb 19 14:58:05 2017
|
||||
//! \brief RealLive audio archive.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace GameRes.Formats.RealLive
|
||||
{
|
||||
internal class KoeEntry : PackedEntry
|
||||
{
|
||||
public ushort SampleCount;
|
||||
}
|
||||
|
||||
internal class KoeArchive : ArcFile
|
||||
{
|
||||
public readonly WaveFormat Format;
|
||||
|
||||
public KoeArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, WaveFormat format)
|
||||
: base (arc, impl, dir)
|
||||
{
|
||||
Format = format;
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class KoeOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "KOE"; } }
|
||||
public override string Description { get { return "RealLive engine audio archive"; } }
|
||||
public override uint Signature { get { return 0x50454F4B; } } // 'KOEPAC'
|
||||
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, "AC\0"))
|
||||
return null;
|
||||
int count = file.View.ReadInt32 (0x10);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
uint data_offset = file.View.ReadUInt32 (0x14);
|
||||
uint sample_rate = file.View.ReadUInt32 (0x18);
|
||||
if (0 == sample_rate)
|
||||
sample_rate = 22050;
|
||||
|
||||
var base_name = Path.GetFileNameWithoutExtension (file.Name);
|
||||
uint index_offset = 0x20;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
int id = file.View.ReadUInt16 (index_offset);
|
||||
var entry = new KoeEntry
|
||||
{
|
||||
Name = string.Format ("{0}#{1:D4}.wav", base_name, id),
|
||||
Type = "audio",
|
||||
SampleCount = file.View.ReadUInt16 (index_offset+2),
|
||||
Offset = file.View.ReadUInt32 (index_offset+4),
|
||||
IsPacked = true,
|
||||
};
|
||||
entry.Size = entry.SampleCount * 2u;
|
||||
index_offset += 8;
|
||||
dir.Add (entry);
|
||||
}
|
||||
var format = new WaveFormat
|
||||
{
|
||||
FormatTag = 1,
|
||||
Channels = 2,
|
||||
SamplesPerSecond = sample_rate,
|
||||
AverageBytesPerSecond = 4 * sample_rate,
|
||||
BlockAlign = 4,
|
||||
BitsPerSample = 16,
|
||||
};
|
||||
return new KoeArchive (file, this, dir, format);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var kent = (KoeEntry)entry;
|
||||
var karc = (KoeArchive)arc;
|
||||
var table = new ushort[kent.SampleCount];
|
||||
uint packed_size = 0;
|
||||
var offset = kent.Offset;
|
||||
for (int i = 0; i < table.Length; ++i)
|
||||
{
|
||||
table[i] = arc.File.View.ReadUInt16 (offset);
|
||||
offset += 2;
|
||||
packed_size += table[i];
|
||||
}
|
||||
int total_size = kent.SampleCount * 0x1000;
|
||||
var wav = new MemoryStream (total_size);
|
||||
WaveAudio.WriteRiffHeader (wav, karc.Format, (uint)total_size);
|
||||
using (var output = new BinaryWriter (wav, Encoding.ASCII, true))
|
||||
using (var input = arc.File.CreateStream (offset, packed_size))
|
||||
{
|
||||
foreach (ushort chunk_length in table)
|
||||
{
|
||||
if (0 == chunk_length)
|
||||
{
|
||||
output.Seek (0x1000, SeekOrigin.Current);
|
||||
}
|
||||
else if (0x400 == chunk_length)
|
||||
{
|
||||
for (int i = 0; i < 0x400; ++i)
|
||||
{
|
||||
ushort sample = SampleTable[input.ReadUInt8()];
|
||||
output.Write (sample);
|
||||
output.Write (sample);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
byte src = 0;
|
||||
for (int i = 0; i < 0x400; i += 2)
|
||||
{
|
||||
byte bits = input.ReadUInt8();
|
||||
if (0 != ((bits + 1) & 0xF))
|
||||
{
|
||||
src -= AdjustTable[bits & 0xF];
|
||||
}
|
||||
else
|
||||
{
|
||||
int idx = bits >> 4;
|
||||
bits = input.ReadUInt8();
|
||||
idx |= (bits << 4) & 0xF0;
|
||||
src -= AdjustTable[idx];
|
||||
}
|
||||
ushort sample = SampleTable[src];
|
||||
output.Write (sample);
|
||||
output.Write (sample);
|
||||
bits >>= 4;
|
||||
if (0 != ((bits + 1) & 0xF))
|
||||
src -= AdjustTable[bits & 0xF];
|
||||
else
|
||||
src -= AdjustTable[input.ReadUInt8()];
|
||||
|
||||
sample = SampleTable[src];
|
||||
output.Write (sample);
|
||||
output.Write (sample);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
kent.UnpackedSize = (uint)wav.Length;
|
||||
wav.Position = 0;
|
||||
return wav;
|
||||
}
|
||||
|
||||
static readonly ushort[] SampleTable = new ushort[] {
|
||||
0x8000, 0x81FF, 0x83F9, 0x85EF, 0x87E1, 0x89CF, 0x8BB9, 0x8D9F,
|
||||
0x8F81, 0x915F, 0x9339, 0x950F, 0x96E1, 0x98AF, 0x9A79, 0x9C3F,
|
||||
0x9E01, 0x9FBF, 0xA179, 0xA32F, 0xA4E1, 0xA68F, 0xA839, 0xA9DF,
|
||||
0xAB81, 0xAD1F, 0xAEB9, 0xB04F, 0xB1E1, 0xB36F, 0xB4F9, 0xB67F,
|
||||
0xB801, 0xB97F, 0xBAF9, 0xBC6F, 0xBDE1, 0xBF4F, 0xC0B9, 0xC21F,
|
||||
0xC381, 0xC4DF, 0xC639, 0xC78F, 0xC8E1, 0xCA2F, 0xCB79, 0xCCBF,
|
||||
0xCE01, 0xCF3F, 0xD079, 0xD1AF, 0xD2E1, 0xD40F, 0xD539, 0xD65F,
|
||||
0xD781, 0xD89F, 0xD9B9, 0xDACF, 0xDBE1, 0xDCEF, 0xDDF9, 0xDEFF,
|
||||
0xE001, 0xE0FF, 0xE1F9, 0xE2EF, 0xE3E1, 0xE4CF, 0xE5B9, 0xE69F,
|
||||
0xE781, 0xE85F, 0xE939, 0xEA0F, 0xEAE1, 0xEBAF, 0xEC79, 0xED3F,
|
||||
0xEE01, 0xEEBF, 0xEF79, 0xF02F, 0xF0E1, 0xF18F, 0xF239, 0xF2DF,
|
||||
0xF381, 0xF41F, 0xF4B9, 0xF54F, 0xF5E1, 0xF66F, 0xF6F9, 0xF77F,
|
||||
0xF801, 0xF87F, 0xF8F9, 0xF96F, 0xF9E1, 0xFA4F, 0xFAB9, 0xFB1F,
|
||||
0xFB81, 0xFBDF, 0xFC39, 0xFC8F, 0xFCE1, 0xFD2F, 0xFD79, 0xFDBF,
|
||||
0xFE01, 0xFE3F, 0xFE79, 0xFEAF, 0xFEE1, 0xFF0F, 0xFF39, 0xFF5F,
|
||||
0xFF81, 0xFF9F, 0xFFB9, 0xFFCF, 0xFFE1, 0xFFEF, 0xFFF9, 0xFFFF,
|
||||
0x0000, 0x0001, 0x0007, 0x0011, 0x001F, 0x0031, 0x0047, 0x0061,
|
||||
0x007F, 0x00A1, 0x00C7, 0x00F1, 0x011F, 0x0151, 0x0187, 0x01C1,
|
||||
0x01FF, 0x0241, 0x0287, 0x02D1, 0x031F, 0x0371, 0x03C7, 0x0421,
|
||||
0x047F, 0x04E1, 0x0547, 0x05B1, 0x061F, 0x0691, 0x0707, 0x0781,
|
||||
0x07FF, 0x0881, 0x0907, 0x0991, 0x0A1F, 0x0AB1, 0x0B47, 0x0BE1,
|
||||
0x0C7F, 0x0D21, 0x0DC7, 0x0E71, 0x0F1F, 0x0FD1, 0x1087, 0x1141,
|
||||
0x11FF, 0x12C1, 0x1387, 0x1451, 0x151F, 0x15F1, 0x16C7, 0x17A1,
|
||||
0x187F, 0x1961, 0x1A47, 0x1B31, 0x1C1F, 0x1D11, 0x1E07, 0x1F01,
|
||||
0x1FFF, 0x2101, 0x2207, 0x2311, 0x241F, 0x2531, 0x2647, 0x2761,
|
||||
0x287F, 0x29A1, 0x2AC7, 0x2BF1, 0x2D1F, 0x2E51, 0x2F87, 0x30C1,
|
||||
0x31FF, 0x3341, 0x3487, 0x35D1, 0x371F, 0x3871, 0x39C7, 0x3B21,
|
||||
0x3C7F, 0x3DE1, 0x3F47, 0x40B1, 0x421F, 0x4391, 0x4507, 0x4681,
|
||||
0x47FF, 0x4981, 0x4B07, 0x4C91, 0x4E1F, 0x4FB1, 0x5147, 0x52E1,
|
||||
0x547F, 0x5621, 0x57C7, 0x5971, 0x5B1F, 0x5CD1, 0x5E87, 0x6041,
|
||||
0x61FF, 0x63C1, 0x6587, 0x6751, 0x691F, 0x6AF1, 0x6CC7, 0x6EA1,
|
||||
0x707F, 0x7261, 0x7447, 0x7631, 0x781F, 0x7A11, 0x7C07, 0x7FFF
|
||||
};
|
||||
|
||||
static readonly byte[] AdjustTable = new byte[] {
|
||||
0x00, 0xFF, 0x01, 0xFE, 0x02, 0xFD, 0x03, 0xFC, 0x04, 0xFB, 0x05, 0xFA, 0x06, 0xF9, 0x07, 0xF8,
|
||||
0x08, 0xF7, 0x09, 0xF6, 0x0A, 0xF5, 0x0B, 0xF4, 0x0C, 0xF3, 0x0D, 0xF2, 0x0E, 0xF1, 0x0F, 0xF0,
|
||||
0x10, 0xEF, 0x11, 0xEE, 0x12, 0xED, 0x13, 0xEC, 0x14, 0xEB, 0x15, 0xEA, 0x16, 0xE9, 0x17, 0xE8,
|
||||
0x18, 0xE7, 0x19, 0xE6, 0x1A, 0xE5, 0x1B, 0xE4, 0x1C, 0xE3, 0x1D, 0xE2, 0x1E, 0xE1, 0x1F, 0xE0,
|
||||
0x20, 0xDF, 0x21, 0xDE, 0x22, 0xDD, 0x23, 0xDC, 0x24, 0xDB, 0x25, 0xDA, 0x26, 0xD9, 0x27, 0xD8,
|
||||
0x28, 0xD7, 0x29, 0xD6, 0x2A, 0xD5, 0x2B, 0xD4, 0x2C, 0xD3, 0x2D, 0xD2, 0x2E, 0xD1, 0x2F, 0xD0,
|
||||
0x30, 0xCF, 0x31, 0xCE, 0x32, 0xCD, 0x33, 0xCC, 0x34, 0xCB, 0x35, 0xCA, 0x36, 0xC9, 0x37, 0xC8,
|
||||
0x38, 0xC7, 0x39, 0xC6, 0x3A, 0xC5, 0x3B, 0xC4, 0x3C, 0xC3, 0x3D, 0xC2, 0x3E, 0xC1, 0x3F, 0xC0,
|
||||
0x40, 0xBF, 0x41, 0xBE, 0x42, 0xBD, 0x43, 0xBC, 0x44, 0xBB, 0x45, 0xBA, 0x46, 0xB9, 0x47, 0xB8,
|
||||
0x48, 0xB7, 0x49, 0xB6, 0x4A, 0xB5, 0x4B, 0xB4, 0x4C, 0xB3, 0x4D, 0xB2, 0x4E, 0xB1, 0x4F, 0xB0,
|
||||
0x50, 0xAF, 0x51, 0xAE, 0x52, 0xAD, 0x53, 0xAC, 0x54, 0xAB, 0x55, 0xAA, 0x56, 0xA9, 0x57, 0xA8,
|
||||
0x58, 0xA7, 0x59, 0xA6, 0x5A, 0xA5, 0x5B, 0xA4, 0x5C, 0xA3, 0x5D, 0xA2, 0x5E, 0xA1, 0x5F, 0xA0,
|
||||
0x60, 0x9F, 0x61, 0x9E, 0x62, 0x9D, 0x63, 0x9C, 0x64, 0x9B, 0x65, 0x9A, 0x66, 0x99, 0x67, 0x98,
|
||||
0x68, 0x97, 0x69, 0x96, 0x6A, 0x95, 0x6B, 0x94, 0x6C, 0x93, 0x6D, 0x92, 0x6E, 0x91, 0x6F, 0x90,
|
||||
0x70, 0x8F, 0x71, 0x8E, 0x72, 0x8D, 0x73, 0x8C, 0x74, 0x8B, 0x75, 0x8A, 0x76, 0x89, 0x77, 0x88,
|
||||
0x78, 0x87, 0x79, 0x86, 0x7A, 0x85, 0x7B, 0x84, 0x7C, 0x83, 0x7D, 0x82, 0x7E, 0x81, 0x7F, 0x80
|
||||
};
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -2,7 +2,7 @@
|
||||
//! \date Fri Apr 10 03:10:42 2015
|
||||
//! \brief ShiinaRio engine archive format.
|
||||
//
|
||||
// Copyright (C) 2015-2016 by morkt
|
||||
// Copyright (C) 2015-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
|
||||
@@ -83,14 +83,18 @@ namespace GameRes.Formats.ShiinaRio // 椎名里緒
|
||||
if (!file.View.AsciiEqual (4, " 1."))
|
||||
return null;
|
||||
int version = file.View.ReadByte (7) - 0x30;
|
||||
if (version < 1 || version > 7)
|
||||
return null;
|
||||
version = 100 + version * 10;
|
||||
if (170 != version && 150 != version && 140 != version && 130 != version && 120 != version)
|
||||
throw new NotSupportedException ("Not supported WARC version");
|
||||
uint index_offset = 0xf182ad82u ^ file.View.ReadUInt32 (8);
|
||||
uint index_offset = 0xF182AD82u ^ file.View.ReadUInt32 (8);
|
||||
if (index_offset >= file.MaxOffset)
|
||||
return null;
|
||||
|
||||
var scheme = QueryEncryption (file.Name);
|
||||
EncryptionScheme scheme;
|
||||
if (version > 110)
|
||||
scheme = QueryEncryption (file.Name);
|
||||
else
|
||||
scheme = EncryptionScheme.Warc110;
|
||||
if (null == scheme)
|
||||
return null;
|
||||
var decoder = new Decoder (version, scheme);
|
||||
@@ -111,7 +115,7 @@ namespace GameRes.Formats.ShiinaRio // 椎名里緒
|
||||
var zindex = new MemoryStream (enc_index, 8, (int)index_length-8);
|
||||
index = new ZLibStream (zindex, CompressionMode.Decompress);
|
||||
}
|
||||
else
|
||||
else if (version >= 120)
|
||||
{
|
||||
var unpacked = new byte[max_index_len];
|
||||
index_length = UnpackRNG (enc_index, 0, index_length, unpacked);
|
||||
@@ -119,6 +123,10 @@ namespace GameRes.Formats.ShiinaRio // 椎名里緒
|
||||
return null;
|
||||
index = new MemoryStream (unpacked, 0, (int)index_length);
|
||||
}
|
||||
else
|
||||
{
|
||||
index = new MemoryStream (enc_index, 0, (int)index_length);
|
||||
}
|
||||
using (var header = new BinaryReader (index))
|
||||
{
|
||||
byte[] name_buf = new byte[decoder.EntryNameSize];
|
||||
@@ -151,15 +159,14 @@ namespace GameRes.Formats.ShiinaRio // 椎名里緒
|
||||
var wentry = entry as WarcEntry;
|
||||
if (null == warc || null == wentry || entry.Size < 8)
|
||||
return arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
var enc_data = new byte[entry.Size];
|
||||
if (entry.Size != arc.File.View.Read (entry.Offset, enc_data, 0, entry.Size))
|
||||
return Stream.Null;
|
||||
var enc_data = arc.File.View.ReadBytes (entry.Offset, entry.Size);
|
||||
if (enc_data.Length <= 8)
|
||||
return new BinMemoryStream (enc_data, entry.Name);
|
||||
uint sig = LittleEndian.ToUInt32 (enc_data, 0);
|
||||
uint unpacked_size = LittleEndian.ToUInt32 (enc_data, 4);
|
||||
sig ^= (unpacked_size ^ 0x82AD82) & 0xffffff;
|
||||
|
||||
if (entry.Size > 8)
|
||||
if (warc.Decoder.WarcVersion > 110)
|
||||
{
|
||||
sig ^= (unpacked_size ^ 0x82AD82) & 0xFFFFFF;
|
||||
if (0 != (wentry.Flags & 0x80000000u)) // encrypted entry
|
||||
warc.Decoder.Decrypt (enc_data, 8, entry.Size-8);
|
||||
if (warc.Decoder.ExtraCrypt != null)
|
||||
@@ -185,10 +192,13 @@ namespace GameRes.Formats.ShiinaRio // 椎名里緒
|
||||
{
|
||||
unpacked = new byte[unpacked_size];
|
||||
unpack (enc_data, unpacked);
|
||||
if (0 != (wentry.Flags & 0x40000000))
|
||||
warc.Decoder.Decrypt2 (unpacked, 0, (uint)unpacked.Length);
|
||||
if (warc.Decoder.ExtraCrypt != null)
|
||||
warc.Decoder.ExtraCrypt.Decrypt (unpacked, 0, (uint)unpacked.Length, 0x204);
|
||||
if (warc.Decoder.WarcVersion > 110)
|
||||
{
|
||||
if (0 != (wentry.Flags & 0x40000000))
|
||||
warc.Decoder.Decrypt2 (unpacked, 0, (uint)unpacked.Length);
|
||||
if (warc.Decoder.ExtraCrypt != null)
|
||||
warc.Decoder.ExtraCrypt.Decrypt (unpacked, 0, (uint)unpacked.Length, 0x204);
|
||||
}
|
||||
}
|
||||
return new BinMemoryStream (unpacked, entry.Name);
|
||||
}
|
||||
|
||||
@@ -52,11 +52,9 @@ namespace GameRes.Formats.ShiinaRio
|
||||
|
||||
public override ImageData Read (IBinaryStream stream, ImageMetaData info)
|
||||
{
|
||||
using (var reader = new Reader (stream, (int)info.Width, (int)info.Height))
|
||||
{
|
||||
reader.Unpack ();
|
||||
return ImageData.Create (info, PixelFormats.Bgr24, null, reader.Data);
|
||||
}
|
||||
var reader = new Reader (stream, (int)info.Width, (int)info.Height);
|
||||
reader.Unpack();
|
||||
return ImageData.Create (info, PixelFormats.Bgr24, null, reader.Data, reader.Stride);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
@@ -64,13 +62,14 @@ namespace GameRes.Formats.ShiinaRio
|
||||
throw new System.NotImplementedException ("Mi4Format.Write not implemented");
|
||||
}
|
||||
|
||||
internal sealed class Reader : IDisposable
|
||||
internal sealed class Reader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
byte[] m_output;
|
||||
int m_stride;
|
||||
|
||||
public byte[] Data { get { return m_output; } }
|
||||
public int Stride { get { return m_stride; } }
|
||||
|
||||
public Reader (IBinaryStream file, int width, int height)
|
||||
{
|
||||
@@ -79,6 +78,14 @@ namespace GameRes.Formats.ShiinaRio
|
||||
m_output = new byte[m_stride*height];
|
||||
}
|
||||
|
||||
public void Unpack ()
|
||||
{
|
||||
m_input.Position = 0x10;
|
||||
m_bit_count = 0;
|
||||
LoadBits();
|
||||
UnpackV2();
|
||||
}
|
||||
|
||||
int m_bit_count;
|
||||
uint m_bits;
|
||||
|
||||
@@ -107,19 +114,101 @@ namespace GameRes.Formats.ShiinaRio
|
||||
|
||||
uint GetBits (int count)
|
||||
{
|
||||
uint bits = 0;
|
||||
while (count --> 0)
|
||||
int avail_bits = Math.Min (count, m_bit_count);
|
||||
uint bits = m_bits >> (32 - avail_bits);
|
||||
m_bits <<= avail_bits;
|
||||
m_bit_count -= avail_bits;
|
||||
count -= avail_bits;
|
||||
if (0 == m_bit_count)
|
||||
{
|
||||
bits = (bits << 1) | GetBit();
|
||||
LoadBits();
|
||||
if (count > 0)
|
||||
{
|
||||
bits = bits << count | m_bits >> (32 - count);
|
||||
m_bits <<= count;
|
||||
m_bit_count -= count;
|
||||
}
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
public void Unpack ()
|
||||
void UnpackV1 ()
|
||||
{
|
||||
int dst = 0;
|
||||
byte b = 0, g = 0, r = 0;
|
||||
while (dst < m_output.Length)
|
||||
{
|
||||
if (GetBit() == 0)
|
||||
{
|
||||
if (GetBit() != 0)
|
||||
{
|
||||
b = m_input.ReadUInt8();
|
||||
g = m_input.ReadUInt8();
|
||||
r = m_input.ReadUInt8();
|
||||
}
|
||||
else if (GetBit() != 0)
|
||||
{
|
||||
byte v = (byte)GetBits (2);
|
||||
if (3 == v)
|
||||
{
|
||||
b = m_output[dst - m_stride];
|
||||
g = m_output[dst - m_stride + 1];
|
||||
r = m_output[dst - m_stride + 2];
|
||||
}
|
||||
else
|
||||
{
|
||||
b += (byte)(v - 1);
|
||||
g += (byte)(GetBits(2) - 1);
|
||||
r += (byte)(GetBits(2) - 1);
|
||||
}
|
||||
}
|
||||
else if (GetBit() != 0)
|
||||
{
|
||||
byte v = (byte)(GetBits(3));
|
||||
if (7 == v)
|
||||
{
|
||||
b = m_output[dst - m_stride + 3];
|
||||
g = m_output[dst - m_stride + 4];
|
||||
r = m_output[dst - m_stride + 5];
|
||||
}
|
||||
else
|
||||
{
|
||||
b += (byte)(v - 3);
|
||||
g += (byte)(GetBits(3) - 3);
|
||||
r += (byte)(GetBits(3) - 3);
|
||||
}
|
||||
}
|
||||
else if (GetBit() != 0)
|
||||
{
|
||||
byte v = (byte)GetBits (4);
|
||||
if (0xF == v)
|
||||
{
|
||||
b = m_output[dst - m_stride - 3];
|
||||
g = m_output[dst - m_stride - 2];
|
||||
r = m_output[dst - m_stride - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
b += (byte)(v - 7);
|
||||
g += (byte)(GetBits(4) - 7);
|
||||
r += (byte)(GetBits(4) - 7);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
b += (byte)(GetBits(5) - 15);
|
||||
g += (byte)(GetBits(5) - 15);
|
||||
r += (byte)(GetBits(5) - 15);
|
||||
}
|
||||
}
|
||||
m_output[dst++] = b;
|
||||
m_output[dst++] = g;
|
||||
m_output[dst++] = r;
|
||||
}
|
||||
}
|
||||
|
||||
void UnpackV2 ()
|
||||
{
|
||||
m_input.Position = 0x10;
|
||||
m_bit_count = 0;
|
||||
LoadBits();
|
||||
int dst = 0;
|
||||
byte b = 0, g = 0, r = 0;
|
||||
while (dst < m_output.Length)
|
||||
@@ -207,13 +296,6 @@ namespace GameRes.Formats.ShiinaRio
|
||||
m_output[dst++] = r;
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
public void Dispose ()
|
||||
{
|
||||
GC.SuppressFinalize (this);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@ namespace GameRes.Formats.ShiinaRio
|
||||
public byte[] DecodeBin;
|
||||
public IByteArray ShiinaImage;
|
||||
public IDecryptExtra ExtraCrypt;
|
||||
|
||||
public static readonly EncryptionScheme Warc110 = new EncryptionScheme { EntryNameSize = 0x10 };
|
||||
}
|
||||
|
||||
public interface IByteArray
|
||||
@@ -136,7 +138,7 @@ namespace GameRes.Formats.ShiinaRio
|
||||
|
||||
void DoEncryption (byte[] data, int index, uint data_length, ContentEncryptor encryptor)
|
||||
{
|
||||
if (data_length < 3)
|
||||
if (data_length < 3 || WarcVersion < 120)
|
||||
return;
|
||||
uint effective_length = Math.Min (data_length, 1024u);
|
||||
int a, b;
|
||||
|
||||
201
ArcFormats/Software House Parsley/ArcCG.cs
Normal file
201
ArcFormats/Software House Parsley/ArcCG.cs
Normal file
@@ -0,0 +1,201 @@
|
||||
//! \file ArcCG.cs
|
||||
//! \date Tue Feb 16 02:48:55 2016
|
||||
//! \brief Software House Parsley CG archive.
|
||||
//
|
||||
// Copyright (C) 2016-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.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace GameRes.Formats.Parsley
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class CgOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "CG/PARSLEY/2"; } }
|
||||
public override string Description { get { return "Software House Parsley CG archive"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public CgOpener ()
|
||||
{
|
||||
Extensions = new string[] { "" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (8);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
int first_offset = file.View.ReadInt32 (0x2C);
|
||||
if (12 + count*0x28 != first_offset)
|
||||
return null;
|
||||
|
||||
uint index_offset = 0xC;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var name = file.View.ReadString (index_offset, 0x20);
|
||||
if (string.IsNullOrWhiteSpace (name))
|
||||
return null;
|
||||
var entry = FormatCatalog.Instance.Create<Entry> (name);
|
||||
entry.Offset = file.View.ReadUInt32 (index_offset+0x20);
|
||||
entry.Size = file.View.ReadUInt32 (index_offset+0x24);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
index_offset += 0x28;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class CgV1Opener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "CG/PARSLEY/1"; } }
|
||||
public override string Description { get { return "Software House Parsley CG archive"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public CgV1Opener ()
|
||||
{
|
||||
Extensions = new string[] { "" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (0);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
var arc_name = Path.GetFileName (file.Name);
|
||||
bool is_cg = arc_name == "CG";
|
||||
|
||||
using (var index = file.CreateStream())
|
||||
{
|
||||
index.Position = 4;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var name = index.ReadCString();
|
||||
if (string.IsNullOrWhiteSpace (name) || name.Length > 0x100)
|
||||
return null;
|
||||
var entry = FormatCatalog.Instance.Create<Entry> (name);
|
||||
entry.Offset = index.ReadUInt32();
|
||||
if (entry.Offset >= file.MaxOffset || entry.Offset < index.Position)
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
}
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var entry = dir[i];
|
||||
long next_offset = i+1 < count ? dir[i+1].Offset : file.MaxOffset;
|
||||
entry.Size = (uint)(next_offset - entry.Offset);
|
||||
if (is_cg)
|
||||
entry.Type = "image";
|
||||
}
|
||||
if (is_cg)
|
||||
{
|
||||
var palette_entry = dir.FirstOrDefault (e => e.Name.Equals ("Palette", StringComparison.InvariantCultureIgnoreCase));
|
||||
if (palette_entry != null && 1 == file.View.ReadByte (palette_entry.Offset+8))
|
||||
{
|
||||
var palette = ImageFormat.ReadPalette (file, palette_entry.Offset+9, 0x100, PaletteFormat.Rgb);
|
||||
return new CgArchive (file, this, dir, palette);
|
||||
}
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
|
||||
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
|
||||
{
|
||||
int type = arc.File.View.ReadByte (entry.Offset+8);
|
||||
if (type > 1)
|
||||
return base.OpenImage (arc, entry);
|
||||
var info = new CgMetaData {
|
||||
Width = arc.File.View.ReadUInt32 (entry.Offset),
|
||||
Height = arc.File.View.ReadUInt32 (entry.Offset+4),
|
||||
BPP = 8,
|
||||
HasPalette = type == 1,
|
||||
};
|
||||
var cg_arc = arc as CgArchive;
|
||||
if (cg_arc != null)
|
||||
info.DefaultPalette = cg_arc.DefaultPalette;
|
||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
return new CgDecoder (input, info);
|
||||
}
|
||||
}
|
||||
|
||||
internal class CgMetaData : ImageMetaData
|
||||
{
|
||||
public bool HasPalette;
|
||||
public BitmapPalette DefaultPalette;
|
||||
}
|
||||
|
||||
internal class CgArchive : ArcFile
|
||||
{
|
||||
public readonly BitmapPalette DefaultPalette;
|
||||
|
||||
public CgArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, BitmapPalette palette)
|
||||
: base (arc, impl, dir)
|
||||
{
|
||||
DefaultPalette = palette;
|
||||
}
|
||||
}
|
||||
|
||||
internal class CgDecoder : BinaryImageDecoder
|
||||
{
|
||||
bool m_has_palette;
|
||||
BitmapPalette m_palette;
|
||||
|
||||
public CgDecoder (IBinaryStream input, CgMetaData info) : base (input, info)
|
||||
{
|
||||
m_has_palette = info.HasPalette;
|
||||
m_palette = info.DefaultPalette;
|
||||
}
|
||||
|
||||
protected override ImageData GetImageData ()
|
||||
{
|
||||
m_input.Position = 9;
|
||||
if (m_has_palette)
|
||||
ReadPalette();
|
||||
var pixels = m_input.ReadBytes ((int)(Info.Width * Info.Height));
|
||||
if (m_palette != null)
|
||||
return ImageData.Create (Info, PixelFormats.Indexed8, m_palette, pixels);
|
||||
else
|
||||
return ImageData.Create (Info, PixelFormats.Gray8, null, pixels);
|
||||
}
|
||||
|
||||
void ReadPalette ()
|
||||
{
|
||||
m_palette = ImageFormat.ReadPalette (m_input.AsStream, 0x100, PaletteFormat.Rgb);
|
||||
m_input.Position = 0x20909;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
//! \file ArcCG.cs
|
||||
//! \date Tue Feb 16 02:48:55 2016
|
||||
//! \brief Software House Parsley CG archive.
|
||||
//! \file ArcDAT.cs
|
||||
//! \date Sat Feb 25 02:30:54 2017
|
||||
//! \brief Valkyria resource archive.
|
||||
//
|
||||
// Copyright (C) 2016 by morkt
|
||||
// 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
|
||||
@@ -23,50 +23,42 @@
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.Parsley
|
||||
namespace GameRes.Formats.Valkyria
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class CgOpener : ArchiveFormat
|
||||
public class DatOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "CG/PARSLEY"; } }
|
||||
public override string Description { get { return "Software House Parsley CG archive"; } }
|
||||
public override string Tag { get { return "DAT/VALKYRIA"; } }
|
||||
public override string Description { get { return "Valkyria resource archive"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public CgOpener ()
|
||||
{
|
||||
Extensions = new string[] { "" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (8);
|
||||
if (!IsSaneCount (count))
|
||||
uint index_size = file.View.ReadUInt32 (0);
|
||||
if (0 == index_size || index_size >= file.MaxOffset)
|
||||
return null;
|
||||
int first_offset = file.View.ReadInt32 (0x2C);
|
||||
if (12 + count*0x28 != first_offset)
|
||||
int count = (int)index_size / 0x10C;
|
||||
if (index_size != (uint)count * 0x10Cu || !IsSaneCount (count))
|
||||
return null;
|
||||
|
||||
uint index_offset = 0xC;
|
||||
uint index_offset = 4;
|
||||
long base_offset = index_offset + index_size;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var name = file.View.ReadString (index_offset, 0x20);
|
||||
if (string.IsNullOrWhiteSpace (name))
|
||||
return null;
|
||||
var name = file.View.ReadString (index_offset, 0x104);
|
||||
index_offset += 0x104;
|
||||
var entry = FormatCatalog.Instance.Create<Entry> (name);
|
||||
entry.Offset = file.View.ReadUInt32 (index_offset+0x20);
|
||||
entry.Size = file.View.ReadUInt32 (index_offset+0x24);
|
||||
entry.Offset = base_offset + file.View.ReadUInt32 (index_offset);
|
||||
entry.Size = file.View.ReadUInt32 (index_offset+4);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
index_offset += 8;
|
||||
dir.Add (entry);
|
||||
index_offset += 0x28;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
82
ArcFormats/Valkyria/ImageMAL.cs
Normal file
82
ArcFormats/Valkyria/ImageMAL.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
//! \file ImageMAL.cs
|
||||
//! \date Sat Feb 25 05:13:05 2017
|
||||
//! \brief Valkyria mask image format.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace GameRes.Formats.Valkyria
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class MalFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "MAL"; } }
|
||||
public override string Description { get { return "Valkyria mask image format"; } }
|
||||
public override uint Signature { get { return 0x4F43494D; } } // 'MICO'
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0xE);
|
||||
if (!header.AsciiEqual (4, "MSK00"))
|
||||
return null;
|
||||
return new ImageMetaData
|
||||
{
|
||||
Width = header.ToUInt16 (0xA),
|
||||
Height = header.ToUInt16 (0xC),
|
||||
BPP = 8
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
file.Position = 0xE;
|
||||
int total = (int)info.Width * (int)info.Height;
|
||||
var pixels = new byte[total + 15];
|
||||
int dst = 0;
|
||||
while (dst < total)
|
||||
{
|
||||
int count = file.ReadUInt16();
|
||||
if (count > 0x7FFF)
|
||||
{
|
||||
count &= 0x7FFF;
|
||||
file.Read (pixels, dst, count);
|
||||
dst += count;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte c = file.ReadUInt8();
|
||||
while (count --> 0)
|
||||
pixels[dst++] = c;
|
||||
}
|
||||
}
|
||||
return ImageData.Create (info, PixelFormats.Gray8, null, pixels);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("MalFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
}
|
||||
145
ArcFormats/Valkyria/ImageMG2.cs
Normal file
145
ArcFormats/Valkyria/ImageMG2.cs
Normal file
@@ -0,0 +1,145 @@
|
||||
//! \file ImageMG2.cs
|
||||
//! \date Sat Feb 25 03:08:01 2017
|
||||
//! \brief Valkyria image format.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace GameRes.Formats.Valkyria
|
||||
{
|
||||
internal class Mg2MetaData : ImageMetaData
|
||||
{
|
||||
public int ImageLength;
|
||||
public int AlphaLength;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class Mg2Format : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "MG2"; } }
|
||||
public override string Description { get { return "Valkyria image format"; } }
|
||||
public override uint Signature { get { return 0x4F43494D; } } // 'MICO'
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x10);
|
||||
if (!header.AsciiEqual (4, "CG01"))
|
||||
return null;
|
||||
int length = header.ToInt32 (8);
|
||||
using (var input = new Mg2EncryptedStream (file.AsStream, 0x10, length))
|
||||
using (var png = new BinaryStream (input, file.Name))
|
||||
{
|
||||
var info = Png.ReadMetaData (png);
|
||||
if (null == info)
|
||||
return null;
|
||||
return new Mg2MetaData
|
||||
{
|
||||
Width = info.Width,
|
||||
Height = info.Height,
|
||||
OffsetX = info.OffsetX,
|
||||
OffsetY = info.OffsetY,
|
||||
BPP = info.BPP,
|
||||
ImageLength = length,
|
||||
AlphaLength = header.ToInt32 (12)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var meta = (Mg2MetaData)info;
|
||||
BitmapSource frame;
|
||||
using (var input = new Mg2EncryptedStream (file.AsStream, 0x10, meta.ImageLength))
|
||||
{
|
||||
var decoder = new PngBitmapDecoder (input, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||
frame = decoder.Frames[0];
|
||||
if (0 == meta.AlphaLength)
|
||||
{
|
||||
frame.Freeze();
|
||||
return new ImageData (frame, info);
|
||||
}
|
||||
}
|
||||
if (frame.Format.BitsPerPixel != 32)
|
||||
frame = new FormatConvertedBitmap (frame, PixelFormats.Bgr32, null, 0);
|
||||
int stride = frame.PixelWidth * 4;
|
||||
var pixels = new byte[stride * (int)meta.Height];
|
||||
frame.CopyPixels (pixels, stride, 0);
|
||||
|
||||
using (var input = new Mg2EncryptedStream (file.AsStream, 0x10+meta.ImageLength, meta.AlphaLength))
|
||||
{
|
||||
var decoder = new PngBitmapDecoder (input, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||
BitmapSource alpha_frame = decoder.Frames[0];
|
||||
if (alpha_frame.PixelWidth != frame.PixelWidth || alpha_frame.PixelHeight != frame.PixelHeight)
|
||||
return ImageData.Create (info, PixelFormats.Bgr32, null, pixels, stride);
|
||||
|
||||
alpha_frame = new FormatConvertedBitmap (alpha_frame, PixelFormats.Gray8, null, 0);
|
||||
var alpha = new byte[alpha_frame.PixelWidth * alpha_frame.PixelHeight];
|
||||
alpha_frame.CopyPixels (alpha, alpha_frame.PixelWidth, 0);
|
||||
|
||||
int src = 0;
|
||||
for (int dst = 3; dst < pixels.Length; dst += 4)
|
||||
{
|
||||
pixels[dst] = alpha[src++];
|
||||
}
|
||||
return ImageData.Create (info, PixelFormats.Bgra32, null, pixels, stride);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("Mg2Format.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal class Mg2EncryptedStream : StreamRegion
|
||||
{
|
||||
readonly int m_threshold;
|
||||
|
||||
public Mg2EncryptedStream (Stream main, int offset, int length)
|
||||
: base (main, offset, length, true)
|
||||
{
|
||||
m_threshold = length / 5;
|
||||
}
|
||||
|
||||
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)pos++;
|
||||
return read;
|
||||
}
|
||||
|
||||
public override int ReadByte ()
|
||||
{
|
||||
long pos = Position;
|
||||
int b = base.ReadByte();
|
||||
if (b != -1 && pos < m_threshold)
|
||||
b ^= (byte)pos;
|
||||
return b;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.2.12")]
|
||||
[assembly: AssemblyFileVersion ("1.0.2.12")]
|
||||
[assembly: AssemblyVersion ("1.0.3.14")]
|
||||
[assembly: AssemblyFileVersion ("1.0.3.14")]
|
||||
|
||||
@@ -33,7 +33,6 @@ IN THE SOFTWARE.
|
||||
Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
|
||||
ShowInTaskbar="False" WindowStartupLocation="CenterOwner">
|
||||
<Window.Resources>
|
||||
<sys:Uri x:Key="DevLink">https://github.com/morkt/GARbro#readme</sys:Uri>
|
||||
<local:BooleanToVisibiltyConverter x:Key="guiBoolToVisibilityConverter" />
|
||||
<local:CanCreateConverter x:Key="guiCanCreateConverter"/>
|
||||
<CollectionViewSource x:Key="ArcFormatsSource" Source="{Binding Source={x:Static gr:FormatCatalog.Instance}, Path=ArcFormats, Mode=OneWay}">
|
||||
|
||||
@@ -189,20 +189,8 @@ namespace GARbro.GUI
|
||||
|
||||
private void Hyperlink_RequestNavigate (object sender, RequestNavigateEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (e.Uri.IsAbsoluteUri)
|
||||
{
|
||||
Process.Start (new ProcessStartInfo (e.Uri.AbsoluteUri));
|
||||
e.Handled = true;
|
||||
}
|
||||
else
|
||||
throw new ApplicationException ("URI is not absolute");
|
||||
}
|
||||
catch (Exception X)
|
||||
{
|
||||
Trace.WriteLine ("Link navigation failed: "+X.Message, e.Uri.ToString());
|
||||
}
|
||||
if (App.NavigateUri (e.Uri))
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
<Application x:Class="GARbro.GUI.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:sys="clr-namespace:System;assembly=System"
|
||||
StartupUri="MainWindow.xaml" Startup="ApplicationStartup"
|
||||
ShutdownMode="OnMainWindowClose" Exit="ApplicationExit">
|
||||
<Application.Resources>
|
||||
<sys:Uri x:Key="DevLink">https://github.com/morkt/GARbro#readme</sys:Uri>
|
||||
<sys:Uri x:Key="UpdateUrl">https://morkt.github.io/GARbro/version.xml</sys:Uri>
|
||||
<BitmapImage x:Key="IconSearch" UriSource="pack://application:,,,/Images/search4files.ico" />
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
|
||||
@@ -29,6 +29,7 @@ using System.Diagnostics;
|
||||
using GARbro.GUI.Properties;
|
||||
using GameRes;
|
||||
using GameRes.Compression;
|
||||
using System.Reflection;
|
||||
|
||||
namespace GARbro.GUI
|
||||
{
|
||||
@@ -37,9 +38,8 @@ namespace GARbro.GUI
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
const StringComparison StringIgnoreCase = StringComparison.OrdinalIgnoreCase;
|
||||
|
||||
public static string Name { get { return "GARbro"; } }
|
||||
public static string Name { get { return "GARbro"; } }
|
||||
public static string FormatsDat { get { return "Formats.dat"; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initial browsing directory.
|
||||
@@ -79,9 +79,24 @@ namespace GARbro.GUI
|
||||
if (string.IsNullOrEmpty (InitPath))
|
||||
InitPath = Directory.GetCurrentDirectory();
|
||||
|
||||
string scheme_file = Path.Combine (FormatCatalog.Instance.DataDirectory, "Formats.dat");
|
||||
DeserializeScheme (Path.Combine (FormatCatalog.Instance.DataDirectory, FormatsDat));
|
||||
DeserializeScheme (Path.Combine (GetLocalAppDataFolder(), FormatsDat));
|
||||
}
|
||||
|
||||
public string GetLocalAppDataFolder ()
|
||||
{
|
||||
string local_app_data = Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData);
|
||||
var attribs = Assembly.GetExecutingAssembly().GetCustomAttributes (typeof(AssemblyCompanyAttribute), false);
|
||||
string company = attribs.Length > 0 ? ((AssemblyCompanyAttribute)attribs[0]).Company : "";
|
||||
return Path.Combine (local_app_data, company, Name);
|
||||
}
|
||||
|
||||
public void DeserializeScheme (string scheme_file)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists (scheme_file))
|
||||
return;
|
||||
using (var file = File.OpenRead (scheme_file))
|
||||
FormatCatalog.Instance.DeserializeScheme (file);
|
||||
}
|
||||
@@ -116,5 +131,24 @@ namespace GARbro.GUI
|
||||
if (Settings.Default.winState == System.Windows.WindowState.Minimized)
|
||||
Settings.Default.winState = System.Windows.WindowState.Normal;
|
||||
}
|
||||
|
||||
public static bool NavigateUri (Uri uri)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (uri.IsAbsoluteUri)
|
||||
{
|
||||
Process.Start (new ProcessStartInfo (uri.AbsoluteUri));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
throw new ApplicationException ("URI is not absolute");
|
||||
}
|
||||
catch (Exception X)
|
||||
{
|
||||
Trace.WriteLine ("Link navigation failed: "+X.Message, uri.ToString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,6 +154,7 @@
|
||||
<Compile Include="GarCreate.cs" />
|
||||
<Compile Include="GarExtract.cs" />
|
||||
<Compile Include="GarOperation.cs" />
|
||||
<Compile Include="GarUpdate.cs" />
|
||||
<Compile Include="HistoryStack.cs" />
|
||||
<Compile Include="ImagePreview.cs" />
|
||||
<Compile Include="ListViewEx.cs" />
|
||||
@@ -173,6 +174,9 @@
|
||||
<Compile Include="TextViewer.xaml.cs">
|
||||
<DependentUpon>TextViewer.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="UpdateDialog.xaml.cs">
|
||||
<DependentUpon>UpdateDialog.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Utility.cs" />
|
||||
<Compile Include="ViewModel.cs" />
|
||||
<Page Include="AboutBox.xaml">
|
||||
@@ -227,6 +231,10 @@
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="UpdateDialog.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
|
||||
308
GUI/GarUpdate.cs
Normal file
308
GUI/GarUpdate.cs
Normal file
@@ -0,0 +1,308 @@
|
||||
//! \file GarUpdate.cs
|
||||
//! \date Tue Feb 14 00:02:14 2017
|
||||
//! \brief Application update routines.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Net;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Xml;
|
||||
using GameRes;
|
||||
using GARbro.GUI.Strings;
|
||||
using System.IO;
|
||||
|
||||
namespace GARbro.GUI
|
||||
{
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
GarUpdate m_updater;
|
||||
|
||||
private void InitUpdatesChecker ()
|
||||
{
|
||||
var update_url = App.Resources["UpdateUrl"] as Uri;
|
||||
m_updater = new GarUpdate (this, update_url);
|
||||
m_updater.CanExecuteChanged += (s, e) => CommandManager.InvalidateRequerySuggested();
|
||||
}
|
||||
|
||||
public void CanExecuteUpdate (object sender, CanExecuteRoutedEventArgs e)
|
||||
{
|
||||
e.CanExecute = m_updater.CanExecute (e.Parameter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle "Check for updates" command.
|
||||
/// </summary>
|
||||
private void CheckUpdatesExec (object sender, ExecutedRoutedEventArgs e)
|
||||
{
|
||||
m_updater.Execute (e.Parameter);
|
||||
}
|
||||
}
|
||||
|
||||
public class GarUpdateInfo
|
||||
{
|
||||
public Version ReleaseVersion { get; set; }
|
||||
public Uri ReleaseUrl { get; set; }
|
||||
public string ReleaseNotes { get; set; }
|
||||
public int FormatsVersion { get; set; }
|
||||
public Uri FormatsUrl { get; set; }
|
||||
public IDictionary<string, Version> Assemblies { get; set; }
|
||||
|
||||
public static GarUpdateInfo Parse (XmlDocument xml)
|
||||
{
|
||||
var root = xml.DocumentElement.SelectSingleNode ("/GARbro");
|
||||
if (null == root)
|
||||
return null;
|
||||
var info = new GarUpdateInfo
|
||||
{
|
||||
ReleaseVersion = Version.Parse (GetInnerText (root.SelectSingleNode ("Release/Version"))),
|
||||
ReleaseUrl = new Uri (GetInnerText (root.SelectSingleNode ("Release/Url"))),
|
||||
ReleaseNotes = GetInnerText (root.SelectSingleNode ("Release/Notes")),
|
||||
|
||||
FormatsVersion = Int32.Parse (GetInnerText (root.SelectSingleNode ("FormatsData/FileVersion"))),
|
||||
FormatsUrl = new Uri (GetInnerText (root.SelectSingleNode ("FormatsData/Url"))),
|
||||
Assemblies = ParseAssemblies (root.SelectNodes ("FormatsData/Requires/Assembly")),
|
||||
};
|
||||
return info;
|
||||
}
|
||||
|
||||
static string GetInnerText (XmlNode node)
|
||||
{
|
||||
// XXX node?.InnerText ?? ""
|
||||
return node != null ? node.InnerText : "";
|
||||
}
|
||||
|
||||
static IDictionary<string, Version> ParseAssemblies (XmlNodeList nodes)
|
||||
{
|
||||
var dict = new Dictionary<string, Version>();
|
||||
foreach (XmlNode node in nodes)
|
||||
{
|
||||
var attr = node.Attributes;
|
||||
var name = attr["Name"];
|
||||
var version = attr["Version"];
|
||||
if (name != null && version != null)
|
||||
dict[name.Value] = Version.Parse (version.Value);
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class GarUpdate : ICommand, IDisposable
|
||||
{
|
||||
private readonly MainWindow m_main;
|
||||
private readonly BackgroundWorker m_update_checker = new BackgroundWorker();
|
||||
private readonly Uri m_url;
|
||||
|
||||
const int RequestTimeout = 20000; // milliseconds
|
||||
|
||||
public GarUpdate (MainWindow main, Uri url)
|
||||
{
|
||||
m_main = main;
|
||||
m_url = url;
|
||||
m_update_checker.DoWork += StartUpdatesCheck;
|
||||
m_update_checker.RunWorkerCompleted += UpdatesCheckComplete;
|
||||
}
|
||||
|
||||
public void Execute (object parameter)
|
||||
{
|
||||
if (!m_update_checker.IsBusy)
|
||||
m_update_checker.RunWorkerAsync();
|
||||
}
|
||||
|
||||
public bool CanExecute (object parameter)
|
||||
{
|
||||
return !m_update_checker.IsBusy;
|
||||
}
|
||||
|
||||
public event EventHandler CanExecuteChanged;
|
||||
|
||||
void OnCanExecuteChanged ()
|
||||
{
|
||||
var handler = CanExecuteChanged;
|
||||
if (handler != null)
|
||||
handler (this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void StartUpdatesCheck (object sender, DoWorkEventArgs e)
|
||||
{
|
||||
OnCanExecuteChanged();
|
||||
if (m_url != null)
|
||||
e.Result = Check (m_url);
|
||||
}
|
||||
|
||||
private void UpdatesCheckComplete (object sender, RunWorkerCompletedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (e.Error != null)
|
||||
{
|
||||
m_main.SetStatusText (string.Format ("{0} {1}", guiStrings.MsgUpdateFailed, e.Error.Message));
|
||||
return;
|
||||
}
|
||||
else if (e.Cancelled)
|
||||
return;
|
||||
var result = e.Result as GarUpdateInfo;
|
||||
if (null == result)
|
||||
{
|
||||
m_main.SetStatusText (guiStrings.MsgNoUpdates);
|
||||
return;
|
||||
}
|
||||
ShowUpdateResult (result);
|
||||
}
|
||||
finally
|
||||
{
|
||||
OnCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
|
||||
UpdateDialog m_dialog;
|
||||
Uri m_formats_url;
|
||||
|
||||
private void ShowUpdateResult (GarUpdateInfo result)
|
||||
{
|
||||
var app_version = Assembly.GetExecutingAssembly().GetName().Version;
|
||||
var db_version = FormatCatalog.Instance.CurrentSchemeVersion;
|
||||
bool has_app_update = app_version < result.ReleaseVersion;
|
||||
bool has_db_update = db_version < result.FormatsVersion && CheckAssemblies (result.Assemblies);
|
||||
if (!has_app_update && !has_db_update)
|
||||
{
|
||||
m_main.SetStatusText (guiStrings.MsgUpToDate);
|
||||
return;
|
||||
}
|
||||
m_formats_url = result.FormatsUrl;
|
||||
m_dialog = new UpdateDialog (result, has_app_update, has_db_update);
|
||||
m_dialog.Owner = m_main;
|
||||
m_dialog.FormatsDownload.Click += StartFormatsDownload;
|
||||
m_dialog.ShowDialog();
|
||||
}
|
||||
|
||||
private async void StartFormatsDownload (object control, RoutedEventArgs e)
|
||||
{
|
||||
var dialog = m_dialog;
|
||||
try
|
||||
{
|
||||
dialog.FormatsDownload.IsEnabled = false;
|
||||
var app_data_folder = m_main.App.GetLocalAppDataFolder();
|
||||
Directory.CreateDirectory (app_data_folder);
|
||||
using (var client = new WebClientEx())
|
||||
using (var tmp_file = new GARbro.Shell.TemporaryFile (app_data_folder, Path.GetRandomFileName()))
|
||||
{
|
||||
client.Timeout = RequestTimeout;
|
||||
await client.DownloadFileTaskAsync (m_formats_url, tmp_file.Name);
|
||||
|
||||
m_main.App.DeserializeScheme (tmp_file.Name);
|
||||
var local_formats_dat = Path.Combine (app_data_folder, App.FormatsDat);
|
||||
if (!GARbro.Shell.File.Rename (tmp_file.Name, local_formats_dat))
|
||||
throw new Win32Exception (GARbro.Shell.File.GetLastError());
|
||||
}
|
||||
SetFormatsUpdateStatus (dialog, guiStrings.MsgUpdateComplete);
|
||||
}
|
||||
catch (Exception X)
|
||||
{
|
||||
SetFormatsUpdateStatus (dialog, guiStrings.MsgDownloadFailed, X.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
dialog.FormatsDownload.Visibility = Visibility.Hidden;
|
||||
}
|
||||
}
|
||||
|
||||
void SetFormatsUpdateStatus (UpdateDialog dialog, string text1, string text2 = null)
|
||||
{
|
||||
if (dialog.IsClosed)
|
||||
m_main.SetStatusText (text1);
|
||||
else if (null == text2)
|
||||
dialog.FormatsUpdateText.Text = text1;
|
||||
else
|
||||
dialog.FormatsUpdateText.Text = string.Format ("{0}\n{1}", text1, text2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if loaded assemblies match required versions.
|
||||
/// </summary>
|
||||
bool CheckAssemblies (IDictionary<string, Version> assemblies)
|
||||
{
|
||||
var loaded = AppDomain.CurrentDomain.GetAssemblies().Select (a => a.GetName())
|
||||
.ToDictionary (a => a.Name, a => a.Version);
|
||||
foreach (var item in assemblies)
|
||||
{
|
||||
if (!loaded.ContainsKey (item.Key))
|
||||
return false;
|
||||
if (loaded[item.Key] < item.Value)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
GarUpdateInfo Check (Uri version_url)
|
||||
{
|
||||
var request = WebRequest.Create (version_url);
|
||||
request.Timeout = RequestTimeout;
|
||||
var response = (HttpWebResponse)request.GetResponse();
|
||||
using (var input = response.GetResponseStream())
|
||||
{
|
||||
var xml = new XmlDocument();
|
||||
xml.Load (input);
|
||||
return GarUpdateInfo.Parse (xml);
|
||||
}
|
||||
}
|
||||
|
||||
bool m_disposed = false;
|
||||
public void Dispose ()
|
||||
{
|
||||
if (!m_disposed)
|
||||
{
|
||||
m_update_checker.Dispose();
|
||||
m_disposed = true;
|
||||
}
|
||||
GC.SuppressFinalize (this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WebClient with timeout setting.
|
||||
/// </summary>
|
||||
internal class WebClientEx : WebClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Request timeout, in milliseconds.
|
||||
/// </summary>
|
||||
public int Timeout { get; set; }
|
||||
|
||||
public WebClientEx ()
|
||||
{
|
||||
Timeout = 60000;
|
||||
}
|
||||
|
||||
protected override WebRequest GetWebRequest (Uri uri)
|
||||
{
|
||||
var request = base.GetWebRequest (uri);
|
||||
request.Timeout = Timeout;
|
||||
return request;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,6 +151,7 @@
|
||||
</MenuItem>
|
||||
<MenuItem Header="{x:Static s:guiStrings.MenuHelp}">
|
||||
<MenuItem Header="{x:Static s:guiStrings.MenuAbout}" Command="{x:Static local:Commands.About}"/>
|
||||
<MenuItem Header="{x:Static s:guiStrings.MenuCheckUpdates}" Command="{x:Static local:Commands.CheckUpdates}"/>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
<Separator Height="1" Margin="0"/>
|
||||
@@ -402,6 +403,7 @@
|
||||
<CommandBinding Command="{x:Static local:Commands.HideMenuBar}" Executed="HideMenuBarExec" CanExecute="CanExecuteAlways"/>
|
||||
<CommandBinding Command="{x:Static local:Commands.HideToolBar}" Executed="HideToolBarExec" CanExecute="CanExecuteAlways"/>
|
||||
<CommandBinding Command="{x:Static local:Commands.About}" Executed="AboutExec" CanExecute="CanExecuteAlways"/>
|
||||
<CommandBinding Command="{x:Static local:Commands.CheckUpdates}" Executed="CheckUpdatesExec" CanExecute="CanExecuteUpdate"/>
|
||||
<CommandBinding Command="{x:Static local:Commands.Exit}" Executed="ExitExec" CanExecute="CanExecuteAlways"/>
|
||||
</Window.CommandBindings>
|
||||
</Window>
|
||||
|
||||
@@ -54,6 +54,8 @@ namespace GARbro.GUI
|
||||
{
|
||||
private App m_app;
|
||||
|
||||
public App App { get { return m_app; } }
|
||||
|
||||
const StringComparison StringIgnoreCase = StringComparison.CurrentCultureIgnoreCase;
|
||||
|
||||
public MainWindow()
|
||||
@@ -64,6 +66,7 @@ namespace GARbro.GUI
|
||||
if (this.Left < 0) this.Left = 0;
|
||||
InitDirectoryChangesWatcher();
|
||||
InitPreviewPane();
|
||||
InitUpdatesChecker();
|
||||
|
||||
if (null == Settings.Default.appRecentFiles)
|
||||
Settings.Default.appRecentFiles = new StringCollection();
|
||||
@@ -1478,6 +1481,7 @@ namespace GARbro.GUI
|
||||
public static readonly RoutedCommand SortBy = new RoutedCommand();
|
||||
public static readonly RoutedCommand Exit = new RoutedCommand();
|
||||
public static readonly RoutedCommand About = new RoutedCommand();
|
||||
public static readonly RoutedCommand CheckUpdates = new RoutedCommand();
|
||||
public static readonly RoutedCommand GoBack = new RoutedCommand();
|
||||
public static readonly RoutedCommand GoForward = new RoutedCommand();
|
||||
public static readonly RoutedCommand DeleteItem = new RoutedCommand();
|
||||
|
||||
@@ -51,5 +51,5 @@ using System.Windows;
|
||||
// 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.3.25.1723")]
|
||||
[assembly: AssemblyFileVersion ("1.3.25.1723")]
|
||||
[assembly: AssemblyVersion ("1.4.26.1759")]
|
||||
[assembly: AssemblyFileVersion ("1.4.26.1759")]
|
||||
|
||||
108
GUI/Strings/guiStrings.Designer.cs
generated
108
GUI/Strings/guiStrings.Designer.cs
generated
@@ -96,6 +96,15 @@ namespace GARbro.GUI.Strings {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to _Download.
|
||||
/// </summary>
|
||||
public static string ButtonDownload {
|
||||
get {
|
||||
return ResourceManager.GetString("ButtonDownload", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Extract.
|
||||
/// </summary>
|
||||
@@ -456,6 +465,15 @@ namespace GARbro.GUI.Strings {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Release notes.
|
||||
/// </summary>
|
||||
public static string LabelReleaseNotes {
|
||||
get {
|
||||
return ResourceManager.GetString("LabelReleaseNotes", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Skip incovertible files..
|
||||
/// </summary>
|
||||
@@ -474,6 +492,15 @@ namespace GARbro.GUI.Strings {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to _Check for updates....
|
||||
/// </summary>
|
||||
public static string MenuCheckUpdates {
|
||||
get {
|
||||
return ResourceManager.GetString("MenuCheckUpdates", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to E_xit.
|
||||
/// </summary>
|
||||
@@ -636,6 +663,15 @@ namespace GARbro.GUI.Strings {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Update download failed..
|
||||
/// </summary>
|
||||
public static string MsgDownloadFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("MsgDownloadFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to archive is empty.
|
||||
/// </summary>
|
||||
@@ -771,6 +807,15 @@ namespace GARbro.GUI.Strings {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to No updates currently available..
|
||||
/// </summary>
|
||||
public static string MsgNoUpdates {
|
||||
get {
|
||||
return ResourceManager.GetString("MsgNoUpdates", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to File {0}
|
||||
///already exists.
|
||||
@@ -837,6 +882,42 @@ namespace GARbro.GUI.Strings {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Formats database update available..
|
||||
/// </summary>
|
||||
public static string MsgUpdateAvailable {
|
||||
get {
|
||||
return ResourceManager.GetString("MsgUpdateAvailable", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Formats database updated..
|
||||
/// </summary>
|
||||
public static string MsgUpdateComplete {
|
||||
get {
|
||||
return ResourceManager.GetString("MsgUpdateComplete", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Update check failed..
|
||||
/// </summary>
|
||||
public static string MsgUpdateFailed {
|
||||
get {
|
||||
return ResourceManager.GetString("MsgUpdateFailed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to GARbro version is up to date..
|
||||
/// </summary>
|
||||
public static string MsgUpToDate {
|
||||
get {
|
||||
return ResourceManager.GetString("MsgUpToDate", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Version {0}.
|
||||
/// </summary>
|
||||
@@ -1093,6 +1174,15 @@ namespace GARbro.GUI.Strings {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to New version available:.
|
||||
/// </summary>
|
||||
public static string TextNewVersion {
|
||||
get {
|
||||
return ResourceManager.GetString("TextNewVersion", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Archive parameters.
|
||||
/// </summary>
|
||||
@@ -1138,6 +1228,24 @@ namespace GARbro.GUI.Strings {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Application update.
|
||||
/// </summary>
|
||||
public static string TextUpdateTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("TextUpdateTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Visit download page.
|
||||
/// </summary>
|
||||
public static string TextVisitPage {
|
||||
get {
|
||||
return ResourceManager.GetString("TextVisitPage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Either WAV, MP3 or OGG.
|
||||
/// </summary>
|
||||
|
||||
@@ -493,4 +493,40 @@
|
||||
<data name="TextFileAlreadyExists" xml:space="preserve">
|
||||
<value>대상폴더에 파일 {0} 이(가) 이미 존재합니다.</value>
|
||||
</data>
|
||||
<data name="ButtonDownload" xml:space="preserve">
|
||||
<value>다운로드</value>
|
||||
</data>
|
||||
<data name="LabelReleaseNotes" xml:space="preserve">
|
||||
<value>릴리스 정보</value>
|
||||
</data>
|
||||
<data name="MenuCheckUpdates" xml:space="preserve">
|
||||
<value>업데이트 확인...</value>
|
||||
</data>
|
||||
<data name="MsgDownloadFailed" xml:space="preserve">
|
||||
<value>업데이트 다운로드에 실패했습니다.</value>
|
||||
</data>
|
||||
<data name="MsgNoUpdates" xml:space="preserve">
|
||||
<value>현재 사용가능한 업데이트가 없습니다.</value>
|
||||
</data>
|
||||
<data name="MsgUpdateAvailable" xml:space="preserve">
|
||||
<value>포맷 데이터베이스를 업데이트 하실수 있습니다.</value>
|
||||
</data>
|
||||
<data name="MsgUpdateComplete" xml:space="preserve">
|
||||
<value>포맷 데이터베이스를 업데이트했습니다.</value>
|
||||
</data>
|
||||
<data name="MsgUpdateFailed" xml:space="preserve">
|
||||
<value>업데이트 확인에 실패했습니다.</value>
|
||||
</data>
|
||||
<data name="MsgUpToDate" xml:space="preserve">
|
||||
<value>GARbro이 최신버전입니다.</value>
|
||||
</data>
|
||||
<data name="TextNewVersion" xml:space="preserve">
|
||||
<value>새로운 버전을 사용하실 수 있습니다:</value>
|
||||
</data>
|
||||
<data name="TextUpdateTitle" xml:space="preserve">
|
||||
<value>응용프로그램 업데이트</value>
|
||||
</data>
|
||||
<data name="TextVisitPage" xml:space="preserve">
|
||||
<value>다운로드 페이지 방문</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -497,4 +497,40 @@ Overwrite?</value>
|
||||
<data name="TextFileAlreadyExists" xml:space="preserve">
|
||||
<value>File {0} already exists in the destination folder.</value>
|
||||
</data>
|
||||
<data name="ButtonDownload" xml:space="preserve">
|
||||
<value>_Download</value>
|
||||
</data>
|
||||
<data name="LabelReleaseNotes" xml:space="preserve">
|
||||
<value>Release notes</value>
|
||||
</data>
|
||||
<data name="MenuCheckUpdates" xml:space="preserve">
|
||||
<value>_Check for updates...</value>
|
||||
</data>
|
||||
<data name="MsgNoUpdates" xml:space="preserve">
|
||||
<value>No updates currently available.</value>
|
||||
</data>
|
||||
<data name="MsgUpdateAvailable" xml:space="preserve">
|
||||
<value>Formats database update available.</value>
|
||||
</data>
|
||||
<data name="MsgUpdateComplete" xml:space="preserve">
|
||||
<value>Formats database updated.</value>
|
||||
</data>
|
||||
<data name="MsgUpdateFailed" xml:space="preserve">
|
||||
<value>Update check failed.</value>
|
||||
</data>
|
||||
<data name="MsgUpToDate" xml:space="preserve">
|
||||
<value>GARbro version is up to date.</value>
|
||||
</data>
|
||||
<data name="TextNewVersion" xml:space="preserve">
|
||||
<value>New version available:</value>
|
||||
</data>
|
||||
<data name="TextUpdateTitle" xml:space="preserve">
|
||||
<value>Application update</value>
|
||||
</data>
|
||||
<data name="TextVisitPage" xml:space="preserve">
|
||||
<value>Visit download page</value>
|
||||
</data>
|
||||
<data name="MsgDownloadFailed" xml:space="preserve">
|
||||
<value>Update download failed.</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -518,4 +518,40 @@
|
||||
<data name="TextFileAlreadyExists" xml:space="preserve">
|
||||
<value>Файл с именем {0} уже существует.</value>
|
||||
</data>
|
||||
<data name="ButtonDownload" xml:space="preserve">
|
||||
<value>Обновить</value>
|
||||
</data>
|
||||
<data name="LabelReleaseNotes" xml:space="preserve">
|
||||
<value>Примечания к выпуску</value>
|
||||
</data>
|
||||
<data name="MenuCheckUpdates" xml:space="preserve">
|
||||
<value>Проверить обновления...</value>
|
||||
</data>
|
||||
<data name="MsgDownloadFailed" xml:space="preserve">
|
||||
<value>Не удалось обновить базу форматов.</value>
|
||||
</data>
|
||||
<data name="MsgNoUpdates" xml:space="preserve">
|
||||
<value>Обновления недоступны.</value>
|
||||
</data>
|
||||
<data name="MsgUpdateAvailable" xml:space="preserve">
|
||||
<value>Доступна обновлённая база форматов.</value>
|
||||
</data>
|
||||
<data name="MsgUpdateComplete" xml:space="preserve">
|
||||
<value>База форматов успешно обновлена.</value>
|
||||
</data>
|
||||
<data name="MsgUpdateFailed" xml:space="preserve">
|
||||
<value>Сбой проверки обновлений.</value>
|
||||
</data>
|
||||
<data name="MsgUpToDate" xml:space="preserve">
|
||||
<value>Установлена актуальная версия GARbro.</value>
|
||||
</data>
|
||||
<data name="TextNewVersion" xml:space="preserve">
|
||||
<value>Доступна новая версия:</value>
|
||||
</data>
|
||||
<data name="TextUpdateTitle" xml:space="preserve">
|
||||
<value>Обновление программы</value>
|
||||
</data>
|
||||
<data name="TextVisitPage" xml:space="preserve">
|
||||
<value>Перейти на страницу загрузки</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -505,4 +505,52 @@
|
||||
<data name="TextFileAlreadyExists" xml:space="preserve">
|
||||
<value>文件{0}已经存在。</value>
|
||||
</data>
|
||||
<data name="ButtonDownload" xml:space="preserve">
|
||||
<value>Download</value>
|
||||
<comment>translation pending</comment>
|
||||
</data>
|
||||
<data name="LabelReleaseNotes" xml:space="preserve">
|
||||
<value>Release notes</value>
|
||||
<comment>translation pending</comment>
|
||||
</data>
|
||||
<data name="MenuCheckUpdates" xml:space="preserve">
|
||||
<value>Check for updates...</value>
|
||||
<comment>translation pending</comment>
|
||||
</data>
|
||||
<data name="MsgDownloadFailed" xml:space="preserve">
|
||||
<value>Update download failed.</value>
|
||||
<comment>translation pending</comment>
|
||||
</data>
|
||||
<data name="MsgNoUpdates" xml:space="preserve">
|
||||
<value>No updates currently available.</value>
|
||||
<comment>translation pending</comment>
|
||||
</data>
|
||||
<data name="MsgUpdateAvailable" xml:space="preserve">
|
||||
<value>Formats database update available.</value>
|
||||
<comment>translation pending</comment>
|
||||
</data>
|
||||
<data name="MsgUpdateComplete" xml:space="preserve">
|
||||
<value>Formats database updated.</value>
|
||||
<comment>translation pending</comment>
|
||||
</data>
|
||||
<data name="MsgUpdateFailed" xml:space="preserve">
|
||||
<value>Update check failed.</value>
|
||||
<comment>translation pending</comment>
|
||||
</data>
|
||||
<data name="MsgUpToDate" xml:space="preserve">
|
||||
<value>GARbro version is up to date.</value>
|
||||
<comment>translation pending</comment>
|
||||
</data>
|
||||
<data name="TextNewVersion" xml:space="preserve">
|
||||
<value>New version available:</value>
|
||||
<comment>translation pending</comment>
|
||||
</data>
|
||||
<data name="TextUpdateTitle" xml:space="preserve">
|
||||
<value>Application update</value>
|
||||
<comment>translation pending</comment>
|
||||
</data>
|
||||
<data name="TextVisitPage" xml:space="preserve">
|
||||
<value>Visit download page</value>
|
||||
<comment>translation pending</comment>
|
||||
</data>
|
||||
</root>
|
||||
61
GUI/UpdateDialog.xaml
Normal file
61
GUI/UpdateDialog.xaml
Normal file
@@ -0,0 +1,61 @@
|
||||
<!-- Game Resource browser
|
||||
|
||||
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.
|
||||
-->
|
||||
<Window x:Class="GARbro.GUI.UpdateDialog"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:s="clr-namespace:GARbro.GUI.Strings"
|
||||
Title="{x:Static s:guiStrings.TextUpdateTitle}" ShowInTaskbar="False" WindowStartupLocation="CenterOwner"
|
||||
Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
|
||||
SizeToContent="WidthAndHeight" ResizeMode="NoResize">
|
||||
<DockPanel>
|
||||
<DockPanel DockPanel.Dock="Bottom" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
|
||||
<Border BorderThickness="0,1,0,0" BorderBrush="Black">
|
||||
<Button HorizontalAlignment="Right" Content="{x:Static s:guiStrings.ButtonOK}"
|
||||
Margin="10" Width="75" Height="25" Click="Button_Click" IsCancel="True"/>
|
||||
</Border>
|
||||
</DockPanel>
|
||||
<Image DockPanel.Dock="Left" Source="Images/64x64/actions.png" Width="32" Height="32" Margin="10,20"
|
||||
SnapsToDevicePixels="True" VerticalAlignment="Top" RenderOptions.BitmapScalingMode="HighQuality"/>
|
||||
<StackPanel DockPanel.Dock="Right" Orientation="Vertical">
|
||||
<StackPanel x:Name="ReleasePane" Orientation="Vertical">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{x:Static s:guiStrings.TextNewVersion}" Margin="0,10,0,0"/>
|
||||
<TextBlock x:Name="ReleaseVersion" Text="{Binding ReleaseVersion}" Margin="5,10,10,0"/>
|
||||
</StackPanel>
|
||||
<Expander x:Name="ReleaseNotes" Header="{x:Static s:guiStrings.LabelReleaseNotes}" ExpandDirection="Down" IsExpanded="False" Margin="0,0,10,0">
|
||||
<TextBlock Text="{Binding ReleaseNotes}"/>
|
||||
</Expander>
|
||||
<TextBlock Margin="0,5,10,10">
|
||||
<Hyperlink NavigateUri="{Binding ReleaseUrl}" RequestNavigate="Hyperlink_RequestNavigate">
|
||||
<TextBlock Text="{x:Static s:guiStrings.TextVisitPage}" ToolTip="{Binding ReleaseUrl}"/>
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel x:Name="FormatsPane" Orientation="Vertical">
|
||||
<Separator Visibility="{Binding ElementName=ReleasePane, Path=Visibility}"/>
|
||||
<TextBlock x:Name="FormatsUpdateText" Text="{x:Static s:guiStrings.MsgUpdateAvailable}" Margin="0,10,10,10"/>
|
||||
<Button x:Name="FormatsDownload" Content="{x:Static s:guiStrings.ButtonDownload}" Width="75" Height="25" Margin="0,0,10,10" HorizontalAlignment="Left"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
34
GUI/UpdateDialog.xaml.cs
Normal file
34
GUI/UpdateDialog.xaml.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace GARbro.GUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for UpdateDialog.xaml
|
||||
/// </summary>
|
||||
public partial class UpdateDialog : Window
|
||||
{
|
||||
public UpdateDialog (GarUpdateInfo info, bool enable_release, bool enable_formats)
|
||||
{
|
||||
InitializeComponent ();
|
||||
this.ReleasePane.Visibility = enable_release ? Visibility.Visible : Visibility.Collapsed;
|
||||
this.FormatsPane.Visibility = enable_formats ? Visibility.Visible : Visibility.Collapsed;
|
||||
if (string.IsNullOrEmpty (info.ReleaseNotes))
|
||||
this.ReleaseNotes.Visibility = Visibility.Collapsed;
|
||||
this.DataContext = info;
|
||||
this.Closed += (s, e) => IsClosed = true;
|
||||
}
|
||||
|
||||
public bool IsClosed { get; private set; }
|
||||
|
||||
private void Hyperlink_RequestNavigate (object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
|
||||
{
|
||||
if (App.NavigateUri (e.Uri))
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void Button_Click (object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.DialogResult = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,11 @@ namespace GameRes
|
||||
public ushort BlockAlign;
|
||||
public ushort BitsPerSample;
|
||||
public ushort ExtraSize;
|
||||
|
||||
public void SetBPS ()
|
||||
{
|
||||
AverageBytesPerSecond = (uint)(SamplesPerSecond * Channels * BitsPerSample / 8);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class SoundInput : Stream
|
||||
|
||||
@@ -27,6 +27,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using GameRes.Strings;
|
||||
|
||||
@@ -558,7 +559,7 @@ namespace GameRes
|
||||
if (null == arc)
|
||||
{
|
||||
if (FormatCatalog.Instance.LastError is OperationCanceledException)
|
||||
throw FormatCatalog.Instance.LastError;
|
||||
ExceptionDispatchInfo.Capture (FormatCatalog.Instance.LastError).Throw();
|
||||
else
|
||||
throw new UnknownFormatException (FormatCatalog.Instance.LastError);
|
||||
}
|
||||
|
||||
@@ -254,15 +254,9 @@ namespace GameRes
|
||||
|
||||
public void DeserializeScheme (Stream input)
|
||||
{
|
||||
using (var reader = new BinaryReader (input, System.Text.Encoding.UTF8, true))
|
||||
{
|
||||
var header = reader.ReadChars (SchemeID.Length);
|
||||
if (!header.SequenceEqual (SchemeID))
|
||||
throw new FormatException ("Invalid serialization file");
|
||||
int version = reader.ReadInt32();
|
||||
if (version <= CurrentSchemeVersion)
|
||||
return;
|
||||
}
|
||||
int version = GetSerializedSchemeVersion (input);
|
||||
if (version <= CurrentSchemeVersion)
|
||||
return;
|
||||
using (var zs = new ZLibStream (input, CompressionMode.Decompress, true))
|
||||
{
|
||||
var bin = new BinaryFormatter();
|
||||
@@ -307,6 +301,17 @@ namespace GameRes
|
||||
using (var zs = new ZLibStream (output, CompressionMode.Compress, true))
|
||||
bin.Serialize (zs, db);
|
||||
}
|
||||
|
||||
public int GetSerializedSchemeVersion (Stream input)
|
||||
{
|
||||
using (var reader = new BinaryReader (input, System.Text.Encoding.UTF8, true))
|
||||
{
|
||||
var header = reader.ReadChars (SchemeID.Length);
|
||||
if (!header.SequenceEqual (SchemeID))
|
||||
throw new FormatException ("Invalid serialization file");
|
||||
return reader.ReadInt32();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
|
||||
@@ -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.4.26.237")]
|
||||
[assembly: AssemblyFileVersion ("1.4.26.237")]
|
||||
[assembly: AssemblyVersion ("1.4.27.241")]
|
||||
[assembly: AssemblyFileVersion ("1.4.27.241")]
|
||||
|
||||
@@ -5,7 +5,7 @@ Visual Novels resource browser.
|
||||
|
||||
Requires .NET Framework v4.5 or newer (https://www.microsoft.com/net)
|
||||
|
||||
[Supported formats](http://htmlpreview.github.io/?https://github.com/morkt/GARbro/blob/master/supported.html)
|
||||
[Supported formats](https://morkt.github.io/GARbro/supported.html)
|
||||
|
||||
[Download latest release](https://github.com/morkt/GARbro/releases)
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ tr.last { height: auto }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Formats recognized by Game Archived Resources browser. "Titles" column lists tested titles only.</p>
|
||||
<p>Formats recognized by <a href="https://github.com/morkt/GARbro">Game Archived Resources browser</a>. "Titles" column lists tested titles only.</p>
|
||||
<table>
|
||||
<tr><th>Files</th><th>Signature</th><th>Create</th><th>Brand/Engine<th>Titles</th></tr>
|
||||
<tr class="odd"><td>*.pak</td><td><tt>ADPACK32</tt></td><td>No</td><td rowspan="3">Active Soft</td><td rowspan="3">Bible Black<br/>Discipline<br/>Discipline LS<br/>Fearless]</td></tr>
|
||||
@@ -135,6 +135,7 @@ Katahane<br/>
|
||||
Magical Kanon 2 ~Hiiro no Bergamot~<br/>
|
||||
Narimono<br/>
|
||||
Reconquista<br/>
|
||||
Strawberry Feels<br/>
|
||||
Switch!! ~Boku ga Natsu ni Omou Koto~<br/>
|
||||
White ~blanche comme la lune~<br/>
|
||||
</td></tr>
|
||||
@@ -254,8 +255,10 @@ Ah, Ojou-sama<br/>
|
||||
Altered Pink ~Tokumu Sentai Duel Ranger~<br/>
|
||||
Amairo*Islenauts<br/>
|
||||
Aozora Gakko no Sensei-kun<br/>
|
||||
Bishuu ~Chigyaku no Mesu Dorei~<br/>
|
||||
Boku to Koi Suru Ponkotsu Akuma. Suggoi Ecchi!<br/>
|
||||
Cafe Sourire<br/>
|
||||
Clover Day's<br/>
|
||||
Coμ<br/>
|
||||
Corona Blossom vol.1<br/>
|
||||
Crime Rhyme series<br/>
|
||||
@@ -276,6 +279,7 @@ Hime to Majin to Koi Suru Tamashii<br/>
|
||||
Imouto Style<br/>
|
||||
Inaho no Mirai<br/>
|
||||
Karakara<br/>
|
||||
Karenai Sekai to Owaru Hana<br/>
|
||||
Kourin no Machi, Lavender no Shoujo<br/>
|
||||
Kozukuri Onsen ~Ippai Tsukutte Ichizoku Hanei~<br/>
|
||||
Kurenai no Tsuki<br/>
|
||||
@@ -295,12 +299,14 @@ Okiba ga Nai!<br/>
|
||||
Oku-sama wa Moto Yariman<br/>
|
||||
Omana 2: Omaenchi Moeteruzo<br/>
|
||||
Ore no Saimin Fantasia<br/>
|
||||
Otome*Domain<br/>
|
||||
Ouka Ryouran<br/>
|
||||
Oyako Ninjutsu Kunoichi PonPon!!<br/>
|
||||
Rakugaki Overheart<br/>
|
||||
RGH ~Koi to Hero to Gakuen to~<br/>
|
||||
Riding Incubus<br/>
|
||||
Rui wa Tomo o Yobu<br/>
|
||||
Sakura Sakura<br/>
|
||||
Sanoba Witch<br/>
|
||||
Seirei Tenshou<br/>
|
||||
Senren*Banka<br/>
|
||||
@@ -332,6 +338,7 @@ Ryuuyoku no Melodia -Diva with the blessed dragonol-<br/>
|
||||
Sei Monmusu Festival!!<br/>
|
||||
Shin Chikan Ou<br/>
|
||||
Unionism Quartet<br/>
|
||||
Unionism Quartet A3-Days<br/>
|
||||
Usotsuki Ouji to Nayameru Ohime-sama -Princess Syndrome-<br/>
|
||||
</td></tr>
|
||||
<tr class="odd last"><td>*.ycg</td><td><tt>YCG</tt></td><td>No</td></tr>
|
||||
@@ -383,6 +390,7 @@ Ren no Koi<br/>
|
||||
<tr class="odd"><td>*.ald</td><td>-</td><td>No</td><td rowspan="6">Alice Soft</td><td rowspan="6">
|
||||
Boku dake no Hokenshitsu<br/>
|
||||
Daiteikoku<br/>
|
||||
Majo no Shokuzai<br/>
|
||||
Momoiro Guardian<br/>
|
||||
Pastel Chime 3 Bind Seeker<br/>
|
||||
Rance 03<br/>
|
||||
@@ -439,11 +447,12 @@ Shouhei-kun no Hani-Kami Life☆<br/>
|
||||
Touka Gettan<br/>
|
||||
</td></tr>
|
||||
<tr class="odd last"><td>*</td><td><tt>\x00\x00\x04\x00</tt></td><td>No</td></tr>
|
||||
<tr><td>*.war</td><td><tt>WARC 1.7</tt><br/><tt>WARC 1.5</tt><br/><tt>WARC 1.4</tt><br/><tt>WARC 1.3</tt><br/><tt>WARC 1.2</tt></td><td>No</td><td rowspan="5">Shiina Rio</td><td rowspan="5">
|
||||
<tr><td>*.war</td><td><tt>WARC 1.7</tt><br/><tt>WARC 1.5</tt><br/><tt>WARC 1.4</tt><br/><tt>WARC 1.3</tt><br/><tt>WARC 1.2</tt><br/><tt>WARC 1.1</tt></td><td>No</td><td rowspan="5">Shiina Rio</td><td rowspan="5">
|
||||
Ao no Juuai <span class="footnote">ShiinaRio v2.34</span><br/>
|
||||
Aneiro <span class="footnote">ShiinaRio v2.49</span><br/>
|
||||
Bitch Nee-chan ga Seijun na Hazu ga Nai! <span class="footnote">ShiinaRio v2.50</span><br/>
|
||||
Bitch Shimai ga Seijun na Hazu ga Nai!! <span class="footnote">ShiinaRio v2.50</span><br/>
|
||||
Calendar Girl <span class="footnote">ShiinaRio v2.02</span><br/>
|
||||
Can Fes! ~Itazura Majo to Naisho no Gakuensai~ <span class="footnote">ShiinaRio v2.47</span><br/>
|
||||
Chikan Circle <span class="footnote">ShiinaRio v2.46</span><br/>
|
||||
Chikan Circle 2 <span class="footnote">ShiinaRio v2.47</span><br/>
|
||||
@@ -484,6 +493,7 @@ Nagagutsu wo Haita Deco <span class="footnote">ShiinaRio v2.39</span><br/>
|
||||
Najimi no Oba-chan <span class="footnote">ShiinaRio v2.47</span><br/>
|
||||
Niizuma to Yuukaihan <span class="footnote">ShiinaRio v2.45</span><br/>
|
||||
Onegan! <span class="footnote">ShiinaRio v2.48</span><br/>
|
||||
Oshikake! Entremets <span class="footnote">ShiinaRio v2.47</span><br/>
|
||||
Otome Chibaku Yuugi <span class="footnote">ShiinaRio v2.40</span><br/>
|
||||
Otome Juurin Yuugi <span class="footnote">ShiinaRio v2.37</span><br/>
|
||||
Puchipuchi Idol Kouhosei <span class="footnote">ShiinaRio v2.49</span><br/>
|
||||
@@ -500,6 +510,7 @@ Sorcery Jokers<span class="footnote">ShiinaRio v2.50</span><br/>
|
||||
Tantei Shounen A<span class="footnote">some old version, like 2.twenty-something</span><br/>
|
||||
Tekoire Princess!<span class="footnote">ShiinaRio v2.37</span><br/>
|
||||
Toriko no Chigiri<span class="footnote">ShiinaRio v2.49</span><br/>
|
||||
Vestige -Yaiba ni Nokoru wa Kimi no Omokage- <span class="footnote">ShiinaRio v2.47</span><br/>
|
||||
Wana ~Hakudaku Mamire no Houkago~<span class="footnote">ShiinaRio v2.36</span><br/>
|
||||
Wana II ~Gang Rape~<span class="footnote">ShiinaRio v2.44</span><br/>
|
||||
Yakata Jukujo ~The Immoral Residence~ <span class="footnote">ShiinaRio v2.37</span><br/>
|
||||
@@ -528,6 +539,7 @@ Prawf Clwyd<br/>
|
||||
Rakuen no Rukia ~Blood Moon Rising~<br/>
|
||||
Samayou Mitama ni Yasuragi no Toki wo<br/>
|
||||
School Five ~Itsutsu no Himitsu no Monogatari~<br/>
|
||||
Tappun☆Oneecher<br/>
|
||||
Time Trouble ~Marie ni Kubikkake~<br/>
|
||||
</td></tr>
|
||||
<tr><td>*.dat</td><td>-</td><td>No</td><td>Ail</td><td>
|
||||
@@ -564,6 +576,7 @@ Tsurugi Otome Noah<br/>
|
||||
<tr><td>*.pmp<br/>*.pmw</td><td>-</td><td>Yes</td><td>ScenePlayer</td><td>
|
||||
Eraburu ~Erabu + Love x Double de~<br/>
|
||||
Nyuujoku Hitozuma Jogakuen<br/>
|
||||
Nyuukan Nurse<br/>
|
||||
WW&F ~Taishou Teito Denkitan~<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.dat</td><td><tt>GAMEDAT PACK</tt><br/><tt>GAMEDAT PAC2</tt></td><td>No</td><td rowspan="2"> bootUP!<br/>Pajamas Soft<br/>Aries</td><td rowspan="2">
|
||||
@@ -620,7 +633,9 @@ re-Laive<br/>
|
||||
Sakura Musubi<br/>
|
||||
Santaful☆Summer<br/>
|
||||
Space☆Trouble<br/>
|
||||
Spicy Spycy!<br/>
|
||||
Tonarizuma<br/>
|
||||
Tsubasa o Kudasai<br/>
|
||||
Yatohime Zankikou<br/>
|
||||
Zettai Joshi Ryouiki!<br/>
|
||||
</td></tr>
|
||||
@@ -670,6 +685,7 @@ Zetsuboushi<br/>
|
||||
<tr><td>*.pack</td><td><tt>FilePackVer1.0</tt><br/><tt>FilePackVer2.0</tt><br/><tt>FilePackVer3.0</tt></td><td>No</td><td rowspan="3">QLIE</td><td rowspan="3">
|
||||
Bishoujo Mangekyou -Kami ga Tsukuritamouta Shoujo-tachi-<br/>
|
||||
Hidamari Basket<br/>
|
||||
Kikan Bakumatsu Ibun Last Cavalier<br/>
|
||||
Makai Tenshi Djibril -episode 3-<br/>
|
||||
Mehime no Toriko<br/>
|
||||
Nanatsu no Fushigi no Owaru Toki<br/>
|
||||
@@ -755,6 +771,7 @@ Thirua Panic<br/>
|
||||
<tr><td>*.mcg</td><td><tt>MCG 2.00</tt></td><td>No</td></tr>
|
||||
<tr class="last"><td>*.acd</td><td><tt>ACD 1.00</tt></td><td>No</td></tr>
|
||||
<tr class="odd"><td>*.pk<br/>*.gpk<br/>*.tpk<br/>*.wpk<br/></td><td>-</td><td>No</td><td rowspan="2">U-Me Soft</td><td rowspan="2">
|
||||
Do M Otoko Tantei ga Iku Katte ni Ittara Oshioki yo!<br/>
|
||||
Fetish Omote no Kioku/Ura no Kioku series<br/>
|
||||
Kyonyuu Maid Nakadashi Tengoku!<br/>
|
||||
</td></tr>
|
||||
@@ -945,6 +962,7 @@ Kitto, Sumiwataru Asairo Yori mo,<br/>
|
||||
</td></tr>
|
||||
<tr class="odd last"><td>*.mgr</td><td>-</td><td>No</td></tr>
|
||||
<tr><td>*.dat</td><td><tt>PAK0</tt></td><td>No</td><td>Studio e.go!</td><td>
|
||||
GakuPara!! ~Gakuen Paradise!!~<br/>
|
||||
Izumo<br/>
|
||||
Izumo 2<br/>
|
||||
Izumo 2 ~Gakuen Kyousoukyoku~<br/>
|
||||
@@ -1117,13 +1135,16 @@ Angenehm Platz -Kleiner Garten Sie Erstellen-<br/>
|
||||
<tr><td>*.zit</td><td><tt>ZT</tt></td><td>No</td><td>Silky's</td><td>
|
||||
Sweet ~Hanjuku na Tenshi-tachi~<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.ovk</td><td>-</td><td>No</td><td rowspan="4">RealLive</td><td rowspan="4">
|
||||
<tr class="odd"><td>*.ovk</td><td>-</td><td>No</td><td rowspan="5">RealLive</td><td rowspan="5">
|
||||
Devote 2 Ikenai Houkago<br/>
|
||||
Gakuen Taima! Holy x Moly<br/>
|
||||
Joi-san no Iitsuke!!<br/>
|
||||
Shiawase na Ohime-sama<br/>
|
||||
Shin Genre Esudere!!<br/>
|
||||
Warui Koto Shitai<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.nwa</td><td>-</td><td>No</td></tr>
|
||||
<tr class="odd"><td>*.koe</td><td><tt>KOEPAC</tt></td><td>No</td></tr>
|
||||
<tr class="odd"><td>*.g00</td><td>-</td><td>No</td></tr>
|
||||
<tr class="odd last"><td>*.pdt</td><td><tt>PDT10</tt></td><td>No</td></tr>
|
||||
<tr><td>*.pak<br/>*.pac</td><td><tt>VC製品版</tt><br/><tt>\x01\x00\x00\x00</tt></td><td>No</td><td rowspan="2">Circus</td><td rowspan="2">
|
||||
@@ -1143,6 +1164,7 @@ Uso x Mote ~Usonko Motemote-shon~<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.syg</td><td><tt>$SYG</tt></td><td>No</td><td><a title="Risa game platform system">Risa</a></td><td>
|
||||
Chijoku Yuugi<br/>
|
||||
Kakure Ecchi<br/>
|
||||
OL Gari ~Nureta Office~<br/>
|
||||
Taiiku Souko ~Shoujotachi no Sange~<br/>
|
||||
</td></tr>
|
||||
@@ -1181,7 +1203,7 @@ Natsunone -Ring-<br/>
|
||||
<tr class="odd"><td>*.pac</td><td><tt>MGPK</tt><td>No</td><td>MangaGamer</td><td>
|
||||
Cartagra<br/>
|
||||
Kara no Shoujo 2<br/>
|
||||
</td></td>
|
||||
</td></tr>
|
||||
<tr><td>data.NN<br/>ArcNN.dat</td><td>-</td><td>No</td><td>Cyberworks</td><td>
|
||||
Abunai Kankei<br/>
|
||||
Aneboku ~Onee-chan wa Bijin 3 Shimai~<br/>
|
||||
@@ -1322,6 +1344,7 @@ Sakura no Sakukoro<br/>
|
||||
Tsumi no Hikari Rendezvous<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>arc.dat</td><td>-</td><td>No</td><td rowspan="2">AdvSys3</td><td rowspan="2">
|
||||
Chijoku Tsuma Natsuki<br/>
|
||||
Jii Tousaku<br/>
|
||||
Joku Dorei Tsuma<br/>
|
||||
Okami Tsuma ~Ryokan Baito de no Himitsu~<br/>
|
||||
@@ -1363,9 +1386,12 @@ Idol★Harem<br/>
|
||||
Maou no Ingu<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.bin</td><td><tt>KAR</tt></td><td>No</td><td rowspan="2">Cadath</td><td rowspan="2">
|
||||
DA Pantsu!!<br/>
|
||||
Doreijou<br/>
|
||||
</td></tr>
|
||||
<tr class="odd last"><td>*.kgf</td><td><tt>KGF</tt></td><td>No</td></tr>
|
||||
<tr class="odd"><td>*.kgf</td><td><tt>KGF</tt></td><td>No</td></tr>
|
||||
<tr class="odd"><td>*.arc</td><td><tt>DAF\x1A</tt></td><td>No</td></tr>
|
||||
<tr class="odd last"><td>*.cgf</td><td><tt>CGF\x1A</tt></td><td>No</td></tr>
|
||||
<tr><td>*.pfs</td><td><tt>pf8</tt></td><td>No</td><td>Artemis Engine</td><td>
|
||||
Boku no Elf Onee-san<br/>
|
||||
</td></tr>
|
||||
@@ -1381,11 +1407,14 @@ Teri ☆ Mix<br/>
|
||||
Onepapa ~Onegai PaPa!~<br/>
|
||||
</td></tr>
|
||||
<tr class="odd last"><td>*.pb</td><td><tt>PB00</tt></td><td>No</td></tr>
|
||||
<tr><td>*.cmp</td><td>-</td><td>No</td><td rowspan="2">0verflow</td><td rowspan="2">
|
||||
<tr><td>*.cmp</td><td>-</td><td>No</td><td rowspan="3">0verflow</td><td rowspan="3">
|
||||
Large PonPon<br/>
|
||||
PureMail<br/>
|
||||
Summer Radish Vacation!!<br/>
|
||||
Summer Radish Vacation!! 2<br/>
|
||||
</td></tr>
|
||||
<tr><td>*.cgd<br/>*.bgd<br/>*.chr</td><td>-</td><td>No</td></tr>
|
||||
<tr><td>*.cgd<br/>*.bgd<br/>*.chr<br/>*.crgb</td><td>-</td><td>No</td></tr>
|
||||
<tr class="last"><td>*.adp4</td><td>-</td><td>No</td></tr>
|
||||
<tr class="odd"><td>*.vfs</td><td><tt>VFS File</tt></td><td>No</td><td>VNSystem</td><td>
|
||||
Seika no Mori<br/>
|
||||
</td></tr>
|
||||
@@ -1419,11 +1448,30 @@ Ryoujoku Joshi Gakuen<br/>
|
||||
<tr class="odd"><td>*.vpk</td><td><tt>VPK1</tt></td><td>No</td></tr>
|
||||
<tr class="odd"><td>*.wrc</td><td><tt>WVX0</tt></td><td>No</td></tr>
|
||||
<tr class="odd"><td>*.wsm</td><td><tt>WSM1</tt><br/><tt>WSM2</tt><br/><tt>WSM3</tt></td><td>No</td></tr>
|
||||
<tr class="odd"><td>*</td><td><tt>BC</tt></td><td>No</td></tr>
|
||||
<tr class="odd last"><td>*</td><td><tt>BC</tt></td><td>No</td></tr>
|
||||
<tr><td>*.pak</td><td><tt>Graphic PackData</tt></td><td>No</td><td rowspan="2">cromwell</td><td rowspan="2">
|
||||
Ikenie Reijou<br/>
|
||||
Tremolo<br/>
|
||||
</td></tr>
|
||||
<tr><td>*.opk</td><td><tt>VoiceOggPackFile</tt></td><td>No</td></tr>
|
||||
<tr class="odd"><td>CG</td><td>-</td><td>No</td><td>Software House Parsley</td><td>
|
||||
Mugen no Meikyuu 3 Type S<br/>
|
||||
Phantom Knight Mugen no Meikyuu II<br/>
|
||||
</td></tr>
|
||||
<tr><td>*.fa2</td><td><tt>FA2</tt></td><td>No</td><td rowspan="2">Foster</td><td rowspan="2">
|
||||
Mania na Onna
|
||||
</td></tr>
|
||||
<tr class="last"><td>*.c24</td><td><tt>C24</tt></td><td>No</td></tr>
|
||||
<tr class="odd"><td>*.imp</td><td>-</td><td>No</td><td rowspan="2">Black Rainbow</td><td rowspan="2">
|
||||
From M<br/>
|
||||
Kannagi<br/>
|
||||
</td></tr>
|
||||
<tr class="odd last"><td>*.pak</td><td><tt>CCf"</tt></td><td>No</td></tr>
|
||||
<tr><td>*.dat</td><td>-</td><td>No</td><td rowspan="3">Valkyria</td><td rowspan="3">
|
||||
Kangokujou ~Ingyaku no Shihai~<br/>
|
||||
</td></tr>
|
||||
<tr><td>*.mg2</td><td><tt>MICOCG01</tt></td><td>No</td></tr>
|
||||
<tr><td class="last">*.mal</td><td><tt>MICOMSK00</tt></td><td>No</td></tr>
|
||||
</table>
|
||||
<p><a name="note-1" class="footnote">1</a> Non-encrypted only</p>
|
||||
</body>
|
||||
27
docs/version.xml
Normal file
27
docs/version.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<GARbro>
|
||||
<Release>
|
||||
<Version>1.4.26</Version>
|
||||
<Url>https://github.com/morkt/GARbro/releases/latest</Url>
|
||||
<Notes>Attempt to extract NOA and QLIE archive keys from game executable files.
|
||||
|
||||
New formats:
|
||||
- Software House Parsley CG archives
|
||||
- PureMail archives and ADP4 compressed audio
|
||||
- FA2 archives and C24 images
|
||||
- RealLive KOEPAC archives
|
||||
- Black Rainbow IMP and PAK archives
|
||||
- DAF archives and CGF images
|
||||
- Valkyria's MG2 and MAL images
|
||||
- more KiriKiri and ShiinaRio encryption schemes
|
||||
</Notes>
|
||||
</Release>
|
||||
<FormatsData>
|
||||
<FileVersion>55</FileVersion>
|
||||
<Url>https://github.com/morkt/GARbro/raw/master/ArcFormats/Resources/Formats.dat</Url>
|
||||
<Requires>
|
||||
<Assembly Name="ArcFormats" Version="1.2.29.1274"/>
|
||||
<Assembly Name="GameRes" Version="1.4.26.238"/>
|
||||
</Requires>
|
||||
</FormatsData>
|
||||
</GARbro>
|
||||
Reference in New Issue
Block a user