diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj
index 0057045d..155e2339 100644
--- a/ArcFormats/ArcFormats.csproj
+++ b/ArcFormats/ArcFormats.csproj
@@ -126,6 +126,9 @@
+
+ WidgetDXA.xaml
+
@@ -1140,6 +1143,10 @@
Designer
MSBuild:Compile
+
+ Designer
+ MSBuild:Compile
+
Designer
MSBuild:Compile
diff --git a/ArcFormats/DxLib/ArcDX.cs b/ArcFormats/DxLib/ArcDX.cs
index 12a9e263..79073490 100644
--- a/ArcFormats/DxLib/ArcDX.cs
+++ b/ArcFormats/DxLib/ArcDX.cs
@@ -198,7 +198,7 @@ namespace GameRes.Formats.DxLib
}
}
- byte[] Unpack (Stream stream)
+ protected byte[] Unpack (Stream stream)
{
using (var input = new ArcView.Reader (stream))
{
@@ -262,7 +262,7 @@ namespace GameRes.Formats.DxLib
}
}
- List ReadIndex (ArcView file, int version, byte[] key)
+ protected List ReadIndex (ArcView file, int version, byte[] key)
{
DxHeader dx = null;
if (version <= 4)
@@ -313,12 +313,15 @@ namespace GameRes.Formats.DxLib
internal static void Decrypt (byte[] data, int index, int count, long offset, byte[] key)
{
- int key_pos = (int)(offset % key.Length);
- for (int i = 0; i < count; ++i)
+ if (key.Length !=0)
{
- data[index+i] ^= key[key_pos++];
- if (key.Length == key_pos)
- key_pos = 0;
+ int key_pos = (int)(offset % key.Length);
+ for (int i = 0; i < count; ++i)
+ {
+ data[index + i] ^= key[key_pos++];
+ if (key.Length == key_pos)
+ key_pos = 0;
+ }
}
}
@@ -361,8 +364,10 @@ namespace GameRes.Formats.DxLib
{
if (version <= 4)
return new IndexReaderV2 (header, version, input);
- else if (version >= 6)
+ else if (version >= 6 && version < 8)
return new IndexReaderV6 (header, version, input);
+ else if (version >=8)
+ return new IndexReaderV8 (header, version, input);
else
throw new InvalidFormatException ("Not supported DX archive version.");
}
@@ -545,7 +550,7 @@ namespace GameRes.Formats.DxLib
: base (stream, leave_open)
{
m_key = key;
- m_base_pos = (int)(base_position % m_key.Length);
+ m_base_pos = m_key.Length !=0 ?(int)(base_position % m_key.Length):0;
}
public override int Read (byte[] buffer, int offset, int count)
@@ -559,32 +564,48 @@ namespace GameRes.Formats.DxLib
public override int ReadByte ()
{
- int key_pos = (int)((m_base_pos + Position) % m_key.Length);
int b = BaseStream.ReadByte();
- if (-1 != b)
+ if (m_key.Length !=0)
{
- b ^= m_key[key_pos];
+ int key_pos = (int)((m_base_pos + Position) % m_key.Length);
+ if (-1 != b)
+ {
+ b ^= m_key[key_pos];
+ }
}
return b;
}
public override void Write (byte[] buffer, int offset, int count)
{
- int key_pos = (int)((m_base_pos + Position) % m_key.Length);
+
byte[] write_buf = new byte[count];
- for (int i = 0; i < count; ++i)
+ if (m_key.Length != 0)
{
- write_buf[i] = (byte)(buffer[offset+i] ^ m_key[key_pos++]);
- if (m_key.Length == key_pos)
- key_pos = 0;
+ int key_pos = (int)((m_base_pos + Position) % m_key.Length);
+ for (int i = 0; i < count; ++i)
+ {
+ write_buf[i] = (byte)(buffer[offset + i] ^ m_key[key_pos++]);
+ if (m_key.Length == key_pos)
+ key_pos = 0;
+ }
+ } else
+ {
+ write_buf = buffer;
}
BaseStream.Write (write_buf, 0, count);
}
public override void WriteByte (byte value)
{
- int key_pos = (int)((m_base_pos + Position) % m_key.Length);
- BaseStream.WriteByte ((byte)(value ^ m_key[key_pos]));
+ if(m_key.Length != 0)
+ {
+ int key_pos = (int)((m_base_pos + Position) % m_key.Length);
+ BaseStream.WriteByte((byte)(value ^ m_key[key_pos]));
+ } else
+ {
+ BaseStream.WriteByte ((byte)value);
+ }
}
}
}
diff --git a/ArcFormats/DxLib/ArcDX8.cs b/ArcFormats/DxLib/ArcDX8.cs
index 3c707813..d8e80857 100644
--- a/ArcFormats/DxLib/ArcDX8.cs
+++ b/ArcFormats/DxLib/ArcDX8.cs
@@ -23,14 +23,21 @@
// IN THE SOFTWARE.
//
+using GameRes.Formats.PkWare;
+using GameRes.Formats.Strings;
+using NAudio.SoundFont;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
+using System.Linq;
+
+
+
namespace GameRes.Formats.DxLib
{
- [Export(typeof(ArchiveFormat))]
+ [Export(typeof(ArchiveFormat))]
public class Dx8Opener : DxOpener
{
public override string Tag { get { return "BIN/DXLIB"; } }
@@ -51,14 +58,10 @@ namespace GameRes.Formats.DxLib
DxScheme DefaultScheme = new DxScheme { KnownKeys = new List() };
- internal struct DxHeaderV8
+ internal class DxHeaderV8 :DxHeader
{
- public long BaseOffset;
- public long IndexOffset;
- public uint IndexSize;
- public long FileTable;
- public long DirTable;
- public int CodePage;
+ new public long FileTable;
+ new public long DirTable;
public DXA8Flags Flags;
public byte HuffmanKB;
//15 bytes of padding.
@@ -67,7 +70,44 @@ namespace GameRes.Formats.DxLib
internal enum DXA8Flags : UInt32
{
DXA_FLAG_NO_KEY=1, //file is not encrypted
- DXA_FLAG_NO_HEAD_PRESS=1<<1, //do not compress headers
+ DXA_FLAG_NO_HEAD_PRESS=1<<1, //do not compress the entire file after compressing individual entries
+ }
+
+ [Serializable]
+ public class DXAOpts : ResourceOptions
+ {
+ public string Keyword;
+ }
+
+ public override ResourceOptions GetDefaultOptions()
+ {
+ return new DXAOpts
+ {
+ Keyword = Properties.Settings.Default.DXAPassword
+ };
+ }
+
+ string QueryPassword(ArcView file)
+ {
+ var options = Query(arcStrings.ZIPEncryptedNotice);
+ return options.Keyword;
+ }
+
+ public override ResourceOptions GetOptions(object widget)
+ {
+ if (widget is GUI.WidgetDXA)
+ {
+ return new DXAOpts
+ {
+ Keyword = ((GUI.WidgetDXA)widget).Password.Text
+ };
+ }
+ return GetDefaultOptions();
+ }
+
+ public override object GetAccessWidget()
+ {
+ return new GUI.WidgetDXA();
}
public override ArcFile TryOpen (ArcView file)
@@ -84,29 +124,105 @@ namespace GameRes.Formats.DxLib
};
if (dx.DirTable >= dx.IndexSize || dx.FileTable >= dx.IndexSize)
return null;
- //at this point we cannot proceed without user input. If NO_HEAD_PRESS is set we could maybe restore the 7-byte key
- //Otherwise (assuming the archive is encrypted) we have no way to continue without user input.
-
- //TODO: Ask for key here.
-
- var key = DefaultKey;
-
- var index = file.View.ReadBytes(dx.IndexOffset, dx.IndexSize);
- if ((dx.Flags & DXA8Flags.DXA_FLAG_NO_KEY) == 0)
+ DxKey8 key = null;
+
+ //FIXME: ReadBytes sets hard cap of filesize to 4GB.
+ var bodyBuffer = file.View.ReadBytes(dx.BaseOffset, (uint)(file.MaxOffset-dx.BaseOffset));
+ bool isencrypted = (dx.Flags & DXA8Flags.DXA_FLAG_NO_KEY) == 0;
+
+ if (isencrypted)
{
- if ((dx.Flags & DXA8Flags.DXA_FLAG_NO_HEAD_PRESS) != 0)
+ var keyStr = Query(arcStrings.ZIPEncryptedNotice).Keyword;
+ key = new DxKey8(keyStr);
+ Decrypt(bodyBuffer, 0, bodyBuffer.Length, 0, key.Key);
+
+
+ }
+ //Decrypted but might be compressed
+ if ((dx.Flags & DXA8Flags.DXA_FLAG_NO_HEAD_PRESS) == 0)
+ {
+ //IndexSize refers to uncompressed
+ throw new NotImplementedException();
+ }
+
+ var readyStr = new MemoryStream(bodyBuffer);
+ ArcView arcView = new ArcView(readyStr, "body",(uint) bodyBuffer.LongLength);
+ List entries;
+ using (var indexStr = arcView.CreateStream(dx.IndexOffset,dx.IndexSize))
+ using (var reader = IndexReader.Create(dx, 8, indexStr))
+ {
+ entries = reader.Read();
+ }
+ return new DxArchive(arcView, this,entries ,key, 8);
+ //return null;
+ }
+ }
+
+ internal sealed class IndexReaderV8 : IndexReader
+ {
+ readonly int m_entry_size;
+ public IndexReaderV8(DxHeader header, int version, Stream input) : base(header, version, input)
+ {
+ m_entry_size = 0x48;
+ }
+ private class DxDirectory
+ {
+ 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.Position = m_header.FileTable + dir.DirOffset;
+ 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
{
- Decrypt(index, 0, index.Length, 0, key);
+ if (0 == offset || table_offset == offset)
+ throw new InvalidFormatException("Infinite recursion in DXA directory index");
+ ReadFileTable(root, offset);
}
else
{
- //input is compressed. First by huffman then by LZ. if it's also encrypted then we're stuck.
- throw new NotImplementedException();
+ var size = m_input.ReadInt64();
+ var packed_size = m_input.ReadInt64();
+ var huffman_packed_size = m_input.ReadInt64();
+ var entry = FormatCatalog.Instance.Create(Path.Combine(root, ExtractFileName(name_offset)));
+ entry.Offset = m_header.BaseOffset + offset;
+ entry.UnpackedSize = (uint)size;
+ entry.IsPacked = (-1 != packed_size) || -1 != huffman_packed_size;
+ if (entry.IsPacked)
+ entry.Size = (uint)(huffman_packed_size!=-1 ? huffman_packed_size:packed_size);
+ else
+ entry.Size = (uint)size;
+ m_dir.Add(entry);
}
+ current_pos += m_entry_size;
}
- // decrypt-2
- // decompress
- return null;
}
}
}
diff --git a/ArcFormats/DxLib/WidgetDXA.xaml b/ArcFormats/DxLib/WidgetDXA.xaml
new file mode 100644
index 00000000..25e817e7
--- /dev/null
+++ b/ArcFormats/DxLib/WidgetDXA.xaml
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/ArcFormats/DxLib/WidgetDXA.xaml.cs b/ArcFormats/DxLib/WidgetDXA.xaml.cs
new file mode 100644
index 00000000..1f40f949
--- /dev/null
+++ b/ArcFormats/DxLib/WidgetDXA.xaml.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using GameRes.Formats.DxLib;
+
+namespace GameRes.Formats.GUI
+{
+ ///
+ /// Logika interakcji dla klasy WidgetDXA.xaml
+ ///
+ public partial class WidgetDXA : StackPanel
+ {
+ public WidgetDXA()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/ArcFormats/Properties/Settings.Designer.cs b/ArcFormats/Properties/Settings.Designer.cs
index 60c30c27..192f7036 100644
--- a/ArcFormats/Properties/Settings.Designer.cs
+++ b/ArcFormats/Properties/Settings.Designer.cs
@@ -1,10 +1,10 @@
//------------------------------------------------------------------------------
//
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
+// Ten kod został wygenerowany przez narzędzie.
+// Wersja wykonawcza:4.0.30319.42000
//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
+// Zmiany w tym pliku mogą spowodować nieprawidłowe zachowanie i zostaną utracone, jeśli
+// kod zostanie ponownie wygenerowany.
//
//------------------------------------------------------------------------------
@@ -12,7 +12,7 @@ namespace GameRes.Formats.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.3.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.10.0.0")]
public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@@ -813,5 +813,17 @@ namespace GameRes.Formats.Properties {
this["AFAEncodingCP"] = value;
}
}
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("DXARC")]
+ public string DXAPassword {
+ get {
+ return ((string)(this["DXAPassword"]));
+ }
+ set {
+ this["DXAPassword"] = value;
+ }
+ }
}
}
diff --git a/ArcFormats/Properties/Settings.settings b/ArcFormats/Properties/Settings.settings
index 4b4c6753..ebd367cb 100644
--- a/ArcFormats/Properties/Settings.settings
+++ b/ArcFormats/Properties/Settings.settings
@@ -200,5 +200,8 @@
932
+
+ DXARC
+
\ No newline at end of file
diff --git a/ArcFormats/app.config b/ArcFormats/app.config
index dd9cbd38..e78ee4ea 100644
--- a/ArcFormats/app.config
+++ b/ArcFormats/app.config
@@ -202,6 +202,9 @@
932
+
+ DXARC
+