implemented encrypted Tactics archives.

This commit is contained in:
morkt 2016-07-28 04:57:11 +04:00
parent 2f9964c166
commit c4fd78fb37
7 changed files with 292 additions and 11 deletions

View File

@ -97,6 +97,7 @@
<Compile Include="Cyberworks\WidgetBELL.xaml.cs"> <Compile Include="Cyberworks\WidgetBELL.xaml.cs">
<DependentUpon>WidgetBELL.xaml</DependentUpon> <DependentUpon>WidgetBELL.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="DDSystem\ArcDDP.cs" />
<Compile Include="Ipac\ArcIPAC.cs" /> <Compile Include="Ipac\ArcIPAC.cs" />
<Compile Include="Ipac\AudioWST.cs" /> <Compile Include="Ipac\AudioWST.cs" />
<Compile Include="Ipac\ImageIES.cs" /> <Compile Include="Ipac\ImageIES.cs" />
@ -229,6 +230,9 @@
<Compile Include="StudioEgo\ArcPAK0.cs" /> <Compile Include="StudioEgo\ArcPAK0.cs" />
<Compile Include="StudioEgo\ImageANT.cs" /> <Compile Include="StudioEgo\ImageANT.cs" />
<Compile Include="SuperNekoX\ArcGPC.cs" /> <Compile Include="SuperNekoX\ArcGPC.cs" />
<Compile Include="Tactics\WidgetTactics.xaml.cs">
<DependentUpon>WidgetTactics.xaml</DependentUpon>
</Compile>
<Compile Include="TechnoBrain\ImageIPH.cs" /> <Compile Include="TechnoBrain\ImageIPH.cs" />
<Compile Include="DxLib\ArcDX.cs" /> <Compile Include="DxLib\ArcDX.cs" />
<Compile Include="ArcMiris.cs" /> <Compile Include="ArcMiris.cs" />
@ -648,6 +652,10 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Tactics\WidgetTactics.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Will\CreateARCWidget.xaml"> <Page Include="Will\CreateARCWidget.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>

View File

@ -597,5 +597,29 @@ namespace GameRes.Formats.Properties {
this["NPKScheme"] = value; this["NPKScheme"] = value;
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")]
public string TacticsArcPassword {
get {
return ((string)(this["TacticsArcPassword"]));
}
set {
this["TacticsArcPassword"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")]
public string TacticsArcTitle {
get {
return ((string)(this["TacticsArcTitle"]));
}
set {
this["TacticsArcTitle"] = value;
}
}
} }
} }

View File

@ -146,5 +146,11 @@
<Setting Name="NPKScheme" Type="System.String" Scope="User"> <Setting Name="NPKScheme" Type="System.String" Scope="User">
<Value Profile="(Default)" /> <Value Profile="(Default)" />
</Setting> </Setting>
<Setting Name="TacticsArcPassword" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="TacticsArcTitle" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
</Settings> </Settings>
</SettingsFile> </SettingsFile>

View File

@ -2,7 +2,7 @@
//! \date Thu Jul 23 16:27:55 2015 //! \date Thu Jul 23 16:27:55 2015
//! \brief Tactics archive file implementation. //! \brief Tactics archive file implementation.
// //
// Copyright (C) 2015 by morkt // Copyright (C) 2015-2016 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
@ -30,18 +30,29 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
using GameRes.Compression; using GameRes.Compression;
using GameRes.Formats.Properties;
using GameRes.Formats.Strings;
using GameRes.Utility; using GameRes.Utility;
namespace GameRes.Formats.Tactics namespace GameRes.Formats.Tactics
{ {
internal class TacticsArcFile : ArcFile internal class TacticsArcFile : ArcFile
{ {
public byte[] Password; public readonly byte[] Password;
public readonly bool CustomLzss;
public TacticsArcFile (ArcView file, ArchiveFormat format, ICollection<Entry> dir, byte[] pass) public TacticsArcFile (ArcView file, ArchiveFormat format, ICollection<Entry> dir, byte[] pass)
: base (file, format, dir) : base (file, format, dir)
{ {
Password = pass; Password = pass;
CustomLzss = false;
}
public TacticsArcFile (ArcView file, ArchiveFormat format, ICollection<Entry> dir, ArcScheme scheme)
: base (file, format, dir)
{
Password = Encodings.cp932.GetBytes (scheme.Password);
CustomLzss = scheme.CustomLzss;
} }
} }
@ -74,7 +85,7 @@ namespace GameRes.Formats.Tactics
public override Stream OpenEntry (ArcFile arc, Entry entry) public override Stream OpenEntry (ArcFile arc, Entry entry)
{ {
var tarc = arc as TacticsArcFile; var tarc = arc as TacticsArcFile;
var tent = entry as PackedEntry; var tent = (PackedEntry)entry;
if (null == tarc || null == tarc.Password && !tent.IsPacked) if (null == tarc || null == tarc.Password && !tent.IsPacked)
return arc.File.CreateStream (entry.Offset, entry.Size); return arc.File.CreateStream (entry.Offset, entry.Size);
if (null == tarc.Password) if (null == tarc.Password)
@ -88,10 +99,15 @@ namespace GameRes.Formats.Tactics
if (p == tarc.Password.Length) if (p == tarc.Password.Length)
p = 0; p = 0;
} }
var input = new MemoryStream (data); if (tarc.CustomLzss && tent.IsPacked)
if (null == tent || !tent.IsPacked) {
data = UnpackCustomLzss (data);
return new MemoryStream (data);
}
Stream input = new MemoryStream (data);
if (tent.IsPacked)
input = new LzssStream (input);
return input; return input;
return new LzssStream (input);
} }
internal class IndexReader internal class IndexReader
@ -148,7 +164,8 @@ namespace GameRes.Formats.Tactics
using (var proxy = new InputProxyStream (input, true)) using (var proxy = new InputProxyStream (input, true))
using (var xored = new CryptoStream (proxy, new NotTransform(), CryptoStreamMode.Read)) using (var xored = new CryptoStream (proxy, new NotTransform(), CryptoStreamMode.Read))
using (var lzss = new LzssStream (xored)) using (var lzss = new LzssStream (xored))
lzss.Read (m_index, 0, m_index.Length); if (m_index.Length != lzss.Read (m_index, 0, m_index.Length))
return false;
int index_offset = Array.IndexOf<byte> (m_index, 0); int index_offset = Array.IndexOf<byte> (m_index, 0);
if (-1 == index_offset || 0 == index_offset) if (-1 == index_offset || 0 == index_offset)
@ -161,10 +178,10 @@ namespace GameRes.Formats.Tactics
var entry = new PackedEntry(); var entry = new PackedEntry();
entry.Offset = LittleEndian.ToUInt32 (m_index, index_offset) + base_offset; entry.Offset = LittleEndian.ToUInt32 (m_index, index_offset) + base_offset;
entry.Size = LittleEndian.ToUInt32 (m_index, index_offset + 4); entry.Size = LittleEndian.ToUInt32 (m_index, index_offset + 4);
entry.UnpackedSize = LittleEndian.ToUInt32 (m_index, index_offset + 8);
entry.IsPacked = entry.UnpackedSize != 0;
if (!entry.CheckPlacement (m_file.MaxOffset)) if (!entry.CheckPlacement (m_file.MaxOffset))
return false; return false;
entry.UnpackedSize = LittleEndian.ToUInt32 (m_index, index_offset + 8);
entry.IsPacked = entry.UnpackedSize != 0;
if (!entry.IsPacked) if (!entry.IsPacked)
entry.UnpackedSize = entry.Size; entry.UnpackedSize = entry.Size;
int name_len = LittleEndian.ToInt32 (m_index, index_offset + 0xC); int name_len = LittleEndian.ToInt32 (m_index, index_offset + 0xC);
@ -186,7 +203,8 @@ namespace GameRes.Formats.Tactics
return false; return false;
using (var lzss = new LzssStream (input, LzssMode.Decompress, true)) using (var lzss = new LzssStream (input, LzssMode.Decompress, true))
lzss.Read (m_index, 0, m_index.Length); if (m_index.Length != lzss.Read (m_index, 0, m_index.Length))
return false;
for (int i = 0; i < m_index.Length; ++i) for (int i = 0; i < m_index.Length; ++i)
{ {
@ -221,5 +239,193 @@ namespace GameRes.Formats.Tactics
return true; return true;
} }
} }
static readonly ushort[] LzRefTable = {
0x0001, 0x0804, 0x1001, 0x2001, 0x0002, 0x0805, 0x1002, 0x2002,
0x0003, 0x0806, 0x1003, 0x2003, 0x0004, 0x0807, 0x1004, 0x2004,
0x0005, 0x0808, 0x1005, 0x2005, 0x0006, 0x0809, 0x1006, 0x2006,
0x0007, 0x080A, 0x1007, 0x2007, 0x0008, 0x080B, 0x1008, 0x2008,
0x0009, 0x0904, 0x1009, 0x2009, 0x000A, 0x0905, 0x100A, 0x200A,
0x000B, 0x0906, 0x100B, 0x200B, 0x000C, 0x0907, 0x100C, 0x200C,
0x000D, 0x0908, 0x100D, 0x200D, 0x000E, 0x0909, 0x100E, 0x200E,
0x000F, 0x090A, 0x100F, 0x200F, 0x0010, 0x090B, 0x1010, 0x2010,
0x0011, 0x0A04, 0x1011, 0x2011, 0x0012, 0x0A05, 0x1012, 0x2012,
0x0013, 0x0A06, 0x1013, 0x2013, 0x0014, 0x0A07, 0x1014, 0x2014,
0x0015, 0x0A08, 0x1015, 0x2015, 0x0016, 0x0A09, 0x1016, 0x2016,
0x0017, 0x0A0A, 0x1017, 0x2017, 0x0018, 0x0A0B, 0x1018, 0x2018,
0x0019, 0x0B04, 0x1019, 0x2019, 0x001A, 0x0B05, 0x101A, 0x201A,
0x001B, 0x0B06, 0x101B, 0x201B, 0x001C, 0x0B07, 0x101C, 0x201C,
0x001D, 0x0B08, 0x101D, 0x201D, 0x001E, 0x0B09, 0x101E, 0x201E,
0x001F, 0x0B0A, 0x101F, 0x201F, 0x0020, 0x0B0B, 0x1020, 0x2020,
0x0021, 0x0C04, 0x1021, 0x2021, 0x0022, 0x0C05, 0x1022, 0x2022,
0x0023, 0x0C06, 0x1023, 0x2023, 0x0024, 0x0C07, 0x1024, 0x2024,
0x0025, 0x0C08, 0x1025, 0x2025, 0x0026, 0x0C09, 0x1026, 0x2026,
0x0027, 0x0C0A, 0x1027, 0x2027, 0x0028, 0x0C0B, 0x1028, 0x2028,
0x0029, 0x0D04, 0x1029, 0x2029, 0x002A, 0x0D05, 0x102A, 0x202A,
0x002B, 0x0D06, 0x102B, 0x202B, 0x002C, 0x0D07, 0x102C, 0x202C,
0x002D, 0x0D08, 0x102D, 0x202D, 0x002E, 0x0D09, 0x102E, 0x202E,
0x002F, 0x0D0A, 0x102F, 0x202F, 0x0030, 0x0D0B, 0x1030, 0x2030,
0x0031, 0x0E04, 0x1031, 0x2031, 0x0032, 0x0E05, 0x1032, 0x2032,
0x0033, 0x0E06, 0x1033, 0x2033, 0x0034, 0x0E07, 0x1034, 0x2034,
0x0035, 0x0E08, 0x1035, 0x2035, 0x0036, 0x0E09, 0x1036, 0x2036,
0x0037, 0x0E0A, 0x1037, 0x2037, 0x0038, 0x0E0B, 0x1038, 0x2038,
0x0039, 0x0F04, 0x1039, 0x2039, 0x003A, 0x0F05, 0x103A, 0x203A,
0x003B, 0x0F06, 0x103B, 0x203B, 0x003C, 0x0F07, 0x103C, 0x203C,
0x0801, 0x0F08, 0x103D, 0x203D, 0x1001, 0x0F09, 0x103E, 0x203E,
0x1801, 0x0F0A, 0x103F, 0x203F, 0x2001, 0x0F0B, 0x1040, 0x2040,
};
internal byte[] UnpackCustomLzss (byte[] input)
{
int unpacked_size = 0;
int src = 0;
int i = 0;
byte b;
do
{
b = input[src++];
unpacked_size |= (b & 0x7F) << i;
i += 7;
}
while (b >= 0x80);
if (unpacked_size <= 0)
throw new InvalidEncryptionScheme();
var output = new byte[unpacked_size];
int dst = 0;
while (dst < unpacked_size)
{
b = input[src++];
if (0 != (b & 3))
{
int offset_length = (LzRefTable[b] >> 8) & ~7;
int offset = 0;
for (i = 0; i < offset_length; i += 8)
offset |= input[src++] << i;
offset += LzRefTable[b] & 0x700;
int count = LzRefTable[b] & 0xFF;
Binary.CopyOverlapped (output, dst - offset, dst, count);
dst += count;
}
else
{
int count = (b >> 2) + 1;
if (count >= 0x3D)
{
int count_length = (count - 0x3C) * 8;
count = 0;
for (i = 0; i < count_length; i += 8)
count |= input[src++] << i;
count++;
}
Buffer.BlockCopy (input, src, output, dst, count);
src += count;
dst += count;
}
}
return output;
}
}
[Export(typeof(ArchiveFormat))]
public class Arc2Opener : ArcOpener
{
public override string Tag { get { return "ARC/Tactics/2"; } }
public override bool CanCreate { get { return false; } }
public Arc2Opener ()
{
Extensions = new string[] { "arc" };
}
public override ArcFile TryOpen (ArcView file)
{
if (!file.View.AsciiEqual (4, "ICS_ARC_FILE"))
return null;
var dir = new List<Entry>();
long offset = 0x10;
while (offset < file.MaxOffset)
{
uint size = file.View.ReadUInt32 (offset);
uint unpacked_size = file.View.ReadUInt32 (offset+4);
uint name_length = file.View.ReadUInt32 (offset+8);
if (0 == name_length)
break;
if (name_length > 0x100)
return null;
offset += 0x14;
var name = file.View.ReadString (offset, name_length);
offset += name_length;
if (offset + size > file.MaxOffset)
return null;
var entry = FormatCatalog.Instance.Create<PackedEntry> (name);
entry.Offset = offset;
entry.Size = size;
entry.IsPacked = unpacked_size != 0;
entry.UnpackedSize = entry.IsPacked ? unpacked_size : size;
dir.Add (entry);
offset += size;
}
if (0 == dir.Count)
return null;
var scheme = QueryScheme();
if (null == scheme)
return null;
return new TacticsArcFile (file, this, dir, scheme);
}
ArcScheme QueryScheme ()
{
var options = Query<TacticsOptions> (arcStrings.ArcEncryptedNotice);
return options.Scheme;
}
public override ResourceOptions GetDefaultOptions ()
{
string title = Settings.Default.TacticsArcTitle;
ArcScheme scheme = null;
if (!KnownSchemes.TryGetValue (title, out scheme) && !string.IsNullOrEmpty (Settings.Default.TacticsArcPassword))
scheme = new ArcScheme (Settings.Default.TacticsArcPassword);
return new TacticsOptions { Scheme = scheme };
}
public override object GetAccessWidget ()
{
return new GUI.WidgetTactics();
}
public static Dictionary<string, ArcScheme> KnownSchemes = new Dictionary<string, ArcScheme>();
public override ResourceScheme Scheme
{
get { return new SchemeMap { KnownSchemes = KnownSchemes }; }
set { KnownSchemes = ((SchemeMap)value).KnownSchemes; }
}
}
public class TacticsOptions : ResourceOptions
{
public ArcScheme Scheme;
}
[Serializable]
public class ArcScheme
{
public string Password;
public bool CustomLzss;
public ArcScheme (string password, bool custom_lzss = false)
{
Password = password;
CustomLzss = custom_lzss;
}
}
[Serializable]
public class SchemeMap : ResourceScheme
{
public Dictionary<string, ArcScheme> KnownSchemes;
} }
} }

View File

@ -0,0 +1,10 @@
<StackPanel x:Class="GameRes.Formats.GUI.WidgetTactics"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:p="clr-namespace:GameRes.Formats.Properties"
xmlns:s="clr-namespace:GameRes.Formats.Strings"
MaxWidth="250" Orientation="Vertical">
<ComboBox Name="Title" Width="250" HorizontalAlignment="Left" Margin="0,5,0,0"
ItemsSource="{Binding}" DisplayMemberPath="Key" SelectedValuePath="Key"
SelectedValue="{Binding Source={x:Static p:Settings.Default}, Path=TacticsArcTitle, Mode=TwoWay}"/>
</StackPanel>

View File

@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows.Controls;
using GameRes.Formats.Properties;
using GameRes.Formats.Tactics;
namespace GameRes.Formats.GUI
{
/// <summary>
/// Interaction logic for WidgetTactics.xaml
/// </summary>
public partial class WidgetTactics : StackPanel
{
public WidgetTactics()
{
InitializeComponent();
Title.ItemsSource = Arc2Opener.KnownSchemes.OrderBy (x => x.Key);
}
}
}

View File

@ -148,6 +148,12 @@
<setting name="NPKScheme" serializeAs="String"> <setting name="NPKScheme" serializeAs="String">
<value /> <value />
</setting> </setting>
<setting name="TacticsArcPassword" serializeAs="String">
<value />
</setting>
<setting name="TacticsArcTitle" serializeAs="String">
<value />
</setting>
</GameRes.Formats.Properties.Settings> </GameRes.Formats.Properties.Settings>
</userSettings> </userSettings>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup></configuration> <startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup></configuration>