mirror of
https://github.com/crskycode/GARbro.git
synced 2026-06-18 17:04:32 +08:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c8b3e3e62 | ||
|
|
2aa6f92b1a | ||
|
|
8c37a7e107 | ||
|
|
525c2ff29b | ||
|
|
1aeb6cb791 | ||
|
|
5bd63c01cb | ||
|
|
37a038b090 | ||
|
|
96ebe2bd85 | ||
|
|
5097a98cb3 | ||
|
|
241cb95f8b | ||
|
|
8a74eb1c77 | ||
|
|
360664343f | ||
|
|
da3403339e | ||
|
|
2d186edb39 | ||
|
|
abd6ff6b39 | ||
|
|
a5f398a2d5 | ||
|
|
337f238198 | ||
|
|
6185af55cd | ||
|
|
b1ebf89890 | ||
|
|
2f2b067272 | ||
|
|
11273de4f6 | ||
|
|
2ceafe55df | ||
|
|
0fd301d72b |
@@ -12,6 +12,7 @@
|
|||||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
|
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
@@ -108,10 +109,13 @@
|
|||||||
<Compile Include="GameSystem\AudioADP4.cs" />
|
<Compile Include="GameSystem\AudioADP4.cs" />
|
||||||
<Compile Include="Kaas\ArcPB.cs" />
|
<Compile Include="Kaas\ArcPB.cs" />
|
||||||
<Compile Include="Kaas\AudioKAAS.cs" />
|
<Compile Include="Kaas\AudioKAAS.cs" />
|
||||||
|
<Compile Include="Kaguya\ArcAN21.cs" />
|
||||||
<Compile Include="Kaguya\ArcUF.cs" />
|
<Compile Include="Kaguya\ArcUF.cs" />
|
||||||
<Compile Include="Kurumi\ImageGRA.cs" />
|
<Compile Include="Kurumi\ImageGRA.cs" />
|
||||||
<Compile Include="Leaf\ArcPX.cs" />
|
<Compile Include="Leaf\ArcPX.cs" />
|
||||||
<Compile Include="Malie\ArcLIBU.cs" />
|
<Compile Include="Malie\ArcLIBU.cs" />
|
||||||
|
<Compile Include="Malie\LibScheme.cs" />
|
||||||
|
<Compile Include="Malie\MalieEncryption.cs" />
|
||||||
<Compile Include="MicroVision\ArcARC.cs" />
|
<Compile Include="MicroVision\ArcARC.cs" />
|
||||||
<Compile Include="MicroVision\ArcGSD.cs" />
|
<Compile Include="MicroVision\ArcGSD.cs" />
|
||||||
<Compile Include="MicroVision\AudioIKM.cs" />
|
<Compile Include="MicroVision\AudioIKM.cs" />
|
||||||
@@ -557,6 +561,7 @@
|
|||||||
<Compile Include="Xuse\ArcXARC.cs" />
|
<Compile Include="Xuse\ArcXARC.cs" />
|
||||||
<Compile Include="Xuse\ArcXuse.cs" />
|
<Compile Include="Xuse\ArcXuse.cs" />
|
||||||
<Compile Include="YaneSDK\ArcDAT.cs" />
|
<Compile Include="YaneSDK\ArcDAT.cs" />
|
||||||
|
<Compile Include="YaneSDK\ArcHibiki.cs" />
|
||||||
<Compile Include="Yatagarasu\ArcPKG.cs" />
|
<Compile Include="Yatagarasu\ArcPKG.cs" />
|
||||||
<Compile Include="Yatagarasu\ArcPKG2.cs" />
|
<Compile Include="Yatagarasu\ArcPKG2.cs" />
|
||||||
<Compile Include="Youkai\ArcDAT.cs" />
|
<Compile Include="Youkai\ArcDAT.cs" />
|
||||||
|
|||||||
@@ -52,13 +52,14 @@ namespace GameRes.Formats.Artemis
|
|||||||
int version = file.View.ReadByte (2) - '0';
|
int version = file.View.ReadByte (2) - '0';
|
||||||
switch (version)
|
switch (version)
|
||||||
{
|
{
|
||||||
case 8: return OpenPf8 (file);
|
case 6:
|
||||||
|
case 8: return OpenPf (file, version);
|
||||||
case 2: return OpenPf2 (file);
|
case 2: return OpenPf2 (file);
|
||||||
default: return null;
|
default: return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ArcFile OpenPf8 (ArcView file)
|
ArcFile OpenPf (ArcView file, int version)
|
||||||
{
|
{
|
||||||
uint index_size = file.View.ReadUInt32 (3);
|
uint index_size = file.View.ReadUInt32 (3);
|
||||||
int count = file.View.ReadInt32 (7);
|
int count = file.View.ReadInt32 (7);
|
||||||
@@ -81,9 +82,13 @@ namespace GameRes.Formats.Artemis
|
|||||||
index_offset += 8;
|
index_offset += 8;
|
||||||
dir.Add (entry);
|
dir.Add (entry);
|
||||||
}
|
}
|
||||||
|
if (version != 8 && version != 9 && version != 4 && version != 5)
|
||||||
|
return new ArcFile (file, this, dir);
|
||||||
|
|
||||||
|
// key calculated for archive versions 4, 5, 8 and 9
|
||||||
using (var sha1 = SHA1.Create())
|
using (var sha1 = SHA1.Create())
|
||||||
{
|
{
|
||||||
var key = sha1.ComputeHash (index); // calculated for archive versions 4, 5, 8 and 9
|
var key = sha1.ComputeHash (index);
|
||||||
return new PfsArchive (file, this, dir, key);
|
return new PfsArchive (file, this, dir, key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
//! \date Thu Jun 16 13:48:04 2016
|
//! \date Thu Jun 16 13:48:04 2016
|
||||||
//! \brief Tinker Bell resource archive.
|
//! \brief Tinker Bell resource archive.
|
||||||
//
|
//
|
||||||
// Copyright (C) 2016 by morkt
|
// Copyright (C) 2016-2017 by morkt
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to
|
// of this software and associated documentation files (the "Software"), to
|
||||||
@@ -272,8 +272,12 @@ namespace GameRes.Formats.Cyberworks
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var reader = DecryptImage (input, barc.Scheme);
|
var reader = DecryptImage (input, barc.Scheme);
|
||||||
if (BlendOverlayImages && reader is AImageReader)
|
if (BlendOverlayImages)
|
||||||
reader = BlendAImage (barc, entry, reader as AImageReader);
|
{
|
||||||
|
var overlay = reader as AImageReader;
|
||||||
|
if (overlay != null)
|
||||||
|
overlay.ReadBaseline (barc, entry);
|
||||||
|
}
|
||||||
return reader;
|
return reader;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@@ -294,55 +298,18 @@ namespace GameRes.Formats.Cyberworks
|
|||||||
input = BinaryStream.FromStream (new StreamRegion (input.AsStream, 5, img_size), input.Name);
|
input = BinaryStream.FromStream (new StreamRegion (input.AsStream, 5, img_size), input.Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (scheme != null && 'a' == type && input.Length > 21)
|
else if (scheme != null && ('a' == type || 'd' == type) && input.Length > 21)
|
||||||
{
|
{
|
||||||
int id = input.ReadByte();
|
int id = input.ReadByte();
|
||||||
if (id == scheme.Value2)
|
if (id == scheme.Value2)
|
||||||
{
|
{
|
||||||
return new AImageReader (input, scheme);
|
return new AImageReader (input, scheme, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
input.Position = 0;
|
input.Position = 0;
|
||||||
return new ImageFormatDecoder (input);
|
return new ImageFormatDecoder (input);
|
||||||
}
|
}
|
||||||
|
|
||||||
IImageDecoder BlendAImage (BellArchive arc, Entry entry, AImageReader overlay)
|
|
||||||
{
|
|
||||||
var header = overlay.ReadHeader();
|
|
||||||
if (header[0] != 1)
|
|
||||||
return overlay;
|
|
||||||
var scheme = arc.Scheme;
|
|
||||||
var dir = (List<Entry>)arc.Dir;
|
|
||||||
int i = dir.IndexOf (entry);
|
|
||||||
while (--i >= 0 && "image" == dir[i].Type)
|
|
||||||
{
|
|
||||||
using (var input = OpenEntry (arc, dir[i]))
|
|
||||||
{
|
|
||||||
int type = input.ReadByte();
|
|
||||||
if (type != 'a')
|
|
||||||
break;
|
|
||||||
int id = input.ReadByte();
|
|
||||||
if (id != scheme.Value2)
|
|
||||||
break;
|
|
||||||
using (var bin = new BinaryStream (input, dir[i].Name))
|
|
||||||
using (var base_image = new AImageReader (bin, scheme))
|
|
||||||
{
|
|
||||||
var base_header = base_image.ReadHeader();
|
|
||||||
if (1 == base_header[0])
|
|
||||||
continue;
|
|
||||||
// check if image width/height are the same
|
|
||||||
if (base_header[3] == header[3] && base_header[4] == header[4])
|
|
||||||
{
|
|
||||||
base_image.Unpack();
|
|
||||||
overlay.Baseline = base_image.Data;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return overlay;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal AImageScheme QueryScheme (string arc_name)
|
internal AImageScheme QueryScheme (string arc_name)
|
||||||
{
|
{
|
||||||
var title = FormatCatalog.Instance.LookupGame (arc_name);
|
var title = FormatCatalog.Instance.LookupGame (arc_name);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
//! \date Fri Jun 17 18:49:04 2016
|
//! \date Fri Jun 17 18:49:04 2016
|
||||||
//! \brief Tinker Bell encrypted image file.
|
//! \brief Tinker Bell encrypted image file.
|
||||||
//
|
//
|
||||||
// Copyright (C) 2016 by morkt
|
// Copyright (C) 2016-2017 by morkt
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to
|
// of this software and associated documentation files (the "Software"), to
|
||||||
@@ -24,9 +24,10 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.ComponentModel.Composition;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
|
||||||
namespace GameRes.Formats.Cyberworks
|
namespace GameRes.Formats.Cyberworks
|
||||||
{
|
{
|
||||||
@@ -52,11 +53,13 @@ namespace GameRes.Formats.Cyberworks
|
|||||||
AImageScheme m_scheme;
|
AImageScheme m_scheme;
|
||||||
ImageData m_image;
|
ImageData m_image;
|
||||||
int[] m_header;
|
int[] m_header;
|
||||||
|
int m_type;
|
||||||
|
|
||||||
public Stream Source { get { m_input.Position = 0; return m_input.AsStream; } }
|
public Stream Source { get { m_input.Position = 0; return m_input.AsStream; } }
|
||||||
public ImageFormat SourceFormat { get { return null; } }
|
public ImageFormat SourceFormat { get { return null; } }
|
||||||
public ImageMetaData Info { get { return m_info; } }
|
public ImageMetaData Info { get { return m_info; } }
|
||||||
public byte[] Baseline { get; set; }
|
public byte[] Baseline { get; set; }
|
||||||
|
public int Type { get { return m_type; } }
|
||||||
|
|
||||||
public ImageData Image
|
public ImageData Image
|
||||||
{
|
{
|
||||||
@@ -77,10 +80,11 @@ namespace GameRes.Formats.Cyberworks
|
|||||||
|
|
||||||
public byte[] Data { get { return m_output; } }
|
public byte[] Data { get { return m_output; } }
|
||||||
|
|
||||||
public AImageReader (IBinaryStream input, AImageScheme scheme)
|
public AImageReader (IBinaryStream input, AImageScheme scheme, int type = 'a')
|
||||||
{
|
{
|
||||||
m_input = input;
|
m_input = input;
|
||||||
m_scheme = scheme;
|
m_scheme = scheme;
|
||||||
|
m_type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal int[] ReadHeader ()
|
internal int[] ReadHeader ()
|
||||||
@@ -99,6 +103,65 @@ namespace GameRes.Formats.Cyberworks
|
|||||||
return m_header;
|
return m_header;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Search archive <paramref name="arc"/> for baseline image.
|
||||||
|
/// </summary>
|
||||||
|
internal void ReadBaseline (BellArchive arc, Entry entry)
|
||||||
|
{
|
||||||
|
var header = ReadHeader();
|
||||||
|
if (!((header[0] & 1) == 1 && 'd' == this.Type
|
||||||
|
|| header[0] == 1 && 'a' == this.Type))
|
||||||
|
return;
|
||||||
|
var scheme = arc.Scheme;
|
||||||
|
var dir = (List<Entry>)arc.Dir;
|
||||||
|
int i = dir.IndexOf (entry);
|
||||||
|
while (--i >= 0 && "image" == dir[i].Type)
|
||||||
|
{
|
||||||
|
using (var input = arc.OpenEntry (dir[i]))
|
||||||
|
{
|
||||||
|
int type = input.ReadByte();
|
||||||
|
if ('d' == type)
|
||||||
|
continue;
|
||||||
|
if ('a' == type)
|
||||||
|
{
|
||||||
|
int id = input.ReadByte();
|
||||||
|
if (id != scheme.Value2)
|
||||||
|
break;
|
||||||
|
using (var bin = new BinaryStream (input, dir[i].Name))
|
||||||
|
using (var base_image = new AImageReader (bin, scheme))
|
||||||
|
{
|
||||||
|
var base_header = base_image.ReadHeader();
|
||||||
|
if (1 == base_header[0])
|
||||||
|
continue;
|
||||||
|
// check if image width/height are the same
|
||||||
|
if (base_header[3] == header[3] && base_header[4] == header[4])
|
||||||
|
{
|
||||||
|
base_image.Unpack();
|
||||||
|
Baseline = base_image.Data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ('b' == type || 'c' == type)
|
||||||
|
{
|
||||||
|
var size_buf = new byte[4];
|
||||||
|
input.Read (size_buf, 0 , 4);
|
||||||
|
var decoder = new PngBitmapDecoder (input, BitmapCreateOptions.None,
|
||||||
|
BitmapCacheOption.OnLoad);
|
||||||
|
BitmapSource frame = decoder.Frames[0];
|
||||||
|
Info.Width = (uint)frame.PixelWidth;
|
||||||
|
Info.Height = (uint)frame.PixelHeight;
|
||||||
|
if (frame.Format.BitsPerPixel != 32)
|
||||||
|
frame = new FormatConvertedBitmap (frame, PixelFormats.Bgra32, null, 0);
|
||||||
|
int stride = frame.PixelWidth * 4;
|
||||||
|
var pixels = new byte[stride * frame.PixelHeight];
|
||||||
|
frame.CopyPixels (pixels, stride, 0);
|
||||||
|
Baseline = pixels;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Unpack ()
|
public void Unpack ()
|
||||||
{
|
{
|
||||||
var header = ReadHeader();
|
var header = ReadHeader();
|
||||||
@@ -118,6 +181,8 @@ namespace GameRes.Formats.Cyberworks
|
|||||||
{
|
{
|
||||||
if (0 == bits_size)
|
if (0 == bits_size)
|
||||||
CopyV6 (unpacked_size, header[6]);
|
CopyV6 (unpacked_size, header[6]);
|
||||||
|
else if (1 == (flags & 1) && 'd' == m_type && Baseline != null)
|
||||||
|
UnpackV6d (bits_size, bits_size + header[6]);
|
||||||
else
|
else
|
||||||
UnpackV6 (bits_size, data_offset, data_offset + header[6]);
|
UnpackV6 (bits_size, data_offset, data_offset + header[6]);
|
||||||
}
|
}
|
||||||
@@ -306,6 +371,34 @@ namespace GameRes.Formats.Cyberworks
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UnpackV6d (int bits_size, int rgb_offset)
|
||||||
|
{
|
||||||
|
Info.BPP = 32;
|
||||||
|
var rgb_map = m_input.ReadBytes (bits_size);
|
||||||
|
var alpha = m_input.ReadBytes (rgb_offset - bits_size);
|
||||||
|
int plane_size = Math.Min (Baseline.Length, bits_size*8);
|
||||||
|
m_output = Baseline;
|
||||||
|
int bit = 1;
|
||||||
|
int bit_src = 0;
|
||||||
|
int alpha_src = 0;
|
||||||
|
int dst = 0;
|
||||||
|
for (int i = 0; i < plane_size; ++i)
|
||||||
|
{
|
||||||
|
if ((bit & rgb_map[bit_src]) != 0)
|
||||||
|
{
|
||||||
|
m_input.Read (m_output, dst, 3);
|
||||||
|
m_output[dst+3] = alpha[alpha_src++];
|
||||||
|
}
|
||||||
|
dst += 4;
|
||||||
|
bit <<= 1;
|
||||||
|
if (0x100 == bit)
|
||||||
|
{
|
||||||
|
++bit_src;
|
||||||
|
bit = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int GetInt ()
|
int GetInt ()
|
||||||
{
|
{
|
||||||
byte a = m_input.ReadUInt8();
|
byte a = m_input.ReadUInt8();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
//! \date Thu Oct 08 00:18:56 2015
|
//! \date Thu Oct 08 00:18:56 2015
|
||||||
//! \brief DxLib engine archives with 'DX' signature.
|
//! \brief DxLib engine archives with 'DX' signature.
|
||||||
//
|
//
|
||||||
// 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
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to
|
// of this software and associated documentation files (the "Software"), to
|
||||||
@@ -28,6 +28,7 @@ using System.Collections.Generic;
|
|||||||
using System.ComponentModel.Composition;
|
using System.ComponentModel.Composition;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using GameRes.Utility;
|
using GameRes.Utility;
|
||||||
|
|
||||||
namespace GameRes.Formats.DxLib
|
namespace GameRes.Formats.DxLib
|
||||||
@@ -35,11 +36,13 @@ namespace GameRes.Formats.DxLib
|
|||||||
internal class DxArchive : ArcFile
|
internal class DxArchive : ArcFile
|
||||||
{
|
{
|
||||||
public readonly byte[] Key;
|
public readonly byte[] Key;
|
||||||
|
public readonly int Version;
|
||||||
|
|
||||||
public DxArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, byte[] key)
|
public DxArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, byte[] key, int version)
|
||||||
: base (arc, impl, dir)
|
: base (arc, impl, dir)
|
||||||
{
|
{
|
||||||
Key = key;
|
Key = key;
|
||||||
|
Version = version;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,8 +63,8 @@ namespace GameRes.Formats.DxLib
|
|||||||
|
|
||||||
public DxOpener ()
|
public DxOpener ()
|
||||||
{
|
{
|
||||||
Extensions = new string[] { "dxa", "hud", "usi", "med", "dat" };
|
Extensions = new string[] { "dxa", "hud", "usi", "med", "dat", "bin" };
|
||||||
Signatures = new uint[] { 0x19EF8ED4, 0xA9FCCEDD, 0x0AEE0FD3, 0x5523F211, 0x5524F211, 0 };
|
Signatures = new uint[] { 0x19EF8ED4, 0xA9FCCEDD, 0x0AEE0FD3, 0x5523F211, 0x5524F211, 0x69FC5FE4, 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IList<byte[]> KnownKeys = new List<byte[]>();
|
public static IList<byte[]> KnownKeys = new List<byte[]>();
|
||||||
@@ -76,7 +79,7 @@ namespace GameRes.Formats.DxLib
|
|||||||
uint sig_key = LittleEndian.ToUInt32 (key, 0);
|
uint sig_key = LittleEndian.ToUInt32 (key, 0);
|
||||||
uint sig_test = signature ^ sig_key;
|
uint sig_test = signature ^ sig_key;
|
||||||
int version = (int)(sig_test >> 16);
|
int version = (int)(sig_test >> 16);
|
||||||
if (0x5844 == (sig_test & 0xFFFF) && version <= 4) // 'DX'
|
if (0x5844 == (sig_test & 0xFFFF) && version <= 6) // 'DX'
|
||||||
{
|
{
|
||||||
var dir = ReadIndex (file, version, key);
|
var dir = ReadIndex (file, version, key);
|
||||||
if (null != dir)
|
if (null != dir)
|
||||||
@@ -87,7 +90,7 @@ namespace GameRes.Formats.DxLib
|
|||||||
KnownKeys.Remove (key);
|
KnownKeys.Remove (key);
|
||||||
KnownKeys.Insert (0, key);
|
KnownKeys.Insert (0, key);
|
||||||
}
|
}
|
||||||
return new DxArchive (file, this, dir, key);
|
return new DxArchive (file, this, dir, key, version);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -124,7 +127,7 @@ namespace GameRes.Formats.DxLib
|
|||||||
if (null != dir)
|
if (null != dir)
|
||||||
{
|
{
|
||||||
KnownKeys.Insert (0, key);
|
KnownKeys.Insert (0, key);
|
||||||
return new DxArchive (file, this, dir, key);
|
return new DxArchive (file, this, dir, key, version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch { /* ignore parse errors */ }
|
catch { /* ignore parse errors */ }
|
||||||
@@ -138,9 +141,14 @@ namespace GameRes.Formats.DxLib
|
|||||||
var dx_arc = arc as DxArchive;
|
var dx_arc = arc as DxArchive;
|
||||||
if (null == dx_arc)
|
if (null == dx_arc)
|
||||||
return input;
|
return input;
|
||||||
input = new EncryptedStream (input, entry.Offset, dx_arc.Key);
|
var dx_ent = (PackedEntry)entry;
|
||||||
var dx_ent = entry as PackedEntry;
|
long dec_offset = entry.Offset;
|
||||||
if (null == dx_ent || !dx_ent.IsPacked)
|
if (dx_arc.Version > 5)
|
||||||
|
{
|
||||||
|
dec_offset = dx_ent.UnpackedSize;
|
||||||
|
}
|
||||||
|
input = new EncryptedStream (input, dec_offset, dx_arc.Key);
|
||||||
|
if (!dx_ent.IsPacked)
|
||||||
return input;
|
return input;
|
||||||
using (input)
|
using (input)
|
||||||
{
|
{
|
||||||
@@ -214,26 +222,53 @@ namespace GameRes.Formats.DxLib
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<Entry> ReadIndex (ArcView file, int version, byte[] key)
|
List<Entry> ReadIndex (ArcView file, int version, byte[] key)
|
||||||
|
{
|
||||||
|
DxHeader dx = null;
|
||||||
|
if (version <= 4)
|
||||||
|
dx = ReadArcHeaderV4 (file, version, key);
|
||||||
|
else if (6 == version)
|
||||||
|
dx = ReadArcHeaderV6 (file, version, key);
|
||||||
|
if (null == dx || dx.DirTable >= dx.IndexSize || dx.FileTable >= dx.IndexSize)
|
||||||
|
return null;
|
||||||
|
using (var encrypted = file.CreateStream (dx.IndexOffset, dx.IndexSize))
|
||||||
|
using (var index = new EncryptedStream (encrypted, 6 == version ? 0 : dx.IndexOffset, key))
|
||||||
|
using (var reader = IndexReader.Create (dx, version, index))
|
||||||
|
{
|
||||||
|
return reader.Read();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DxHeader ReadArcHeaderV4 (ArcView file, int version, byte[] key)
|
||||||
{
|
{
|
||||||
var header = file.View.ReadBytes (4, 0x18);
|
var header = file.View.ReadBytes (4, 0x18);
|
||||||
if (0x18 != header.Length)
|
if (0x18 != header.Length)
|
||||||
return null;
|
return null;
|
||||||
Decrypt (header, 0, header.Length, 4, key);
|
Decrypt (header, 0, header.Length, 4, key);
|
||||||
var dx = new DxHeader {
|
return new DxHeader {
|
||||||
IndexSize = LittleEndian.ToUInt32 (header, 0),
|
IndexSize = LittleEndian.ToUInt32 (header, 0),
|
||||||
BaseOffset = LittleEndian.ToUInt32 (header, 4),
|
BaseOffset = LittleEndian.ToUInt32 (header, 4),
|
||||||
IndexOffset = LittleEndian.ToUInt32 (header, 8),
|
IndexOffset = LittleEndian.ToUInt32 (header, 8),
|
||||||
FileTable = LittleEndian.ToUInt32 (header, 0x0c),
|
FileTable = LittleEndian.ToUInt32 (header, 0x0C),
|
||||||
DirTable = LittleEndian.ToUInt32 (header, 0x10),
|
DirTable = LittleEndian.ToUInt32 (header, 0x10),
|
||||||
|
CodePage = 932,
|
||||||
};
|
};
|
||||||
if (dx.DirTable >= dx.IndexSize || dx.FileTable >= dx.IndexSize)
|
}
|
||||||
|
|
||||||
|
DxHeader ReadArcHeaderV6 (ArcView file, int version, byte[] key)
|
||||||
|
{
|
||||||
|
var header = file.View.ReadBytes (4, 0x2C);
|
||||||
|
if (0x2C != header.Length)
|
||||||
return null;
|
return null;
|
||||||
using (var encrypted = file.CreateStream (dx.IndexOffset, dx.IndexSize))
|
Decrypt (header, 0, header.Length, 4, key);
|
||||||
using (var index = new EncryptedStream (encrypted, dx.IndexOffset, key))
|
Dump.Write (header);
|
||||||
using (var reader = new IndexReader (dx, version, index))
|
return new DxHeader {
|
||||||
{
|
IndexSize = LittleEndian.ToUInt32 (header, 0),
|
||||||
return reader.Read();
|
BaseOffset = LittleEndian.ToInt64 (header, 4),
|
||||||
}
|
IndexOffset = LittleEndian.ToInt64 (header, 0x0C),
|
||||||
|
FileTable = (uint)LittleEndian.ToInt64 (header, 0x14),
|
||||||
|
DirTable = (uint)LittleEndian.ToInt64 (header, 0x1C),
|
||||||
|
CodePage = LittleEndian.ToInt32 (header, 0x24),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void Decrypt (byte[] data, int index, int count, long offset, byte[] key)
|
internal static void Decrypt (byte[] data, int index, int count, long offset, byte[] key)
|
||||||
@@ -291,32 +326,33 @@ namespace GameRes.Formats.DxLib
|
|||||||
public uint IndexSize;
|
public uint IndexSize;
|
||||||
public uint FileTable;
|
public uint FileTable;
|
||||||
public uint DirTable;
|
public uint DirTable;
|
||||||
|
public int CodePage;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class DxDirectory
|
internal abstract class IndexReader : IDisposable
|
||||||
{
|
{
|
||||||
public int DirOffset;
|
protected readonly int m_version;
|
||||||
public int ParentDirOffset;
|
protected DxHeader m_header;
|
||||||
public int FileCount;
|
protected BinaryStream m_input;
|
||||||
public int FileTable;
|
protected Encoding m_encoding;
|
||||||
}
|
protected List<Entry> m_dir = new List<Entry>();
|
||||||
|
|
||||||
internal sealed class IndexReader : IDisposable
|
protected IndexReader (DxHeader header, int version, Stream input)
|
||||||
{
|
|
||||||
readonly int m_version;
|
|
||||||
readonly int m_entry_size;
|
|
||||||
DxHeader m_header;
|
|
||||||
BinaryReader m_input;
|
|
||||||
List<Entry> m_dir = new List<Entry>();
|
|
||||||
|
|
||||||
public List<Entry> Dir { get { return m_dir; } }
|
|
||||||
|
|
||||||
public IndexReader (DxHeader header, int version, Stream input)
|
|
||||||
{
|
{
|
||||||
m_version = version;
|
|
||||||
m_entry_size = m_version >= 2 ? 0x2C : 0x28;
|
|
||||||
m_header = header;
|
m_header = header;
|
||||||
m_input = new ArcView.Reader (input);
|
m_version = version;
|
||||||
|
m_input = new BinaryStream (input, "");
|
||||||
|
m_encoding = Encoding.GetEncoding (header.CodePage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IndexReader Create (DxHeader header, int version, Stream input)
|
||||||
|
{
|
||||||
|
if (version <= 4)
|
||||||
|
return new IndexReaderV2 (header, version, input);
|
||||||
|
else if (6 == version)
|
||||||
|
return new IndexReaderV6 (header, version, input);
|
||||||
|
else
|
||||||
|
throw new InvalidFormatException ("Not supported DX archive version.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Entry> Read ()
|
public List<Entry> Read ()
|
||||||
@@ -325,6 +361,46 @@ namespace GameRes.Formats.DxLib
|
|||||||
return m_dir;
|
return m_dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract void ReadFileTable (string root, long table_offset);
|
||||||
|
|
||||||
|
protected string ExtractFileName (long table_offset)
|
||||||
|
{
|
||||||
|
m_input.Position = table_offset;
|
||||||
|
int name_offset = m_input.ReadUInt16() * 4 + 4;
|
||||||
|
m_input.Position = table_offset + name_offset;
|
||||||
|
return m_input.ReadCString (m_encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IDisposable Members
|
||||||
|
bool disposed = false;
|
||||||
|
public void Dispose ()
|
||||||
|
{
|
||||||
|
if (!disposed)
|
||||||
|
{
|
||||||
|
m_input.Dispose();
|
||||||
|
disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class IndexReaderV2 : IndexReader
|
||||||
|
{
|
||||||
|
readonly int m_entry_size;
|
||||||
|
|
||||||
|
public IndexReaderV2 (DxHeader header, int version, Stream input) : base (header, version, input)
|
||||||
|
{
|
||||||
|
m_entry_size = m_version >= 2 ? 0x2C : 0x28;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DxDirectory
|
||||||
|
{
|
||||||
|
public int DirOffset;
|
||||||
|
public int ParentDirOffset;
|
||||||
|
public int FileCount;
|
||||||
|
public int FileTable;
|
||||||
|
}
|
||||||
|
|
||||||
DxDirectory ReadDirEntry ()
|
DxDirectory ReadDirEntry ()
|
||||||
{
|
{
|
||||||
var dir = new DxDirectory();
|
var dir = new DxDirectory();
|
||||||
@@ -335,22 +411,22 @@ namespace GameRes.Formats.DxLib
|
|||||||
return dir;
|
return dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadFileTable (string root, uint table_offset)
|
protected override void ReadFileTable (string root, long table_offset)
|
||||||
{
|
{
|
||||||
m_input.BaseStream.Position = m_header.DirTable + table_offset;
|
m_input.Position = m_header.DirTable + table_offset;
|
||||||
var dir = ReadDirEntry();
|
var dir = ReadDirEntry();
|
||||||
if (dir.DirOffset != -1 && dir.ParentDirOffset != -1)
|
if (dir.DirOffset != -1 && dir.ParentDirOffset != -1)
|
||||||
{
|
{
|
||||||
m_input.BaseStream.Position = m_header.FileTable + dir.DirOffset;
|
m_input.Position = m_header.FileTable + dir.DirOffset;
|
||||||
root = Path.Combine (root, ExtractFileName (m_input.ReadUInt32()));
|
root = Path.Combine (root, ExtractFileName (m_input.ReadUInt32()));
|
||||||
}
|
}
|
||||||
long current_pos = m_header.FileTable + dir.FileTable;
|
long current_pos = m_header.FileTable + dir.FileTable;
|
||||||
for (int i = 0; i < dir.FileCount; ++i)
|
for (int i = 0; i < dir.FileCount; ++i)
|
||||||
{
|
{
|
||||||
m_input.BaseStream.Position = current_pos;
|
m_input.Position = current_pos;
|
||||||
uint name_offset = m_input.ReadUInt32();
|
uint name_offset = m_input.ReadUInt32();
|
||||||
uint attr = m_input.ReadUInt32();
|
uint attr = m_input.ReadUInt32();
|
||||||
m_input.BaseStream.Seek (0x18, SeekOrigin.Current);
|
m_input.Seek (0x18, SeekOrigin.Current);
|
||||||
uint offset = m_input.ReadUInt32();
|
uint offset = m_input.ReadUInt32();
|
||||||
if (0 != (attr & 0x10)) // FILE_ATTRIBUTE_DIRECTORY
|
if (0 != (attr & 0x10)) // FILE_ATTRIBUTE_DIRECTORY
|
||||||
{
|
{
|
||||||
@@ -377,26 +453,75 @@ namespace GameRes.Formats.DxLib
|
|||||||
current_pos += m_entry_size;
|
current_pos += m_entry_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
string ExtractFileName (uint table_offset)
|
internal sealed class IndexReaderV6 : IndexReader
|
||||||
|
{
|
||||||
|
readonly int m_entry_size;
|
||||||
|
|
||||||
|
public IndexReaderV6 (DxHeader header, int version, Stream input) : base (header, version, input)
|
||||||
{
|
{
|
||||||
m_input.BaseStream.Position = table_offset;
|
m_entry_size = 0x40;
|
||||||
int name_offset = m_input.ReadUInt16() * 4 + 4;
|
|
||||||
m_input.BaseStream.Position = table_offset + name_offset;
|
|
||||||
return m_input.BaseStream.ReadCString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#region IDisposable Members
|
private class DxDirectory
|
||||||
bool disposed = false;
|
|
||||||
public void Dispose ()
|
|
||||||
{
|
{
|
||||||
if (!disposed)
|
public long DirOffset;
|
||||||
|
public long ParentDirOffset;
|
||||||
|
public int FileCount;
|
||||||
|
public long FileTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
DxDirectory ReadDirEntry ()
|
||||||
|
{
|
||||||
|
var dir = new DxDirectory();
|
||||||
|
dir.DirOffset = m_input.ReadInt64();
|
||||||
|
dir.ParentDirOffset = m_input.ReadInt64();
|
||||||
|
dir.FileCount = (int)m_input.ReadInt64();
|
||||||
|
dir.FileTable = m_input.ReadInt64();
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ReadFileTable (string root, long table_offset)
|
||||||
|
{
|
||||||
|
m_input.Position = m_header.DirTable + table_offset;
|
||||||
|
var dir = ReadDirEntry();
|
||||||
|
if (dir.DirOffset != -1 && dir.ParentDirOffset != -1)
|
||||||
{
|
{
|
||||||
m_input.Dispose();
|
m_input.Position = m_header.FileTable + dir.DirOffset;
|
||||||
disposed = true;
|
root = Path.Combine (root, ExtractFileName (m_input.ReadInt64()));
|
||||||
|
}
|
||||||
|
long current_pos = m_header.FileTable + dir.FileTable;
|
||||||
|
for (int i = 0; i < dir.FileCount; ++i)
|
||||||
|
{
|
||||||
|
m_input.Position = current_pos;
|
||||||
|
var name_offset = m_input.ReadInt64();
|
||||||
|
uint attr = (uint)m_input.ReadInt64();
|
||||||
|
m_input.Seek (0x18, SeekOrigin.Current);
|
||||||
|
var offset = m_input.ReadInt64();
|
||||||
|
if (0 != (attr & 0x10)) // FILE_ATTRIBUTE_DIRECTORY
|
||||||
|
{
|
||||||
|
if (0 == offset || table_offset == offset)
|
||||||
|
throw new InvalidFormatException ("Infinite recursion in DXA directory index");
|
||||||
|
ReadFileTable (root, offset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var size = m_input.ReadInt64();
|
||||||
|
var packed_size = m_input.ReadInt64();
|
||||||
|
var entry = FormatCatalog.Instance.Create<PackedEntry> (Path.Combine (root, ExtractFileName (name_offset)));
|
||||||
|
entry.Offset = m_header.BaseOffset + offset;
|
||||||
|
entry.UnpackedSize = (uint)size;
|
||||||
|
entry.IsPacked = -1 != packed_size;
|
||||||
|
if (entry.IsPacked)
|
||||||
|
entry.Size = (uint)packed_size;
|
||||||
|
else
|
||||||
|
entry.Size = (uint)size;
|
||||||
|
m_dir.Add (entry);
|
||||||
|
}
|
||||||
|
current_pos += m_entry_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class EncryptedStream : ProxyStream
|
internal class EncryptedStream : ProxyStream
|
||||||
|
|||||||
@@ -693,6 +693,8 @@ namespace GameRes.Formats.Emote
|
|||||||
ReadRgba4444 (pixels, stride);
|
ReadRgba4444 (pixels, stride);
|
||||||
else if ("RL" == m_info.TexType)
|
else if ("RL" == m_info.TexType)
|
||||||
ReadRle (pixels, stride);
|
ReadRle (pixels, stride);
|
||||||
|
else if ("DXT5" == m_info.TexType)
|
||||||
|
pixels = ReadDxt5();
|
||||||
else
|
else
|
||||||
throw new NotImplementedException (string.Format ("PSB texture format '{0}' not implemented", m_info.TexType));
|
throw new NotImplementedException (string.Format ("PSB texture format '{0}' not implemented", m_info.TexType));
|
||||||
return ImageData.Create (m_info, PixelFormats.Bgra32, null, pixels, stride);
|
return ImageData.Create (m_info, PixelFormats.Bgra32, null, pixels, stride);
|
||||||
@@ -799,5 +801,12 @@ namespace GameRes.Formats.Emote
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
byte[] ReadDxt5 ()
|
||||||
|
{
|
||||||
|
var packed = m_input.ReadBytes ((int)m_input.Length);
|
||||||
|
var dxt = new DirectDraw.DxtDecoder (packed, m_info);
|
||||||
|
return dxt.UnpackDXT5();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
251
ArcFormats/Kaguya/ArcAN21.cs
Normal file
251
ArcFormats/Kaguya/ArcAN21.cs
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
//! \file ArcAN21.cs
|
||||||
|
//! \date Sun Apr 30 21:04:25 2017
|
||||||
|
//! \brief KaGuYa script engine animation resource.
|
||||||
|
//
|
||||||
|
// 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.Linq;
|
||||||
|
using System.Windows.Media;
|
||||||
|
|
||||||
|
namespace GameRes.Formats.Kaguya
|
||||||
|
{
|
||||||
|
class An21Entry : PackedEntry
|
||||||
|
{
|
||||||
|
public int FrameIndex;
|
||||||
|
public int RleStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Export(typeof(ArchiveFormat))]
|
||||||
|
public class An21Opener : ArchiveFormat
|
||||||
|
{
|
||||||
|
public override string Tag { get { return "AN21/KAGUYA"; } }
|
||||||
|
public override string Description { get { return "KaGuYa script engine animation resource"; } }
|
||||||
|
public override uint Signature { get { return 0x31324E41; } } // 'AN21'
|
||||||
|
public override bool IsHierarchic { get { return false; } }
|
||||||
|
public override bool CanWrite { get { return false; } }
|
||||||
|
|
||||||
|
public An21Opener ()
|
||||||
|
{
|
||||||
|
Extensions = new string[] { "anm" };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ArcFile TryOpen (ArcView file)
|
||||||
|
{
|
||||||
|
int table_count = file.View.ReadUInt16 (4);
|
||||||
|
uint current_offset = 8;
|
||||||
|
for (int i = 0; i < table_count; ++i)
|
||||||
|
{
|
||||||
|
switch (file.View.ReadByte (current_offset++))
|
||||||
|
{
|
||||||
|
case 0: break;
|
||||||
|
case 1: current_offset += 8; break;
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
case 4:
|
||||||
|
case 5: current_offset += 4; break;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current_offset += 2 + file.View.ReadUInt16 (current_offset) * 8u;
|
||||||
|
if (!file.View.AsciiEqual (current_offset, "[PIC]10"))
|
||||||
|
return null;
|
||||||
|
current_offset += 7;
|
||||||
|
int frame_count = file.View.ReadInt16 (current_offset);
|
||||||
|
if (!IsSaneCount (frame_count))
|
||||||
|
return null;
|
||||||
|
current_offset += 0x12;
|
||||||
|
string base_name = Path.GetFileNameWithoutExtension (file.Name);
|
||||||
|
var dir = new List<Entry> (frame_count);
|
||||||
|
var info = new ImageMetaData
|
||||||
|
{
|
||||||
|
OffsetX = file.View.ReadInt32 (current_offset),
|
||||||
|
OffsetY = file.View.ReadInt32 (current_offset+4),
|
||||||
|
Width = file.View.ReadUInt32 (current_offset+8),
|
||||||
|
Height = file.View.ReadUInt32 (current_offset+12),
|
||||||
|
};
|
||||||
|
int channels = file.View.ReadInt32 (current_offset+0x10);
|
||||||
|
info.BPP = channels * 8;
|
||||||
|
current_offset += 0x14;
|
||||||
|
var entry = new An21Entry
|
||||||
|
{
|
||||||
|
FrameIndex = 0,
|
||||||
|
Name = string.Format ("{0}#{1:D2}", base_name, 0),
|
||||||
|
Type = "image",
|
||||||
|
Offset = current_offset,
|
||||||
|
Size = (uint)channels * info.Width * info.Height,
|
||||||
|
IsPacked = false,
|
||||||
|
};
|
||||||
|
dir.Add (entry);
|
||||||
|
current_offset += entry.Size;
|
||||||
|
for (int i = 1; i < frame_count; ++i)
|
||||||
|
{
|
||||||
|
int step = file.View.ReadByte (current_offset++);
|
||||||
|
if (0 == step)
|
||||||
|
return null;
|
||||||
|
uint packed_size = file.View.ReadUInt32 (current_offset);
|
||||||
|
uint unpacked_size = (uint)(channels * (info.OffsetX + (int)info.Width)
|
||||||
|
* (info.OffsetY + (int)info.Height));
|
||||||
|
current_offset += 4;
|
||||||
|
entry = new An21Entry
|
||||||
|
{
|
||||||
|
FrameIndex = i,
|
||||||
|
Name = string.Format ("{0}#{1:D2}", base_name, i),
|
||||||
|
Type = "image",
|
||||||
|
Offset = current_offset,
|
||||||
|
Size = packed_size,
|
||||||
|
UnpackedSize = unpacked_size,
|
||||||
|
IsPacked = true,
|
||||||
|
RleStep = step,
|
||||||
|
};
|
||||||
|
dir.Add (entry);
|
||||||
|
current_offset += packed_size;
|
||||||
|
}
|
||||||
|
return new An21Archive (file, this, dir, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||||
|
{
|
||||||
|
var anent = entry as An21Entry;
|
||||||
|
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||||
|
if (null == anent || !anent.IsPacked)
|
||||||
|
return input;
|
||||||
|
using (input)
|
||||||
|
{
|
||||||
|
var data = DecompressRLE (input, anent.UnpackedSize, anent.RleStep);
|
||||||
|
return new BinMemoryStream (data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
|
||||||
|
{
|
||||||
|
var anarc = (An21Archive)arc;
|
||||||
|
var anent = (An21Entry)entry;
|
||||||
|
var pixels = anarc.GetFrame (anent.FrameIndex);
|
||||||
|
return new BitmapDecoder (pixels, anarc.ImageInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static byte[] DecompressRLE (IBinaryStream input, uint unpacked_size, int rle_step)
|
||||||
|
{
|
||||||
|
var output = new byte[unpacked_size];
|
||||||
|
for (int i = 0; i < rle_step; ++i)
|
||||||
|
{
|
||||||
|
byte v1 = input.ReadUInt8();
|
||||||
|
output[i] = v1;
|
||||||
|
int dst = i + rle_step;
|
||||||
|
while (dst < output.Length)
|
||||||
|
{
|
||||||
|
byte v2 = input.ReadUInt8();
|
||||||
|
output[dst] = v2;
|
||||||
|
dst += rle_step;
|
||||||
|
if (v2 == v1)
|
||||||
|
{
|
||||||
|
int count = input.ReadUInt8();
|
||||||
|
if (0 != (count & 0x80))
|
||||||
|
count = input.ReadUInt8() + ((count & 0x7F) << 8) + 128;
|
||||||
|
while (count --> 0 && dst < output.Length)
|
||||||
|
{
|
||||||
|
output[dst] = v2;
|
||||||
|
dst += rle_step;
|
||||||
|
}
|
||||||
|
if (dst < output.Length)
|
||||||
|
{
|
||||||
|
v2 = input.ReadUInt8();
|
||||||
|
output[dst] = v2;
|
||||||
|
dst += rle_step;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v1 = v2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class An21Archive : AnmArchive
|
||||||
|
{
|
||||||
|
byte[][] Frames;
|
||||||
|
|
||||||
|
public An21Archive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, ImageMetaData base_info)
|
||||||
|
: base (arc, impl, dir, base_info)
|
||||||
|
{
|
||||||
|
Frames = new byte[dir.Count][];
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] GetFrame (int index)
|
||||||
|
{
|
||||||
|
if (index >= Frames.Length)
|
||||||
|
throw new ArgumentException ("index");
|
||||||
|
if (null != Frames[index])
|
||||||
|
return Frames[index];
|
||||||
|
|
||||||
|
var entry = Dir.ElementAt (index);
|
||||||
|
byte[] pixels;
|
||||||
|
using (var stream = OpenEntry (entry))
|
||||||
|
{
|
||||||
|
pixels = new byte[stream.Length];
|
||||||
|
stream.Read (pixels, 0, pixels.Length);
|
||||||
|
}
|
||||||
|
if (index > 0)
|
||||||
|
{
|
||||||
|
var prev_frame = GetFrame (index-1);
|
||||||
|
for (int i = 0; i < pixels.Length; ++i)
|
||||||
|
pixels[i] += prev_frame[i];
|
||||||
|
}
|
||||||
|
Frames[index] = pixels;
|
||||||
|
return pixels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BitmapDecoder : IImageDecoder
|
||||||
|
{
|
||||||
|
public Stream Source { get { return null; } }
|
||||||
|
public ImageFormat SourceFormat { get { return null; } }
|
||||||
|
public ImageMetaData Info { get; private set; }
|
||||||
|
public ImageData Image { get; private set; }
|
||||||
|
|
||||||
|
public BitmapDecoder (byte[] pixels, ImageMetaData info)
|
||||||
|
{
|
||||||
|
Info = info;
|
||||||
|
int stride = (int)info.Width * info.BPP / 8;
|
||||||
|
Image = ImageData.CreateFlipped (info, GetFormat(), null, pixels, stride);
|
||||||
|
}
|
||||||
|
|
||||||
|
PixelFormat GetFormat ()
|
||||||
|
{
|
||||||
|
switch (Info.BPP)
|
||||||
|
{
|
||||||
|
case 8: return PixelFormats.Gray8;
|
||||||
|
case 24: return PixelFormats.Bgr24;
|
||||||
|
case 32: return PixelFormats.Bgra32;
|
||||||
|
default: throw new InvalidFormatException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
//! \date Fri Jan 22 18:44:56 2016
|
//! \date Fri Jan 22 18:44:56 2016
|
||||||
//! \brief KaGuYa archive format.
|
//! \brief KaGuYa archive format.
|
||||||
//
|
//
|
||||||
// Copyright (C) 2016 by morkt
|
// Copyright (C) 2016-2017 by morkt
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to
|
// of this software and associated documentation files (the "Software"), to
|
||||||
@@ -436,13 +436,14 @@ namespace GameRes.Formats.Kaguya
|
|||||||
public static ParamsDeserializer Create (IBinaryStream input)
|
public static ParamsDeserializer Create (IBinaryStream input)
|
||||||
{
|
{
|
||||||
var header = input.ReadHeader (0x11);
|
var header = input.ReadHeader (0x11);
|
||||||
if (!header.AsciiEqual ("[SCR-PARAMS]"))
|
if (header.AsciiEqual ("[SCR-PARAMS]"))
|
||||||
return null;
|
{
|
||||||
if (header.AsciiEqual (12, "v02"))
|
if (header.AsciiEqual (12, "v02"))
|
||||||
return new ParamsV2Deserializer (input);
|
return new ParamsV2Deserializer (input);
|
||||||
else if (header.AsciiEqual (12, "v05.6"))
|
else if (header.AsciiEqual (12, "v05.5") || header.AsciiEqual (12, "v05.6"))
|
||||||
return new ParamsV5Deserializer (input);
|
return new ParamsV5Deserializer (input);
|
||||||
return null;
|
}
|
||||||
|
throw new UnknownEncryptionScheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual LinkEncryption GetEncryption ()
|
public virtual LinkEncryption GetEncryption ()
|
||||||
@@ -642,7 +643,10 @@ namespace GameRes.Formats.Kaguya
|
|||||||
new Tuple<string, Decryptor> ("AP", (a, e) => DecryptImage (a, e, 0xC)),
|
new Tuple<string, Decryptor> ("AP", (a, e) => DecryptImage (a, e, 0xC)),
|
||||||
};
|
};
|
||||||
if (anm_encrypted)
|
if (anm_encrypted)
|
||||||
|
{
|
||||||
table.Add (new Tuple<string, Decryptor> ("AN00", (a, e) => DecryptAn00 (a, e)));
|
table.Add (new Tuple<string, Decryptor> ("AN00", (a, e) => DecryptAn00 (a, e)));
|
||||||
|
table.Add (new Tuple<string, Decryptor> ("AN21", (a, e) => DecryptAn21 (a, e)));
|
||||||
|
}
|
||||||
m_type_table = table.ToArray();
|
m_type_table = table.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -683,6 +687,34 @@ namespace GameRes.Formats.Kaguya
|
|||||||
return new BinMemoryStream (data, entry.Name);
|
return new BinMemoryStream (data, entry.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stream DecryptAn21 (LinkArchive arc, LinkEntry entry)
|
||||||
|
{
|
||||||
|
var data = arc.File.View.ReadBytes (entry.Offset, entry.Size);
|
||||||
|
int count = data.ToUInt16 (4);
|
||||||
|
int offset = 8;
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
switch (data[offset++])
|
||||||
|
{
|
||||||
|
case 0: break;
|
||||||
|
case 1: offset += 8; break;
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
case 4:
|
||||||
|
case 5: offset += 4; break;
|
||||||
|
default: return new BinMemoryStream (data, entry.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count = data.ToUInt16 (offset);
|
||||||
|
offset += 2 + count * 8 + 0x21;
|
||||||
|
int w = data.ToInt32 (offset);
|
||||||
|
int h = data.ToInt32 (offset+4);
|
||||||
|
int channels = data.ToInt32 (offset+8);
|
||||||
|
offset += 12;
|
||||||
|
DecryptData (data, offset, channels * w * h);
|
||||||
|
return new BinMemoryStream (data, entry.Name);
|
||||||
|
}
|
||||||
|
|
||||||
void DecryptData (byte[] data, int index, int length)
|
void DecryptData (byte[] data, int index, int length)
|
||||||
{
|
{
|
||||||
while (length > 0)
|
while (length > 0)
|
||||||
|
|||||||
@@ -184,17 +184,16 @@ namespace GameRes.Formats.KiriKiri
|
|||||||
entry.Size = (uint)packed_size;
|
entry.Size = (uint)packed_size;
|
||||||
entry.UnpackedSize = (uint)file_size;
|
entry.UnpackedSize = (uint)file_size;
|
||||||
|
|
||||||
int name_size = header.ReadInt16();
|
|
||||||
if (name_size > 0x100 || name_size <= 0)
|
|
||||||
{
|
|
||||||
goto NextEntry;
|
|
||||||
}
|
|
||||||
if (entry.IsEncrypted || ForceEncryptionQuery)
|
if (entry.IsEncrypted || ForceEncryptionQuery)
|
||||||
entry.Cipher = crypt_algorithm.Value;
|
entry.Cipher = crypt_algorithm.Value;
|
||||||
else
|
else
|
||||||
entry.Cipher = NoCryptAlgorithm;
|
entry.Cipher = NoCryptAlgorithm;
|
||||||
|
|
||||||
var name = new string (header.ReadChars (name_size));
|
var name = entry.Cipher.ReadName (header);
|
||||||
|
if (null == name)
|
||||||
|
{
|
||||||
|
goto NextEntry;
|
||||||
|
}
|
||||||
if (entry.Cipher.ObfuscatedIndex && ObfuscatedPathRe.IsMatch (name))
|
if (entry.Cipher.ObfuscatedIndex && ObfuscatedPathRe.IsMatch (name))
|
||||||
{
|
{
|
||||||
goto NextEntry;
|
goto NextEntry;
|
||||||
|
|||||||
@@ -71,6 +71,18 @@ namespace GameRes.Formats.KiriKiri
|
|||||||
public virtual void Init (ArcFile arc)
|
public virtual void Init (ArcFile arc)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read entry name from archive index.
|
||||||
|
/// </summary>
|
||||||
|
public virtual string ReadName (BinaryReader header)
|
||||||
|
{
|
||||||
|
int name_size = header.ReadInt16();
|
||||||
|
if (name_size > 0 && name_size <= 0x100)
|
||||||
|
return new string (header.ReadChars (name_size));
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
@@ -976,4 +988,142 @@ namespace GameRes.Formats.KiriKiri
|
|||||||
Decrypt (entry, offset, data, pos, count);
|
Decrypt (entry, offset, data, pos, count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class PuCaCrypt : ICrypt
|
||||||
|
{
|
||||||
|
public uint[] HashTable;
|
||||||
|
public byte[] KeyTable;
|
||||||
|
|
||||||
|
public override void Decrypt (Xp3Entry entry, long offset, byte[] buffer, int pos, int count)
|
||||||
|
{
|
||||||
|
if (HashTable != null)
|
||||||
|
{
|
||||||
|
int i = Array.IndexOf (HashTable, entry.Hash);
|
||||||
|
if (i != -1)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < count; ++j)
|
||||||
|
buffer[pos+j] ^= KeyTable[i];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var hash_table = new byte[32];
|
||||||
|
uint hash = entry.Hash;
|
||||||
|
for (int k = 0; k < 32; k += 4)
|
||||||
|
{
|
||||||
|
if (0 != (hash & 1))
|
||||||
|
hash |= 0x80000000;
|
||||||
|
else
|
||||||
|
hash &= 0x7FFFFFFF;
|
||||||
|
LittleEndian.Pack (hash, hash_table, k);
|
||||||
|
hash >>= 1;
|
||||||
|
}
|
||||||
|
var key_table = new byte[0x400];
|
||||||
|
for (int l = 0; l < 32; ++l)
|
||||||
|
{
|
||||||
|
for (int m = 0; m < 32; ++m)
|
||||||
|
key_table[32 * l + m] = (byte)(~hash_table[l] ^ hash_table[m]);
|
||||||
|
}
|
||||||
|
for (int n = 0; n < count; ++n)
|
||||||
|
buffer[pos+n] ^= key_table[(offset + n) & 0x3FF];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Encrypt (Xp3Entry entry, long offset, byte[] buffer, int pos, int count)
|
||||||
|
{
|
||||||
|
Decrypt (entry, offset, buffer, pos, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class RhapsodyCrypt : ICrypt
|
||||||
|
{
|
||||||
|
public string FileListName { get; set; }
|
||||||
|
|
||||||
|
public override void Decrypt (Xp3Entry entry, long offset, byte[] buffer, int pos, int count)
|
||||||
|
{
|
||||||
|
var key = new byte[12];
|
||||||
|
LittleEndian.Pack (entry.Hash, key, 0);
|
||||||
|
LittleEndian.Pack (0x6E1DA9B2u, key, 4);
|
||||||
|
LittleEndian.Pack (0x0040C800u, key, 8);
|
||||||
|
int k = (int)(offset % 12);
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
buffer[pos+i] ^= key[k++];
|
||||||
|
if (12 == k)
|
||||||
|
k = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Encrypt (Xp3Entry entry, long offset, byte[] buffer, int pos, int count)
|
||||||
|
{
|
||||||
|
Decrypt (entry, offset, buffer, pos, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ReadName (BinaryReader header)
|
||||||
|
{
|
||||||
|
if (null == KnownNames)
|
||||||
|
ReadNames();
|
||||||
|
uint key = header.ReadUInt32();
|
||||||
|
uint name_hash = header.ReadUInt32() ^ key;
|
||||||
|
string name;
|
||||||
|
if (KnownNames.TryGetValue (name_hash, out name))
|
||||||
|
return name;
|
||||||
|
uint ext_hash = header.ReadUInt32() ^ key;
|
||||||
|
name = name_hash.ToString ("X8");
|
||||||
|
switch (ext_hash)
|
||||||
|
{
|
||||||
|
case 0x01854675: name += ".png"; break; // GetNameHash (".png")
|
||||||
|
case 0x03D435DE: name += ".map"; break; // GetNameHash (".map")
|
||||||
|
case 0x2D1F13E0: name += ".asd"; break; // GetNameHash (".asd")
|
||||||
|
case 0x482F4319: name += ".tjs"; break; // GetNameHash (".tjs")
|
||||||
|
case 0x58924012: name += ".txt"; break; // GetNameHash (".txt")
|
||||||
|
case 0xB01C48CA: name += ".ks"; break; // GetNameHash (".ks")
|
||||||
|
case 0xC0F7DFB2: name += ".wav"; break; // GetNameHash (".wav")
|
||||||
|
case 0xE3A31D19: name += ".jpg"; break; // GetNameHash (".jpg")
|
||||||
|
case 0xE7F3FEEB: name += ".ogg"; break; // GetNameHash (".ogg")
|
||||||
|
default: name += ext_hash.ToString ("X8"); break;
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint GetNameHash (string name)
|
||||||
|
{
|
||||||
|
uint hash = 0;
|
||||||
|
for (int i = 0; i < name.Length; ++i)
|
||||||
|
{
|
||||||
|
int c = char.ToLowerInvariant (name[i]);
|
||||||
|
hash = 0x1000193u * hash ^ (byte)c;
|
||||||
|
hash = 0x1000193u * hash ^ (byte)(c >> 8);
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadNames ()
|
||||||
|
{
|
||||||
|
var dir = FormatCatalog.Instance.DataDirectory;
|
||||||
|
var names_file = Path.Combine (dir, FileListName);
|
||||||
|
var names = new Dictionary<uint, string>();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var reader = new StreamReader (names_file))
|
||||||
|
{
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
var name = reader.ReadLine();
|
||||||
|
if (null == name)
|
||||||
|
break;
|
||||||
|
names[GetNameHash (name)] = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception X)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Trace.WriteLine (X.Message, "[RhapsodyCrypt]");
|
||||||
|
}
|
||||||
|
KnownNames = names;
|
||||||
|
}
|
||||||
|
|
||||||
|
[NonSerialized]
|
||||||
|
Dictionary<uint, string> KnownNames = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.Composition;
|
using System.ComponentModel.Composition;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
|
||||||
using GameRes.Cryptography;
|
|
||||||
using GameRes.Utility;
|
using GameRes.Utility;
|
||||||
|
|
||||||
namespace GameRes.Formats.Malie
|
namespace GameRes.Formats.Malie
|
||||||
@@ -115,46 +113,15 @@ namespace GameRes.Formats.Malie
|
|||||||
|
|
||||||
public class MalieArchive : ArcFile
|
public class MalieArchive : ArcFile
|
||||||
{
|
{
|
||||||
public readonly Camellia Encryption;
|
public readonly IMalieDecryptor Decryptor;
|
||||||
|
|
||||||
public MalieArchive (ArcView file, ArchiveFormat format, ICollection<Entry> dir, Camellia encryption)
|
public MalieArchive (ArcView file, ArchiveFormat format, ICollection<Entry> dir, IMalieDecryptor decr)
|
||||||
: base (file, format, dir)
|
: base (file, format, dir)
|
||||||
{
|
{
|
||||||
Encryption = encryption;
|
Decryptor = decr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
|
||||||
public class LibScheme
|
|
||||||
{
|
|
||||||
public uint DataAlign;
|
|
||||||
public uint[] Key;
|
|
||||||
|
|
||||||
public LibScheme (uint[] key) : this (0x1000, key)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public LibScheme (uint align, uint[] key)
|
|
||||||
{
|
|
||||||
DataAlign = align;
|
|
||||||
Key = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LibScheme (string key) : this (Camellia.GenerateKey (key))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public LibScheme (uint align, string key) : this (align, Camellia.GenerateKey (key))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable]
|
|
||||||
public class MalieScheme : ResourceScheme
|
|
||||||
{
|
|
||||||
public Dictionary<string, LibScheme> KnownSchemes;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Export(typeof(ArchiveFormat))]
|
[Export(typeof(ArchiveFormat))]
|
||||||
public class DatOpener : ArchiveFormat
|
public class DatOpener : ArchiveFormat
|
||||||
{
|
{
|
||||||
@@ -167,6 +134,7 @@ namespace GameRes.Formats.Malie
|
|||||||
public DatOpener ()
|
public DatOpener ()
|
||||||
{
|
{
|
||||||
Extensions = new string[] { "lib", "dat" };
|
Extensions = new string[] { "lib", "dat" };
|
||||||
|
Signatures = new uint[] { 0, 0x3F503FB1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ArcFile TryOpen (ArcView file)
|
public override ArcFile TryOpen (ArcView file)
|
||||||
@@ -176,19 +144,19 @@ namespace GameRes.Formats.Malie
|
|||||||
var header = new byte[0x10];
|
var header = new byte[0x10];
|
||||||
foreach (var scheme in KnownSchemes.Values)
|
foreach (var scheme in KnownSchemes.Values)
|
||||||
{
|
{
|
||||||
var encryption = new Camellia (scheme.Key);
|
var decryptor = scheme.CreateDecryptor();
|
||||||
ReadEncrypted (file.View, encryption, 0, header, 0, 0x10);
|
ReadEncrypted (file.View, decryptor, 0, header, 0, 0x10);
|
||||||
ILibIndexReader reader;
|
ILibIndexReader reader;
|
||||||
if (Binary.AsciiEqual (header, 0, "LIBP"))
|
if (Binary.AsciiEqual (header, 0, "LIBP"))
|
||||||
reader = new LibPReader (file, encryption, header, scheme);
|
reader = new LibPReader (file, decryptor, header, scheme);
|
||||||
else if (Binary.AsciiEqual (header, 0, "LIBU"))
|
else if (Binary.AsciiEqual (header, 0, "LIBU"))
|
||||||
reader = LibUReader.Create (file, encryption);
|
reader = LibUReader.Create (file, decryptor);
|
||||||
else
|
else
|
||||||
continue;
|
continue;
|
||||||
using (reader)
|
using (reader)
|
||||||
{
|
{
|
||||||
if (reader.ReadIndex())
|
if (reader.ReadIndex())
|
||||||
return new MalieArchive (file, this, reader.Dir, encryption);
|
return new MalieArchive (file, this, reader.Dir, decryptor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -199,25 +167,25 @@ namespace GameRes.Formats.Malie
|
|||||||
var march = arc as MalieArchive;
|
var march = arc as MalieArchive;
|
||||||
if (null == march)
|
if (null == march)
|
||||||
return arc.File.CreateStream (entry.Offset, entry.Size);
|
return arc.File.CreateStream (entry.Offset, entry.Size);
|
||||||
var input = new EncryptedStream (march.File, march.Encryption);
|
var input = new EncryptedStream (march.File, march.Decryptor);
|
||||||
return new StreamRegion (input, entry.Offset, entry.Size);
|
return new StreamRegion (input, entry.Offset, entry.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal abstract class LibIndexReader : ILibIndexReader
|
internal abstract class LibIndexReader : ILibIndexReader
|
||||||
{
|
{
|
||||||
protected ArcView.Frame m_view;
|
protected ArcView.Frame m_view;
|
||||||
protected readonly long m_max_offset;
|
protected readonly long m_max_offset;
|
||||||
protected Camellia m_enc;
|
protected IMalieDecryptor m_dec;
|
||||||
protected List<Entry> m_dir = new List<Entry>();
|
protected List<Entry> m_dir = new List<Entry>();
|
||||||
protected byte[] m_header;
|
protected byte[] m_header;
|
||||||
|
|
||||||
public List<Entry> Dir { get { return m_dir; } }
|
public List<Entry> Dir { get { return m_dir; } }
|
||||||
|
|
||||||
protected LibIndexReader (ArcView file, Camellia encryption, byte[] header)
|
protected LibIndexReader (ArcView file, IMalieDecryptor decryptor, byte[] header)
|
||||||
{
|
{
|
||||||
m_view = file.View;
|
m_view = file.View;
|
||||||
m_max_offset = file.MaxOffset;
|
m_max_offset = file.MaxOffset;
|
||||||
m_enc = encryption;
|
m_dec = decryptor;
|
||||||
m_header = header;
|
m_header = header;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,16 +206,16 @@ namespace GameRes.Formats.Malie
|
|||||||
|
|
||||||
internal class LibPReader : LibIndexReader
|
internal class LibPReader : LibIndexReader
|
||||||
{
|
{
|
||||||
byte[] m_index;
|
byte[] m_index;
|
||||||
long m_base_offset;
|
long m_base_offset;
|
||||||
long m_data_align;
|
uint[] m_offset_table;
|
||||||
uint[] m_offset_table;
|
LibScheme m_scheme;
|
||||||
|
|
||||||
public LibPReader (ArcView file, Camellia encryption, byte[] header, LibScheme scheme)
|
public LibPReader (ArcView file, IMalieDecryptor decryptor, byte[] header, LibScheme scheme)
|
||||||
: base (file, encryption, header)
|
: base (file, decryptor, header)
|
||||||
{
|
{
|
||||||
m_base_offset = 0;
|
m_base_offset = 0;
|
||||||
m_data_align = scheme.DataAlign - 1;
|
m_scheme = scheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool ReadIndex ()
|
public override bool ReadIndex ()
|
||||||
@@ -261,16 +229,16 @@ namespace GameRes.Formats.Malie
|
|||||||
var offsets = new byte[4 * offset_count];
|
var offsets = new byte[4 * offset_count];
|
||||||
|
|
||||||
m_base_offset += 0x10;
|
m_base_offset += 0x10;
|
||||||
if (m_index.Length != ReadEncrypted (m_view, m_enc, m_base_offset, m_index, 0, m_index.Length))
|
if (m_index.Length != ReadEncrypted (m_view, m_dec, m_base_offset, m_index, 0, m_index.Length))
|
||||||
return false;
|
return false;
|
||||||
m_base_offset += m_index.Length;
|
m_base_offset += m_index.Length;
|
||||||
if (offsets.Length != ReadEncrypted (m_view, m_enc, m_base_offset, offsets, 0, offsets.Length))
|
if (offsets.Length != ReadEncrypted (m_view, m_dec, m_base_offset, offsets, 0, offsets.Length))
|
||||||
return false;
|
return false;
|
||||||
m_offset_table = new uint[offset_count];
|
m_offset_table = new uint[offset_count];
|
||||||
Buffer.BlockCopy (offsets, 0, m_offset_table, 0, offsets.Length);
|
Buffer.BlockCopy (offsets, 0, m_offset_table, 0, offsets.Length);
|
||||||
|
|
||||||
m_base_offset += offsets.Length;
|
m_base_offset += offsets.Length;
|
||||||
m_base_offset = (m_base_offset + m_data_align) & ~m_data_align;
|
m_base_offset = m_scheme.GetAlignedOffset (m_base_offset);
|
||||||
|
|
||||||
m_dir.Capacity = offset_count;
|
m_dir.Capacity = offset_count;
|
||||||
ReadDir ("", 0, 1);
|
ReadDir ("", 0, 1);
|
||||||
@@ -306,7 +274,7 @@ namespace GameRes.Formats.Malie
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int ReadEncrypted (ArcView.Frame view, Camellia enc, long offset, byte[] buffer, int index, int length)
|
private static int ReadEncrypted (ArcView.Frame view, IMalieDecryptor dec, long offset, byte[] buffer, int index, int length)
|
||||||
{
|
{
|
||||||
int offset_pad = (int)offset & 0xF;
|
int offset_pad = (int)offset & 0xF;
|
||||||
int aligned_len = (offset_pad + length + 0xF) & ~0xF;
|
int aligned_len = (offset_pad + length + 0xF) & ~0xF;
|
||||||
@@ -328,7 +296,7 @@ namespace GameRes.Formats.Malie
|
|||||||
|
|
||||||
for (int block_count = aligned_len / 0x10; block_count > 0; --block_count)
|
for (int block_count = aligned_len / 0x10; block_count > 0; --block_count)
|
||||||
{
|
{
|
||||||
enc.DecryptBlock (offset, aligned_buf, block);
|
dec.DecryptBlock (offset, aligned_buf, block);
|
||||||
block += 0x10;
|
block += 0x10;
|
||||||
offset += 0x10;
|
offset += 0x10;
|
||||||
}
|
}
|
||||||
@@ -345,110 +313,4 @@ namespace GameRes.Formats.Malie
|
|||||||
set { KnownSchemes = ((MalieScheme)value).KnownSchemes; }
|
set { KnownSchemes = ((MalieScheme)value).KnownSchemes; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class EncryptedStream : Stream
|
|
||||||
{
|
|
||||||
ArcView.Frame m_view;
|
|
||||||
Camellia m_enc;
|
|
||||||
long m_max_offset;
|
|
||||||
long m_position = 0;
|
|
||||||
byte[] m_current_block = new byte[BlockLength];
|
|
||||||
int m_current_block_length = 0;
|
|
||||||
long m_current_block_position = 0;
|
|
||||||
|
|
||||||
public const int BlockLength = 0x1000;
|
|
||||||
|
|
||||||
public Camellia Encryption { get { return m_enc; } }
|
|
||||||
|
|
||||||
public EncryptedStream (ArcView mmap, Camellia encryption)
|
|
||||||
{
|
|
||||||
m_view = mmap.CreateFrame();
|
|
||||||
m_enc = encryption;
|
|
||||||
m_max_offset = mmap.MaxOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int Read (byte[] buf, int index, int count)
|
|
||||||
{
|
|
||||||
int total_read = 0;
|
|
||||||
bool refill_buffer = !(m_position >= m_current_block_position && m_position < m_current_block_position + m_current_block_length);
|
|
||||||
while (count > 0 && m_position < m_max_offset)
|
|
||||||
{
|
|
||||||
if (refill_buffer)
|
|
||||||
{
|
|
||||||
m_current_block_position = m_position & ~((long)BlockLength-1);
|
|
||||||
FillBuffer();
|
|
||||||
}
|
|
||||||
int src_offset = (int)m_position & (BlockLength-1);
|
|
||||||
int available = Math.Min (count, m_current_block_length - src_offset);
|
|
||||||
Buffer.BlockCopy (m_current_block, src_offset, buf, index, available);
|
|
||||||
m_position += available;
|
|
||||||
total_read += available;
|
|
||||||
index += available;
|
|
||||||
count -= available;
|
|
||||||
refill_buffer = true;
|
|
||||||
}
|
|
||||||
return total_read;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FillBuffer ()
|
|
||||||
{
|
|
||||||
m_current_block_length = m_view.Read (m_current_block_position, m_current_block, 0, (uint)BlockLength);
|
|
||||||
for (int offset = 0; offset < m_current_block_length; offset += 0x10)
|
|
||||||
{
|
|
||||||
m_enc.DecryptBlock (m_current_block_position+offset, m_current_block, offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region IO.Stream methods
|
|
||||||
public override bool CanRead { get { return !m_disposed; } }
|
|
||||||
public override bool CanWrite { get { return false; } }
|
|
||||||
public override bool CanSeek { get { return !m_disposed; } }
|
|
||||||
|
|
||||||
public override long Length { get { return m_max_offset; } }
|
|
||||||
public override long Position
|
|
||||||
{
|
|
||||||
get { return m_position; }
|
|
||||||
set { m_position = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public override long Seek (long pos, SeekOrigin whence)
|
|
||||||
{
|
|
||||||
if (SeekOrigin.Current == whence)
|
|
||||||
m_position += pos;
|
|
||||||
else if (SeekOrigin.End == whence)
|
|
||||||
m_position = m_max_offset + pos;
|
|
||||||
else
|
|
||||||
m_position = pos;
|
|
||||||
return m_position;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write (byte[] buf, int index, int count)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetLength (long length)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Flush ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region IDisposable methods
|
|
||||||
bool m_disposed = false;
|
|
||||||
protected override void Dispose (bool disposing)
|
|
||||||
{
|
|
||||||
if (!m_disposed)
|
|
||||||
{
|
|
||||||
if (disposing)
|
|
||||||
m_view.Dispose();
|
|
||||||
m_disposed = true;
|
|
||||||
base.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,9 +77,9 @@ namespace GameRes.Formats.Malie
|
|||||||
return new LibUReader (input);
|
return new LibUReader (input);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LibUReader Create (ArcView file, Camellia encryption)
|
public static LibUReader Create (ArcView file, IMalieDecryptor decryptor)
|
||||||
{
|
{
|
||||||
var input = new EncryptedStream (file, encryption);
|
var input = new EncryptedStream (file, decryptor);
|
||||||
return new LibUReader (input);
|
return new LibUReader (input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
100
ArcFormats/Malie/LibScheme.cs
Normal file
100
ArcFormats/Malie/LibScheme.cs
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
//! \file LibScheme.cs
|
||||||
|
//! \date Tue Jun 06 22:47:22 2017
|
||||||
|
//! \brief Malie encryption schemes.
|
||||||
|
//
|
||||||
|
// 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 GameRes.Cryptography;
|
||||||
|
|
||||||
|
namespace GameRes.Formats.Malie
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public abstract class LibScheme
|
||||||
|
{
|
||||||
|
uint DataAlign;
|
||||||
|
|
||||||
|
public LibScheme (uint align)
|
||||||
|
{
|
||||||
|
DataAlign = align;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract IMalieDecryptor CreateDecryptor ();
|
||||||
|
|
||||||
|
public virtual long GetAlignedOffset (long offset)
|
||||||
|
{
|
||||||
|
long align = DataAlign - 1;
|
||||||
|
return (offset + align) & ~align;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class LibCamelliaScheme : LibScheme
|
||||||
|
{
|
||||||
|
public uint[] Key { get; set; }
|
||||||
|
|
||||||
|
public LibCamelliaScheme (uint[] key) : this (0x1000, key)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public LibCamelliaScheme (uint align, uint[] key) : base (align)
|
||||||
|
{
|
||||||
|
Key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LibCamelliaScheme (string key) : this (Camellia.GenerateKey (key))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public LibCamelliaScheme (uint align, string key) : this (align, Camellia.GenerateKey (key))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IMalieDecryptor CreateDecryptor ()
|
||||||
|
{
|
||||||
|
return new CamelliaDecryptor (Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class LibCfiScheme : LibScheme
|
||||||
|
{
|
||||||
|
public byte[] Key { get; set; }
|
||||||
|
|
||||||
|
public LibCfiScheme (uint align, byte[] key) : base (align)
|
||||||
|
{
|
||||||
|
Key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IMalieDecryptor CreateDecryptor ()
|
||||||
|
{
|
||||||
|
return new CfiDecryptor (Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class MalieScheme : ResourceScheme
|
||||||
|
{
|
||||||
|
public Dictionary<string, LibScheme> KnownSchemes;
|
||||||
|
}
|
||||||
|
}
|
||||||
200
ArcFormats/Malie/MalieEncryption.cs
Normal file
200
ArcFormats/Malie/MalieEncryption.cs
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
//! \file MalieEncryption.cs
|
||||||
|
//! \date Tue Jun 06 20:38:57 2017
|
||||||
|
//! \brief Malie System encryption implementation.
|
||||||
|
//
|
||||||
|
// 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.IO;
|
||||||
|
using GameRes.Cryptography;
|
||||||
|
using GameRes.Utility;
|
||||||
|
|
||||||
|
namespace GameRes.Formats.Malie
|
||||||
|
{
|
||||||
|
public interface IMalieDecryptor
|
||||||
|
{
|
||||||
|
void DecryptBlock (long block_offset, byte[] buffer, int index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CamelliaDecryptor : IMalieDecryptor
|
||||||
|
{
|
||||||
|
Camellia m_enc;
|
||||||
|
|
||||||
|
public CamelliaDecryptor (uint[] key)
|
||||||
|
{
|
||||||
|
m_enc = new Camellia (key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DecryptBlock (long block_offset, byte[] buffer, int index)
|
||||||
|
{
|
||||||
|
m_enc.DecryptBlock (block_offset, buffer, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CfiDecryptor : IMalieDecryptor
|
||||||
|
{
|
||||||
|
byte[] m_key;
|
||||||
|
|
||||||
|
public CfiDecryptor (byte[] key)
|
||||||
|
{
|
||||||
|
m_key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DecryptBlock (long block_offset, byte[] data, int index)
|
||||||
|
{
|
||||||
|
if (null == data)
|
||||||
|
throw new ArgumentNullException ("data");
|
||||||
|
if (index < 0 || index + 0x10 > data.Length)
|
||||||
|
throw new ArgumentOutOfRangeException ("index");
|
||||||
|
int offset = (int)block_offset;
|
||||||
|
int o = offset & 0xF;
|
||||||
|
byte first = data[index+o];
|
||||||
|
for (int i = 0; i < 0x10; ++i)
|
||||||
|
{
|
||||||
|
if (o != i)
|
||||||
|
data[index+i] ^= first;
|
||||||
|
}
|
||||||
|
offset >>= 4;
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
fixed (byte* data8 = &data[index])
|
||||||
|
{
|
||||||
|
uint* data32 = (uint*)data8;
|
||||||
|
uint k = Binary.RotR (0x39653542, m_key[offset & 0x1F] ^ 0xA5);
|
||||||
|
data32[0] = Binary.RotR (data32[0] ^ k, m_key[(offset + 12) & 0x1F] ^ 0xA5);
|
||||||
|
k = Binary.RotL (0x76706367, m_key[(offset + 3) & 0x1F] ^ 0xA5);
|
||||||
|
data32[1] = Binary.RotL (data32[1] ^ k, m_key[(offset + 15) & 0x1F] ^ 0xA5);
|
||||||
|
k = Binary.RotR (0x69454462, m_key[(offset + 6) & 0x1F] ^ 0xA5);
|
||||||
|
data32[2] = Binary.RotR (data32[2] ^ k, m_key[(offset - 14) & 0x1F] ^ 0xA5);
|
||||||
|
k = Binary.RotL (0x71334334, m_key[(offset + 9) & 0x1F] ^ 0xA5);
|
||||||
|
data32[3] = Binary.RotL (data32[3] ^ k, m_key[(offset - 11) & 0x1F] ^ 0xA5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class EncryptedStream : Stream
|
||||||
|
{
|
||||||
|
ArcView.Frame m_view;
|
||||||
|
IMalieDecryptor m_dec;
|
||||||
|
long m_max_offset;
|
||||||
|
long m_position = 0;
|
||||||
|
byte[] m_current_block = new byte[BlockLength];
|
||||||
|
int m_current_block_length = 0;
|
||||||
|
long m_current_block_position = 0;
|
||||||
|
|
||||||
|
public const int BlockLength = 0x1000;
|
||||||
|
|
||||||
|
public IMalieDecryptor Decryptor { get { return m_dec; } }
|
||||||
|
|
||||||
|
public EncryptedStream (ArcView mmap, IMalieDecryptor decryptor)
|
||||||
|
{
|
||||||
|
m_view = mmap.CreateFrame();
|
||||||
|
m_dec = decryptor;
|
||||||
|
m_max_offset = mmap.MaxOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int Read (byte[] buf, int index, int count)
|
||||||
|
{
|
||||||
|
int total_read = 0;
|
||||||
|
bool refill_buffer = !(m_position >= m_current_block_position && m_position < m_current_block_position + m_current_block_length);
|
||||||
|
while (count > 0 && m_position < m_max_offset)
|
||||||
|
{
|
||||||
|
if (refill_buffer)
|
||||||
|
{
|
||||||
|
m_current_block_position = m_position & ~((long)BlockLength-1);
|
||||||
|
FillBuffer();
|
||||||
|
}
|
||||||
|
int src_offset = (int)m_position & (BlockLength-1);
|
||||||
|
int available = Math.Min (count, m_current_block_length - src_offset);
|
||||||
|
Buffer.BlockCopy (m_current_block, src_offset, buf, index, available);
|
||||||
|
m_position += available;
|
||||||
|
total_read += available;
|
||||||
|
index += available;
|
||||||
|
count -= available;
|
||||||
|
refill_buffer = true;
|
||||||
|
}
|
||||||
|
return total_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FillBuffer ()
|
||||||
|
{
|
||||||
|
m_current_block_length = m_view.Read (m_current_block_position, m_current_block, 0, (uint)BlockLength);
|
||||||
|
for (int offset = 0; offset < m_current_block_length; offset += 0x10)
|
||||||
|
{
|
||||||
|
m_dec.DecryptBlock (m_current_block_position+offset, m_current_block, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IO.Stream methods
|
||||||
|
public override bool CanRead { get { return !m_disposed; } }
|
||||||
|
public override bool CanWrite { get { return false; } }
|
||||||
|
public override bool CanSeek { get { return !m_disposed; } }
|
||||||
|
|
||||||
|
public override long Length { get { return m_max_offset; } }
|
||||||
|
public override long Position
|
||||||
|
{
|
||||||
|
get { return m_position; }
|
||||||
|
set { m_position = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override long Seek (long pos, SeekOrigin whence)
|
||||||
|
{
|
||||||
|
if (SeekOrigin.Current == whence)
|
||||||
|
m_position += pos;
|
||||||
|
else if (SeekOrigin.End == whence)
|
||||||
|
m_position = m_max_offset + pos;
|
||||||
|
else
|
||||||
|
m_position = pos;
|
||||||
|
return m_position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write (byte[] buf, int index, int count)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetLength (long length)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Flush ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IDisposable methods
|
||||||
|
bool m_disposed = false;
|
||||||
|
protected override void Dispose (bool disposing)
|
||||||
|
{
|
||||||
|
if (!m_disposed)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
m_view.Dispose();
|
||||||
|
m_disposed = true;
|
||||||
|
base.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion ("1.2.32.1373")]
|
[assembly: AssemblyVersion ("1.2.34.1396")]
|
||||||
[assembly: AssemblyFileVersion ("1.2.32.1373")]
|
[assembly: AssemblyFileVersion ("1.2.34.1396")]
|
||||||
|
|||||||
Binary file not shown.
@@ -57,6 +57,7 @@ namespace GameRes.Formats.Will
|
|||||||
ArcOpener ()
|
ArcOpener ()
|
||||||
{
|
{
|
||||||
Extensions = new string[] { "arc" };
|
Extensions = new string[] { "arc" };
|
||||||
|
Signatures = new uint[] { 1, 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ArcFile TryOpen (ArcView file)
|
public override ArcFile TryOpen (ArcView file)
|
||||||
|
|||||||
204
ArcFormats/YaneSDK/ArcHibiki.cs
Normal file
204
ArcFormats/YaneSDK/ArcHibiki.cs
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
//! \file ArcHibiki.cs
|
||||||
|
//! \date Fri May 12 19:06:19 2017
|
||||||
|
//! \brief Hibiki Works 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.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.Serialization.Formatters.Binary;
|
||||||
|
using GameRes.Utility;
|
||||||
|
|
||||||
|
namespace GameRes.Formats.YaneSDK
|
||||||
|
{
|
||||||
|
[Export(typeof(ArchiveFormat))]
|
||||||
|
public class HDatOpener : ArchiveFormat
|
||||||
|
{
|
||||||
|
public override string Tag { get { return "DAT/hibiki"; } }
|
||||||
|
public override string Description { get { return "YaneSDK engine resource archive"; } }
|
||||||
|
public override uint Signature { get { return 0; } }
|
||||||
|
public override bool IsHierarchic { get { return false; } }
|
||||||
|
public override bool CanWrite { get { return false; } }
|
||||||
|
|
||||||
|
public static readonly string SchemeFileName = "hibiki_works.dat";
|
||||||
|
|
||||||
|
public override ArcFile TryOpen (ArcView file)
|
||||||
|
{
|
||||||
|
if (!file.Name.HasExtension (".dat"))
|
||||||
|
return null;
|
||||||
|
int count = (short)(file.View.ReadUInt16 (0) ^ 0x8080);
|
||||||
|
if (!IsSaneCount (count))
|
||||||
|
return null;
|
||||||
|
var scheme = QueryScheme (file.Name);
|
||||||
|
if (null == scheme)
|
||||||
|
return null;
|
||||||
|
var dat_name = Path.GetFileName (file.Name).ToLowerInvariant();
|
||||||
|
IList<HibikiTocRecord> toc_table = null;
|
||||||
|
if (scheme.ArcMap != null && scheme.ArcMap.TryGetValue (dat_name, out toc_table))
|
||||||
|
{
|
||||||
|
if (toc_table.Count != count)
|
||||||
|
toc_table = null;
|
||||||
|
}
|
||||||
|
var lst_name = Path.ChangeExtension (file.Name, ".lst");
|
||||||
|
Stream input;
|
||||||
|
if (VFS.FileExists (lst_name))
|
||||||
|
input = VFS.OpenStream (lst_name);
|
||||||
|
else
|
||||||
|
input = file.CreateStream();
|
||||||
|
|
||||||
|
using (var dec = new XoredStream (input, 0x80))
|
||||||
|
using (var index = new BinaryReader (dec))
|
||||||
|
{
|
||||||
|
const int name_length = 0x100;
|
||||||
|
index.BaseStream.Position = 2;
|
||||||
|
Func<int, Entry> read_entry;
|
||||||
|
if (null == toc_table)
|
||||||
|
{
|
||||||
|
var name_buf = new byte[name_length];
|
||||||
|
read_entry = i => {
|
||||||
|
if (name_length != index.Read (name_buf, 0, name_length))
|
||||||
|
return null;
|
||||||
|
var name = Binary.GetCString (name_buf, 0);
|
||||||
|
var entry = FormatCatalog.Instance.Create<Entry> (name);
|
||||||
|
index.ReadUInt16();
|
||||||
|
entry.Size = index.ReadUInt32();
|
||||||
|
entry.Offset = index.ReadUInt32();
|
||||||
|
return entry;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
read_entry = i => {
|
||||||
|
index.BaseStream.Seek (name_length + 6, SeekOrigin.Current);
|
||||||
|
index.ReadUInt32(); // throws in case of EOF
|
||||||
|
var toc_entry = toc_table[i];
|
||||||
|
var entry = FormatCatalog.Instance.Create<Entry> (toc_entry.Name);
|
||||||
|
entry.Offset = toc_entry.Offset;
|
||||||
|
entry.Size = toc_entry.Size;
|
||||||
|
return entry;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
var dir = new List<Entry> (count);
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
var entry = read_entry (i);
|
||||||
|
if (!entry.CheckPlacement (file.MaxOffset))
|
||||||
|
return null;
|
||||||
|
dir.Add (entry);
|
||||||
|
}
|
||||||
|
return new HibikiArchive (file, this, dir, scheme.ContentKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||||
|
{
|
||||||
|
var harc = arc as HibikiArchive;
|
||||||
|
if (null == harc)
|
||||||
|
return base.OpenEntry (arc, entry);
|
||||||
|
var key = harc.Key;
|
||||||
|
uint encrypted = Math.Min (entry.Size, (uint)key.Length);
|
||||||
|
var header = arc.File.View.ReadBytes (entry.Offset, encrypted);
|
||||||
|
for (int i = 0; i < header.Length; ++i)
|
||||||
|
header[i] ^= key[i];
|
||||||
|
if (encrypted == entry.Size)
|
||||||
|
return new BinMemoryStream (header);
|
||||||
|
var rest = arc.File.CreateStream (entry.Offset + encrypted, entry.Size - encrypted);
|
||||||
|
return new PrefixStream (header, rest);
|
||||||
|
}
|
||||||
|
|
||||||
|
HibikiDatScheme QueryScheme (string arc_name)
|
||||||
|
{
|
||||||
|
if (null == KnownSchemes)
|
||||||
|
return null;
|
||||||
|
// XXX add GUI widget to select scheme
|
||||||
|
return KnownSchemes.Values.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Lazy<HibikiScheme> s_Scheme = new Lazy<HibikiScheme> (DeserializeScheme);
|
||||||
|
|
||||||
|
internal IDictionary<string, HibikiDatScheme> KnownSchemes {
|
||||||
|
get { return s_Scheme.Value.KnownSchemes; }
|
||||||
|
}
|
||||||
|
|
||||||
|
static HibikiScheme DeserializeScheme ()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var dir = FormatCatalog.Instance.DataDirectory;
|
||||||
|
var scheme_file = Path.Combine (dir, SchemeFileName);
|
||||||
|
using (var input = File.OpenRead (scheme_file))
|
||||||
|
{
|
||||||
|
var bin = new BinaryFormatter();
|
||||||
|
return (HibikiScheme)bin.Deserialize (input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception X)
|
||||||
|
{
|
||||||
|
Trace.WriteLine (X.Message, "hibiki_works scheme deserialization failed");
|
||||||
|
return new HibikiScheme();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class HibikiArchive : ArcFile
|
||||||
|
{
|
||||||
|
public readonly byte[] Key;
|
||||||
|
|
||||||
|
public HibikiArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, byte[] key)
|
||||||
|
: base (arc, impl, dir)
|
||||||
|
{
|
||||||
|
Key = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class HibikiScheme : ResourceScheme
|
||||||
|
{
|
||||||
|
public IDictionary<string, HibikiDatScheme> KnownSchemes;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class HibikiDatScheme
|
||||||
|
{
|
||||||
|
public byte[] ContentKey;
|
||||||
|
public IDictionary<string, IList<HibikiTocRecord>> ArcMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class HibikiTocRecord
|
||||||
|
{
|
||||||
|
public string Name;
|
||||||
|
public uint Offset;
|
||||||
|
public uint Size;
|
||||||
|
|
||||||
|
public HibikiTocRecord (string name, uint offset, uint size)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Offset = offset;
|
||||||
|
Size = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<NuGetPackageImportStamp>9af0b529</NuGetPackageImportStamp>
|
<NuGetPackageImportStamp>9af0b529</NuGetPackageImportStamp>
|
||||||
|
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
@@ -110,13 +111,6 @@
|
|||||||
<PreBuildEvent>perl "$(SolutionDir)inc-revision.pl" "$(ProjectPath)" $(ConfigurationName)
|
<PreBuildEvent>perl "$(SolutionDir)inc-revision.pl" "$(ProjectPath)" $(ConfigurationName)
|
||||||
exit 0</PreBuildEvent>
|
exit 0</PreBuildEvent>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="..\packages\System.Data.SQLite.Core.1.0.105.0\build\net45\System.Data.SQLite.Core.targets" Condition="Exists('..\packages\System.Data.SQLite.Core.1.0.105.0\build\net45\System.Data.SQLite.Core.targets')" />
|
|
||||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
|
||||||
<PropertyGroup>
|
|
||||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Error Condition="!Exists('..\packages\System.Data.SQLite.Core.1.0.105.0\build\net45\System.Data.SQLite.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\System.Data.SQLite.Core.1.0.105.0\build\net45\System.Data.SQLite.Core.targets'))" />
|
|
||||||
</Target>
|
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
<Target Name="BeforeBuild">
|
<Target Name="BeforeBuild">
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
<UseApplicationTrust>false</UseApplicationTrust>
|
<UseApplicationTrust>false</UseApplicationTrust>
|
||||||
<PublishWizardCompleted>true</PublishWizardCompleted>
|
<PublishWizardCompleted>true</PublishWizardCompleted>
|
||||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||||
|
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
|||||||
@@ -51,5 +51,5 @@ using System.Windows;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion ("1.4.28.1865")]
|
[assembly: AssemblyVersion ("1.4.30.1888")]
|
||||||
[assembly: AssemblyFileVersion ("1.4.28.1865")]
|
[assembly: AssemblyFileVersion ("1.4.30.1888")]
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
|
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
|||||||
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion ("1.4.29.252")]
|
[assembly: AssemblyVersion ("1.4.31.255")]
|
||||||
[assembly: AssemblyFileVersion ("1.4.29.252")]
|
[assembly: AssemblyFileVersion ("1.4.31.255")]
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ Remember11<br/>
|
|||||||
Chou Dengeki Stryker<br/>
|
Chou Dengeki Stryker<br/>
|
||||||
Chou Jikuu Bakuren Monogatari ~door pi chu~<br/>
|
Chou Jikuu Bakuren Monogatari ~door pi chu~<br/>
|
||||||
Eiken Kikaku<br/>
|
Eiken Kikaku<br/>
|
||||||
|
Fluorite Memories<br/>
|
||||||
H2O -Footprints in the Sand-<br/>
|
H2O -Footprints in the Sand-<br/>
|
||||||
Kanojo*Step<br/>
|
Kanojo*Step<br/>
|
||||||
Melty Moment<br/>
|
Melty Moment<br/>
|
||||||
@@ -216,6 +217,7 @@ Baldr Sky DiveX<br/>
|
|||||||
Fossette ~Cafe au Le Ciel Bleu~<br/>
|
Fossette ~Cafe au Le Ciel Bleu~<br/>
|
||||||
Jinki Extend Re:Vision<br/>
|
Jinki Extend Re:Vision<br/>
|
||||||
Maji de Watashi ni Koishinasai!<br/>
|
Maji de Watashi ni Koishinasai!<br/>
|
||||||
|
Platinum<br/>
|
||||||
Xross Scramble<br/>
|
Xross Scramble<br/>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
<tr><td>*.grp</td><td><tt>GR3</tt></td><td></td></tr>
|
<tr><td>*.grp</td><td><tt>GR3</tt></td><td></td></tr>
|
||||||
@@ -277,7 +279,9 @@ Akazukin to Mayoi no Mori<br/>
|
|||||||
Altered Pink ~Tokumu Sentai Duel Ranger~<br/>
|
Altered Pink ~Tokumu Sentai Duel Ranger~<br/>
|
||||||
Amairo*Islenauts<br/>
|
Amairo*Islenauts<br/>
|
||||||
Aozora Gakko no Sensei-kun<br/>
|
Aozora Gakko no Sensei-kun<br/>
|
||||||
|
Aqua<br/>
|
||||||
Bishuu ~Chigyaku no Mesu Dorei~<br/>
|
Bishuu ~Chigyaku no Mesu Dorei~<br/>
|
||||||
|
Boku to Koi Suru Ponkotsu Akuma.<br/>
|
||||||
Boku to Koi Suru Ponkotsu Akuma. Suggoi Ecchi!<br/>
|
Boku to Koi Suru Ponkotsu Akuma. Suggoi Ecchi!<br/>
|
||||||
Cafe Sourire<br/>
|
Cafe Sourire<br/>
|
||||||
Clover Day's<br/>
|
Clover Day's<br/>
|
||||||
@@ -292,8 +296,10 @@ Fair Child<br/>
|
|||||||
Fantasical<br/>
|
Fantasical<br/>
|
||||||
Fate/stay night<br/>
|
Fate/stay night<br/>
|
||||||
Fate/hollow ataraxia<br/>
|
Fate/hollow ataraxia<br/>
|
||||||
|
Fate/Knight Rhapsody ACT 2<br/>
|
||||||
G-senjou no Maou<br/>
|
G-senjou no Maou<br/>
|
||||||
Gakuen Butou no Folklore<br/>
|
Gakuen Butou no Folklore<br/>
|
||||||
|
Grisaia: Phantom Trigger Vol.2<br/>
|
||||||
Hachukano<br/>
|
Hachukano<br/>
|
||||||
Hanafubuki ~Sennen no Koi o Shimashita~<br/>
|
Hanafubuki ~Sennen no Koi o Shimashita~<br/>
|
||||||
Haruiro ☆ Communication ♪<br/>
|
Haruiro ☆ Communication ♪<br/>
|
||||||
@@ -328,6 +334,8 @@ Ore no Saimin Fantasia<br/>
|
|||||||
Otome*Domain<br/>
|
Otome*Domain<br/>
|
||||||
Ouka Ryouran<br/>
|
Ouka Ryouran<br/>
|
||||||
Oyako Ninjutsu Kunoichi PonPon!!<br/>
|
Oyako Ninjutsu Kunoichi PonPon!!<br/>
|
||||||
|
PRETTY x CATION<br/>
|
||||||
|
PURELY x CATION<br/>
|
||||||
Rakugaki Overheart<br/>
|
Rakugaki Overheart<br/>
|
||||||
RGH ~Koi to Hero to Gakuen to~<br/>
|
RGH ~Koi to Hero to Gakuen to~<br/>
|
||||||
Riajuu Saimin<br/>
|
Riajuu Saimin<br/>
|
||||||
@@ -348,11 +356,13 @@ Specialite!<br/>
|
|||||||
Suiheisen made Nan Mile?<br/>
|
Suiheisen made Nan Mile?<br/>
|
||||||
Swan Song<br/>
|
Swan Song<br/>
|
||||||
Teakamamire no Tenshi<br/>
|
Teakamamire no Tenshi<br/>
|
||||||
|
Toaru Jukujo no Hentai Choukyou<br/>
|
||||||
Towazugatari ~Shoujo Ryoujoku Hishou~<br/>
|
Towazugatari ~Shoujo Ryoujoku Hishou~<br/>
|
||||||
Ushinawareta Mirai o Motomete<br/>
|
Ushinawareta Mirai o Motomete<br/>
|
||||||
With Ribbon<br/>
|
With Ribbon<br/>
|
||||||
Yome Sagashi ga Hakadori Sugite Yabai.<br/>
|
Yome Sagashi ga Hakadori Sugite Yabai.<br/>
|
||||||
Yomibito Shirazu ~Amai Meshibe no Seichoushi~<br/>
|
Yomibito Shirazu ~Amai Meshibe no Seichoushi~<br/>
|
||||||
|
Yotsunoha<br/>
|
||||||
Yuugu ~Nyomitsu Gangu Adesugata~<br/>
|
Yuugu ~Nyomitsu Gangu Adesugata~<br/>
|
||||||
Yuugu 2 ~Dai Ni Kinsho no Nozomu Mono~<br/>
|
Yuugu 2 ~Dai Ni Kinsho no Nozomu Mono~<br/>
|
||||||
Zecchou Spiral!!<br/>
|
Zecchou Spiral!!<br/>
|
||||||
@@ -489,6 +499,7 @@ Bitch Nee-chan ga Seijun na Hazu ga Nai! <span class="footnote">ShiinaRio v2.50<
|
|||||||
Bitch Shimai 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/>
|
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/>
|
Can Fes! ~Itazura Majo to Naisho no Gakuensai~ <span class="footnote">ShiinaRio v2.47</span><br/>
|
||||||
|
Chiccha na Hanayome ~Mada Mada Tsubomi da mon~ <span class="footnote">ShiinaRio v2.50</span><br/>
|
||||||
Chikan Circle <span class="footnote">ShiinaRio v2.46</span><br/>
|
Chikan Circle <span class="footnote">ShiinaRio v2.46</span><br/>
|
||||||
Chikan Circle 2 <span class="footnote">ShiinaRio v2.47</span><br/>
|
Chikan Circle 2 <span class="footnote">ShiinaRio v2.47</span><br/>
|
||||||
Chuuchuu Nurse <span class="footnote">ShiinaRio v2.45</span><br/>
|
Chuuchuu Nurse <span class="footnote">ShiinaRio v2.45</span><br/>
|
||||||
@@ -765,6 +776,7 @@ Kimon Youitan<br/>
|
|||||||
Unbalance<br/>
|
Unbalance<br/>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
<tr class="odd"><td>*.lib<br/>*.dat</td><td><tt>LIB</tt><br/><tt>LIBP</tt><br/><tt>LIBU</tt><span class="footnote">encrypted</span></td><td></td><td rowspan="3">Malie</td><td rowspan="3">
|
<tr class="odd"><td>*.lib<br/>*.dat</td><td><tt>LIB</tt><br/><tt>LIBP</tt><br/><tt>LIBU</tt><span class="footnote">encrypted</span></td><td></td><td rowspan="3">Malie</td><td rowspan="3">
|
||||||
|
Aki Uso -The only neat thing to do-<br/>
|
||||||
Angel Crown<br/>
|
Angel Crown<br/>
|
||||||
Deep Love Diary<br/>
|
Deep Love Diary<br/>
|
||||||
Dies irae<br/>
|
Dies irae<br/>
|
||||||
@@ -926,6 +938,7 @@ Soukai no Valkyria <br/>
|
|||||||
<tr><td>*.dxa<br/>*.usi<br/>*.hud<br/>*.dat</td><td><tt>DX</tt><span class="footnote">encrypted</span></td><td></td><td rowspan="2">DxLib</td><td rowspan="2">
|
<tr><td>*.dxa<br/>*.usi<br/>*.hud<br/>*.dat</td><td><tt>DX</tt><span class="footnote">encrypted</span></td><td></td><td rowspan="2">DxLib</td><td rowspan="2">
|
||||||
Ashita wa Kitto, Haremasu you ni<br/>
|
Ashita wa Kitto, Haremasu you ni<br/>
|
||||||
Cross Quartz<br/>
|
Cross Quartz<br/>
|
||||||
|
Detective Seven<br/>
|
||||||
Hyakki Yakou<br/>
|
Hyakki Yakou<br/>
|
||||||
Mahou Senshi Extra Stage 2 ~Gakuen Kangoku~<br/>
|
Mahou Senshi Extra Stage 2 ~Gakuen Kangoku~<br/>
|
||||||
Saikyou Goshujin-sama! -Mighty My Master-<br/>
|
Saikyou Goshujin-sama! -Mighty My Master-<br/>
|
||||||
@@ -980,6 +993,7 @@ Moshimo Ashita ga Harenaraba<br/>
|
|||||||
<tr><td>*.gyu</td><td><tt>GYU\x1a</tt></td><td></td><td rowspan="2">ExHIBIT</td><td rowspan="2">
|
<tr><td>*.gyu</td><td><tt>GYU\x1a</tt></td><td></td><td rowspan="2">ExHIBIT</td><td rowspan="2">
|
||||||
Eve ~New Generation X~<br/>
|
Eve ~New Generation X~<br/>
|
||||||
Fuyu no Rondo<br/>
|
Fuyu no Rondo<br/>
|
||||||
|
Imouto Paradise! 2<br/>
|
||||||
Natural Another One 2nd -Belladonna-<br/>
|
Natural Another One 2nd -Belladonna-<br/>
|
||||||
Oshiete Ecchi Na Recipe -Anata to Watashi no Ama~i Seikatsu!-<br/>
|
Oshiete Ecchi Na Recipe -Anata to Watashi no Ama~i Seikatsu!-<br/>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
@@ -1123,13 +1137,14 @@ Dokidoki Onee-san<br/>
|
|||||||
Harami Tama<br/>
|
Harami Tama<br/>
|
||||||
Mahokoi ~Ecchi na Mahou de Koi x Koi Shichau~<br/>
|
Mahokoi ~Ecchi na Mahou de Koi x Koi Shichau~<br/>
|
||||||
Mainichi ga M!<br/>
|
Mainichi ga M!<br/>
|
||||||
|
Meidokissa<br/>
|
||||||
Ningyou no Yakata<br/>
|
Ningyou no Yakata<br/>
|
||||||
Osananajimi to Ama~ku Ecchi ni Sugosu Houhou<br/>
|
Osananajimi to Ama~ku Ecchi ni Sugosu Houhou<br/>
|
||||||
Toshishita Gentei Nuki x2 Share-house<br/>
|
Toshishita Gentei Nuki x2 Share-house<br/>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
<tr class="odd"><td>*.alp</td><td><tt>AP-0</tt><br/><tt>AP-2</tt></td><td></td></tr>
|
<tr class="odd"><td>*.alp</td><td><tt>AP-0</tt><br/><tt>AP-2</tt></td><td></td></tr>
|
||||||
<tr class="odd"><td>*.ap3</td><td><tt>\x04APS3</tt></td><td></td></tr>
|
<tr class="odd"><td>*.ap3</td><td><tt>\x04APS3</tt></td><td></td></tr>
|
||||||
<tr class="odd last"><td>*.anm</td><td><tt>AN00</tt></td><td></td></tr>
|
<tr class="odd last"><td>*.anm</td><td><tt>AN00</tt><br/><tt>AN21</tt></td><td></td></tr>
|
||||||
<tr><td>*.pcs</td><td><tt>PCCS</tt></td><td></td><td>C's ware</td><td>
|
<tr><td>*.pcs</td><td><tt>PCCS</tt></td><td></td><td>C's ware</td><td>
|
||||||
Kuro to Kuro to Kuro no Saidan ~Kodoku~<br/>
|
Kuro to Kuro to Kuro no Saidan ~Kodoku~<br/>
|
||||||
Mikan<br/>
|
Mikan<br/>
|
||||||
@@ -1171,7 +1186,6 @@ Angenehm Platz -Kleiner Garten Sie Erstellen-<br/>
|
|||||||
</td></tr>
|
</td></tr>
|
||||||
<tr class="odd"><td>*.arc</td><td><tt>ARCC</tt></td><td></td></tr>
|
<tr class="odd"><td>*.arc</td><td><tt>ARCC</tt></td><td></td></tr>
|
||||||
<tr class="odd last"><td>*.bin</td><td><tt>ODIO</tt></td><td></td></tr>
|
<tr class="odd last"><td>*.bin</td><td><tt>ODIO</tt></td><td></td></tr>
|
||||||
</td></tr>
|
|
||||||
<tr><td>*.eme</td><td><tt>RREDATA</tt></td><td></td><td>Emon Engine</td><td>
|
<tr><td>*.eme</td><td><tt>RREDATA</tt></td><td></td><td>Emon Engine</td><td>
|
||||||
Ase Nure Shoujo Misaki "Anata no Nioi de Icchau!"<br/>
|
Ase Nure Shoujo Misaki "Anata no Nioi de Icchau!"<br/>
|
||||||
D-spray Biyaku de Motemote Kachou Dairi Hosa<br/>
|
D-spray Biyaku de Motemote Kachou Dairi Hosa<br/>
|
||||||
@@ -1323,6 +1337,7 @@ Roshutsu Hentai Yuugi<br/>
|
|||||||
Ryou Seibai! ~Gakuen Bishoujo Seisai Hiroku~<br/>
|
Ryou Seibai! ~Gakuen Bishoujo Seisai Hiroku~<br/>
|
||||||
Samayoi Inmu Kousha ~Konna H na Jugyou, Arienai!~<br/>
|
Samayoi Inmu Kousha ~Konna H na Jugyou, Arienai!~<br/>
|
||||||
Sarai no Me<br/>
|
Sarai no Me<br/>
|
||||||
|
Shinyaku In'youchuu<br/>
|
||||||
Shukubo no Uzuki ~Hitozuma Miboujin no Nareta Karada to Amai Toiki~<br/>
|
Shukubo no Uzuki ~Hitozuma Miboujin no Nareta Karada to Amai Toiki~<br/>
|
||||||
Shukubo no Uzuki 2 ~Nareta Hitozuma kara Tadayou "Onna" no Iroka~<br/>
|
Shukubo no Uzuki 2 ~Nareta Hitozuma kara Tadayou "Onna" no Iroka~<br/>
|
||||||
Shukujo no Tsuyagoto<br/>
|
Shukujo no Tsuyagoto<br/>
|
||||||
@@ -1360,6 +1375,7 @@ Shokusou Tenshi Serika 2<br/>
|
|||||||
</td></tr>
|
</td></tr>
|
||||||
<tr class="odd"><td>*.dat</td><td>-</td><td></td><td>NekoSDK</td><td>
|
<tr class="odd"><td>*.dat</td><td>-</td><td></td><td>NekoSDK</td><td>
|
||||||
Elevator Panic ~Misshitsu no Inkou~<br/>
|
Elevator Panic ~Misshitsu no Inkou~<br/>
|
||||||
|
Yuuguu Settai #<br/>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
<tr><td>*.dat</td><td><tt>MK2.0</tt></td><td></td><td>MAIKA</td><td>
|
<tr><td>*.dat</td><td><tt>MK2.0</tt></td><td></td><td>MAIKA</td><td>
|
||||||
Inka Gakuen Taisen<br/>
|
Inka Gakuen Taisen<br/>
|
||||||
@@ -1492,8 +1508,9 @@ Doreijou<br/>
|
|||||||
<tr class="odd"><td>*.kgf</td><td><tt>KGF</tt></td><td></td></tr>
|
<tr class="odd"><td>*.kgf</td><td><tt>KGF</tt></td><td></td></tr>
|
||||||
<tr class="odd"><td>*.arc</td><td><tt>DAF\x1A</tt></td><td></td></tr>
|
<tr class="odd"><td>*.arc</td><td><tt>DAF\x1A</tt></td><td></td></tr>
|
||||||
<tr class="odd last"><td>*.cgf</td><td><tt>CGF\x1A</tt></td><td></td></tr>
|
<tr class="odd last"><td>*.cgf</td><td><tt>CGF\x1A</tt></td><td></td></tr>
|
||||||
<tr><td>*.pfs</td><td><tt>pf2</tt><br/><tt>pf8</tt></td><td></td><td rowspan="2">Artemis Engine</td><td rowspan="2">
|
<tr><td>*.pfs</td><td><tt>pf2</tt><br/><tt>pf6</tt><br/><tt>pf8</tt></td><td></td><td rowspan="2">Artemis Engine</td><td rowspan="2">
|
||||||
Boku no Elf Onee-san<br/>
|
Boku no Elf Onee-san<br/>
|
||||||
|
Memory's Dogma CODE:01<br/>
|
||||||
Tsugou no Ii Kanojo<br/>
|
Tsugou no Ii Kanojo<br/>
|
||||||
Tsugou no Ii Kazoku<br/>
|
Tsugou no Ii Kazoku<br/>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
@@ -1584,6 +1601,9 @@ Seisai no Resonance<br/>
|
|||||||
Sengoku Hime 7<br/>
|
Sengoku Hime 7<br/>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
<tr class="last"><td>*.ogg<br/>*.wav</td><td><tt>FSB5</tt></td><td></td></tr>
|
<tr class="last"><td>*.ogg<br/>*.wav</td><td><tt>FSB5</tt></td><td></td></tr>
|
||||||
|
<tr class="odd"><td>*.dat</td><td>-</td><td></td><td>YaneSDK?</td><td>
|
||||||
|
Niizuma Lovely x Cation<br/>
|
||||||
|
</td></tr>
|
||||||
</table>
|
</table>
|
||||||
<p><a name="note-1" class="footnote">1</a> Non-encrypted only</p>
|
<p><a name="note-1" class="footnote">1</a> Non-encrypted only</p>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<GARbro>
|
<GARbro>
|
||||||
<Release>
|
<Release>
|
||||||
<Version>1.4.28</Version>
|
<Version>1.4.30</Version>
|
||||||
<Url>https://github.com/morkt/GARbro/releases/latest</Url>
|
<Url>https://github.com/morkt/GARbro/releases/latest</Url>
|
||||||
<Notes>Updated Chinese translation.
|
<Notes>New formats:
|
||||||
|
- 'pf6' archives
|
||||||
New formats:
|
- DxLib version 6 archives (Detective Seven)
|
||||||
- 'UnityFS' archives and FSB5 audio
|
- updated encryption schemes for malie LIBP archives
|
||||||
- GRP audio archives
|
- added more KiriKiri and ShiinaRio encryption schemes
|
||||||
- more KiriKiri and ShiinaRio encryption schemes
|
|
||||||
</Notes>
|
</Notes>
|
||||||
</Release>
|
</Release>
|
||||||
<FormatsData>
|
<FormatsData>
|
||||||
<FileVersion>63</FileVersion>
|
<FileVersion>72</FileVersion>
|
||||||
<Url>https://github.com/morkt/GARbro/raw/master/ArcFormats/Resources/Formats.dat</Url>
|
<Url>https://github.com/morkt/GARbro/raw/master/ArcFormats/Resources/Formats.dat</Url>
|
||||||
<Requires>
|
<Requires>
|
||||||
<Assembly Name="ArcFormats" Version="1.2.31.1367"/>
|
<Assembly Name="ArcFormats" Version="1.2.33.1392"/>
|
||||||
<Assembly Name="GameRes" Version="1.4.26.238"/>
|
<Assembly Name="GameRes" Version="1.4.26.238"/>
|
||||||
</Requires>
|
</Requires>
|
||||||
</FormatsData>
|
</FormatsData>
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<repositories>
|
|
||||||
<repository path="..\ArcFormats\packages.config" />
|
|
||||||
<repository path="..\Experimental\packages.config" />
|
|
||||||
<repository path="..\GameRes\packages.config" />
|
|
||||||
<repository path="..\GUI\packages.config" />
|
|
||||||
</repositories>
|
|
||||||
Reference in New Issue
Block a user