mirror of
https://github.com/crskycode/GARbro.git
synced 2024-11-26 23:24:00 +08:00
Merge pull request #39 from SlawekNowy/master
Add support for DXArchive8.
This commit is contained in:
commit
7b3bb7e730
@ -126,6 +126,10 @@
|
||||
<Compile Include="Artemis\ImageNekoPNG.cs" />
|
||||
<Compile Include="CsWare\AudioWAV.cs" />
|
||||
<Compile Include="CsWare\ImageGDT.cs" />
|
||||
<Compile Include="DxLib\HuffmanDecoder.cs" />
|
||||
<Compile Include="DxLib\WidgetDXA.xaml.cs">
|
||||
<DependentUpon>WidgetDXA.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="FC01\ArcBDT.cs" />
|
||||
<Compile Include="FC01\ArcSCXA.cs" />
|
||||
<Compile Include="FC01\BdtTables.cs" />
|
||||
@ -1140,6 +1144,10 @@
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="DxLib\WidgetDXA.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="DxLib\WidgetSCR.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
@ -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<Entry> ReadIndex (ArcView file, int version, byte[] key)
|
||||
protected List<Entry> ReadIndex (ArcView file, int version, byte[] key)
|
||||
{
|
||||
DxHeader dx = null;
|
||||
if (version <= 4)
|
||||
@ -271,7 +271,7 @@ namespace GameRes.Formats.DxLib
|
||||
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 encrypted = file.CreateStream (dx.IndexOffset, (uint)dx.IndexSize))
|
||||
using (var index = new EncryptedStream (encrypted, version >= 6 ? 0 : dx.IndexOffset, key))
|
||||
using (var reader = IndexReader.Create (dx, version, index))
|
||||
{
|
||||
@ -305,21 +305,23 @@ namespace GameRes.Formats.DxLib
|
||||
IndexSize = LittleEndian.ToUInt32 (header, 0),
|
||||
BaseOffset = LittleEndian.ToInt64 (header, 4),
|
||||
IndexOffset = LittleEndian.ToInt64 (header, 0x0C),
|
||||
FileTable = (uint)LittleEndian.ToInt64 (header, 0x14),
|
||||
DirTable = (uint)LittleEndian.ToInt64 (header, 0x1C),
|
||||
FileTable = LittleEndian.ToInt64 (header, 0x14),
|
||||
DirTable = LittleEndian.ToInt64 (header, 0x1C),
|
||||
CodePage = LittleEndian.ToInt32 (header, 0x24),
|
||||
};
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
data[index+i] ^= key[key_pos++];
|
||||
if (key.Length == key_pos)
|
||||
key_pos = 0;
|
||||
}
|
||||
if (key.Length == 0)
|
||||
return;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public override ResourceScheme Scheme
|
||||
@ -333,9 +335,9 @@ namespace GameRes.Formats.DxLib
|
||||
{
|
||||
public long BaseOffset;
|
||||
public long IndexOffset;
|
||||
public uint IndexSize;
|
||||
public uint FileTable;
|
||||
public uint DirTable;
|
||||
public long IndexSize;
|
||||
public long FileTable;
|
||||
public long DirTable;
|
||||
public int CodePage;
|
||||
}
|
||||
|
||||
@ -361,8 +363,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 +549,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 +563,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,13 +23,47 @@
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using GameRes.Formats.Strings;
|
||||
using System;
|
||||
using System.CodeDom;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
using System.Windows.Navigation;
|
||||
using static GameRes.Formats.DxLib.Dx8Opener;
|
||||
|
||||
|
||||
|
||||
|
||||
namespace GameRes.Formats.DxLib
|
||||
{
|
||||
|
||||
internal class DXA8PackedEntry : PackedEntry {
|
||||
public bool HuffmanCompressed { get; set; }
|
||||
public uint HuffmanSize { get; set; }
|
||||
|
||||
public uint LZSize { get; set; }
|
||||
}
|
||||
|
||||
internal class DxArchive8 : DxArchive
|
||||
{
|
||||
public byte huffmanMaxKB;
|
||||
|
||||
public DxArchive8(ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, IDxKey enc, int version,byte huffmanKB) : base(arc, impl, dir, enc, version)
|
||||
{
|
||||
huffmanMaxKB = huffmanKB;
|
||||
}
|
||||
}
|
||||
|
||||
internal class DxHeaderV8 : DxHeader
|
||||
{
|
||||
public DXA8Flags Flags;
|
||||
public byte HuffmanKB; // oddly used only in Compression process not in decompression.
|
||||
//15 bytes of padding.
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class Dx8Opener : DxOpener
|
||||
{
|
||||
@ -41,30 +75,252 @@ namespace GameRes.Formats.DxLib
|
||||
|
||||
public Dx8Opener ()
|
||||
{
|
||||
Extensions = new[] { "bin" };
|
||||
Extensions = new string[] { "dxa", "hud", "usi", "med", "dat", "bin", "bcx", "wolf" };
|
||||
Signatures = new[] { 0x00085844u };
|
||||
}
|
||||
|
||||
static readonly byte[] DefaultKey = new byte[] { 0xBE, 0xC8, 0x8A, 0xF5, 0x28, 0x50, 0xC9 };
|
||||
//static readonly byte[] DefaultKey = new byte[] { 0xBE, 0xC8, 0x8A, 0xF5, 0x28, 0x50, 0xC9 };
|
||||
|
||||
|
||||
DxScheme DefaultScheme = new DxScheme { KnownKeys = new List<IDxKey>() };
|
||||
|
||||
|
||||
|
||||
|
||||
internal enum DXA8Flags : UInt32
|
||||
{
|
||||
DXA_FLAG_NO_KEY=1, //file is not encrypted
|
||||
DXA_FLAG_NO_HEAD_PRESS=1<<1, //do not compress the header after compressing individual entries
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class DXAOpts : ResourceOptions
|
||||
{
|
||||
public string Keyword;
|
||||
}
|
||||
|
||||
public override ResourceOptions GetDefaultOptions()
|
||||
{
|
||||
return new DXAOpts
|
||||
{
|
||||
Keyword = Properties.Settings.Default.DXAPassword
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
var dx = new DxHeader {
|
||||
var dx = new DxHeaderV8 {
|
||||
IndexSize = file.View.ReadUInt32 (4),
|
||||
BaseOffset = file.View.ReadInt64 (8),
|
||||
IndexOffset = file.View.ReadInt64 (0x10),
|
||||
FileTable = (uint)file.View.ReadInt64 (0x18),
|
||||
DirTable = (uint)file.View.ReadInt64 (0x20),
|
||||
FileTable = file.View.ReadInt64 (0x18),
|
||||
DirTable = file.View.ReadInt64 (0x20),
|
||||
CodePage = file.View.ReadInt32 (0x28),
|
||||
Flags = (DXA8Flags)file.View.ReadUInt32(0x2C),
|
||||
HuffmanKB = file.View.ReadByte(0x30)
|
||||
};
|
||||
if (dx.DirTable >= dx.IndexSize || dx.FileTable >= dx.IndexSize)
|
||||
return null;
|
||||
var key = DefaultKey;
|
||||
var index = file.View.ReadBytes (dx.IndexOffset, dx.IndexSize);
|
||||
Decrypt (index, 0, index.Length, 0, key);
|
||||
// decrypt-2
|
||||
// decompress
|
||||
return null;
|
||||
DxKey8 key = null;
|
||||
|
||||
//FIXME: ReadBytes sets hard cap of filesize to 4GB.
|
||||
var headerBuffer = file.View.ReadBytes(dx.IndexOffset, (uint)(file.MaxOffset-dx.IndexOffset));
|
||||
bool isencrypted = (dx.Flags & DXA8Flags.DXA_FLAG_NO_KEY) == 0;
|
||||
|
||||
if (isencrypted)
|
||||
{
|
||||
var keyStr = Query<DXAOpts>(arcStrings.ZIPEncryptedNotice).Keyword;
|
||||
key = new DxKey8(keyStr,dx.CodePage);
|
||||
|
||||
|
||||
}
|
||||
|
||||
Decrypt(headerBuffer, 0, headerBuffer.Length, 0, key.Key);
|
||||
//Decrypted but might be compressed
|
||||
if ((dx.Flags & DXA8Flags.DXA_FLAG_NO_HEAD_PRESS) == 0)
|
||||
{
|
||||
byte[] huffmanBuffer = new byte[headerBuffer.Length];
|
||||
byte[] lzBuffer;
|
||||
headerBuffer.CopyTo(huffmanBuffer, 0);
|
||||
//huffmanBuffer = headerBuffer;
|
||||
HuffmanDecoder decoder = new HuffmanDecoder(huffmanBuffer, (ulong)huffmanBuffer.LongLength);
|
||||
lzBuffer = decoder.Unpack();
|
||||
MemoryStream lzStream = new MemoryStream(lzBuffer);
|
||||
headerBuffer = Unpack(lzStream);
|
||||
|
||||
}
|
||||
|
||||
|
||||
List<Entry> entries;
|
||||
//There MAY be the case where the singular file is over 4GB, but it's very rare.
|
||||
using (var reader = IndexReader.Create(dx, 8, new MemoryStream(headerBuffer)))
|
||||
{
|
||||
entries = reader.Read();
|
||||
}
|
||||
return new DxArchive8(file, this,entries ,key, 8,dx.HuffmanKB);
|
||||
//retu rn null;
|
||||
}
|
||||
|
||||
public override Stream OpenEntry(ArcFile arc, Entry entry)
|
||||
{
|
||||
Stream input = arc.File.CreateStream(entry.Offset, entry.Size);
|
||||
var dx_arc = arc as DxArchive8;
|
||||
if (null == dx_arc)
|
||||
return input;
|
||||
var dx_ent = (DXA8PackedEntry)entry;
|
||||
long dec_offset = dx_ent.UnpackedSize;
|
||||
var key = dx_arc.Encryption.GetEntryKey(dx_ent.Name);
|
||||
input = new EncryptedStream(input, dec_offset, key);
|
||||
|
||||
byte[] tmpBuffer = new byte[dx_ent.Size];
|
||||
input.Read(tmpBuffer, 0, tmpBuffer.Length);
|
||||
if (dx_ent.HuffmanCompressed)
|
||||
{
|
||||
byte[] buffer = new byte[dx_ent.HuffmanSize];
|
||||
byte[] outBuffer = new byte[dx_ent.IsPacked ? dx_ent.LZSize : dx_ent.UnpackedSize];
|
||||
Array.Copy(tmpBuffer, buffer, dx_ent.HuffmanSize);
|
||||
HuffmanDecoder decoder = new HuffmanDecoder(buffer,dx_ent.HuffmanSize);
|
||||
byte[] partTmpBuffer = decoder.Unpack();
|
||||
//returned buffer might be partial. Check if this is the case.
|
||||
var outBufSize = dx_ent.IsPacked ? dx_ent.LZSize : dx_ent.UnpackedSize;
|
||||
if(dx_arc.huffmanMaxKB != 0xff && outBufSize > dx_arc.huffmanMaxKB * 1024 * 2)
|
||||
{
|
||||
//What we have here is two huffmanMaxKB KB buffers, that constitute the beginning and end of file respectively.
|
||||
Array.Copy(partTmpBuffer,0, outBuffer, 0,dx_arc.huffmanMaxKB*1024);
|
||||
Array.Copy(partTmpBuffer,dx_arc.huffmanMaxKB*1024,outBuffer,outBuffer.Length-dx_arc.huffmanMaxKB*1024,dx_arc.huffmanMaxKB*1024);
|
||||
//uncompressed part goes into middle.
|
||||
Array.Copy(tmpBuffer, dx_ent.HuffmanSize, outBuffer, dx_arc.huffmanMaxKB * 1024, outBufSize - dx_arc.huffmanMaxKB * 1024 * 2);
|
||||
tmpBuffer = outBuffer;
|
||||
} else
|
||||
{
|
||||
//that is all that needs to be done.
|
||||
tmpBuffer = partTmpBuffer;
|
||||
}
|
||||
}
|
||||
if(dx_ent.IsPacked)
|
||||
{
|
||||
byte[] buffer = new byte[dx_ent.LZSize];
|
||||
tmpBuffer.CopyTo(buffer, 0);
|
||||
var tmpMemStream = new MemoryStream(buffer);
|
||||
tmpBuffer = Unpack(tmpMemStream);
|
||||
|
||||
}
|
||||
return new BinMemoryStream(tmpBuffer, entry.Name);
|
||||
|
||||
/*
|
||||
if (!dx_ent.IsPacked)
|
||||
return input;
|
||||
using (input)
|
||||
{
|
||||
var data = Unpack(input);
|
||||
return new BinMemoryStream(data, entry.Name);
|
||||
}
|
||||
*/
|
||||
//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
|
||||
{
|
||||
DirOffset = m_input.ReadInt64(),
|
||||
ParentDirOffset = m_input.ReadInt64(),
|
||||
FileCount = (int)m_input.ReadInt64(),
|
||||
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
|
||||
{
|
||||
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 huffman_packed_size = m_input.ReadInt64();
|
||||
var entry = FormatCatalog.Instance.Create<DXA8PackedEntry>(Path.Combine(root, ExtractFileName(name_offset)));
|
||||
entry.Offset = m_header.BaseOffset + offset;
|
||||
entry.UnpackedSize = (uint)size;
|
||||
entry.IsPacked = -1 != packed_size;
|
||||
entry.HuffmanCompressed = -1 != huffman_packed_size;
|
||||
entry.HuffmanSize = (uint)huffman_packed_size;
|
||||
entry.LZSize = (uint)packed_size;
|
||||
//Huffman compression: huffman_packed_size will not exceed 2*HuffmanKB KB. The rest of data is uncompressed (as far as Huffman compressor is concerned).
|
||||
//Add length of uncompressed data to entry.Size
|
||||
if (entry.HuffmanCompressed)
|
||||
{
|
||||
var outBufSize = entry.IsPacked ? packed_size : size;
|
||||
var dx8_hdr = (DxHeaderV8)m_header;
|
||||
if (outBufSize > dx8_hdr.HuffmanKB * 1024 * 2)
|
||||
{
|
||||
huffman_packed_size += outBufSize - dx8_hdr.HuffmanKB * 1024 * 2;
|
||||
}
|
||||
}
|
||||
if (entry.IsPacked||entry.HuffmanCompressed)
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,8 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.DxLib
|
||||
@ -153,4 +155,78 @@ namespace GameRes.Formats.DxLib
|
||||
throw new NotSupportedException ("SHA-256 key cannot be restored.");
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class DxKey8 : DxKey
|
||||
{
|
||||
private int codepage;
|
||||
public DxKey8(string password,int codepage) : base(password ?? "DXARC")
|
||||
{
|
||||
this.codepage = codepage;
|
||||
}
|
||||
|
||||
public override byte[] GetEntryKey(string name)
|
||||
{
|
||||
var password = this.Password;
|
||||
var path = name.Split('\\', '/');
|
||||
password += string.Join("", path.Reverse().Select(n => n.ToUpperInvariant()));
|
||||
return CreateKey(password);
|
||||
}
|
||||
|
||||
protected override byte[] CreateKey(string keyword)
|
||||
{
|
||||
//from DxArchive.cpp
|
||||
//first check if the keyword is too short
|
||||
if (keyword.Length < 4)
|
||||
{
|
||||
keyword += "DXARC";
|
||||
}
|
||||
//first split string to bytes. Use original encoding as basis. Otherwise we would fail to decrypt that.
|
||||
Encoding tgtEncoding = Encoding.GetEncoding(codepage);
|
||||
byte[] tgtBytes = tgtEncoding.GetBytes(keyword);
|
||||
byte[] oddBuffer = new byte[(tgtBytes.Length/2)+(tgtBytes.Length%2)]; int oddCounter = 0;
|
||||
byte[] evenBuffer = new byte[(tgtBytes.Length/2)]; int evenCounter = 0;
|
||||
for (int i=0; i<tgtBytes.Length;i+=2,oddCounter++)
|
||||
{
|
||||
oddBuffer[oddCounter] = tgtBytes[i];
|
||||
}
|
||||
for (int i = 1; i < tgtBytes.Length; i += 2, evenCounter++)
|
||||
{
|
||||
evenBuffer[evenCounter] = tgtBytes[i];
|
||||
}
|
||||
UInt32 crc_0, crc_1;
|
||||
crc_0 = Crc32.Compute(oddBuffer, 0, oddCounter);
|
||||
crc_1 = Crc32.Compute(evenBuffer, 0, evenCounter);
|
||||
|
||||
byte[] key = new byte[7];
|
||||
byte[] crc_0_Bytes = BitConverter.GetBytes(crc_0),crc_1_Bytes=BitConverter.GetBytes(crc_1);
|
||||
key[0] = crc_0_Bytes[0];
|
||||
key[1] = crc_0_Bytes[1];
|
||||
key[2] = crc_0_Bytes[2];
|
||||
key[3] = crc_0_Bytes[3];
|
||||
key[4] = crc_1_Bytes[0];
|
||||
key[5] = crc_1_Bytes[1];
|
||||
key[6] = crc_1_Bytes[2];
|
||||
return key;
|
||||
|
||||
/*
|
||||
string oddString, evenString;
|
||||
oddString = string.Concat(keyword.Where((c, i) => i % 2 == 0));
|
||||
evenString = string.Concat(keyword.Where((c, i) => (i+1) % 2 == 0));
|
||||
UInt32 crc_0, crc_1;
|
||||
crc_0 = Crc32.Compute(Encoding.ASCII.GetBytes(oddString), 0, oddString.Length);
|
||||
crc_1 = Crc32.Compute(Encoding.ASCII.GetBytes(evenString), 0, evenString.Length); */
|
||||
/*
|
||||
using (var sha = SHA256.Create())
|
||||
{
|
||||
var bytes = Encodings.cp932.GetBytes(keyword);
|
||||
return sha.ComputeHash(bytes);
|
||||
} */
|
||||
}
|
||||
|
||||
protected override string RestoreKey(byte[] key)
|
||||
{
|
||||
throw new NotSupportedException("CRC key cannot be restored.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
368
ArcFormats/DxLib/HuffmanDecoder.cs
Normal file
368
ArcFormats/DxLib/HuffmanDecoder.cs
Normal file
@ -0,0 +1,368 @@
|
||||
//! \file HuffmanDecoder.cs
|
||||
//! \date 2024 Aug 2
|
||||
//! \brief Custom Huffman decoder for DXA archives.
|
||||
//
|
||||
// Copyright (C) 2017 by morkt - GetBits function
|
||||
// Copyright (C) 2024 by MrSoup678
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
//Original file is Huffman.cpp. Creator: 山田 巧 Date: 2018 Dec 16
|
||||
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.DxLib
|
||||
{
|
||||
internal class DXA8HuffmanNode
|
||||
{
|
||||
public UInt64 Weight;
|
||||
public int bitNumber;
|
||||
public byte[] bitArray; //32 bytes here
|
||||
public int Index;
|
||||
|
||||
public int ParentNode; // index of parent node.
|
||||
public int[] ChildNode; //two children nodes, -1 if not existent.
|
||||
|
||||
internal DXA8HuffmanNode()
|
||||
{
|
||||
bitArray = new byte[32];
|
||||
ChildNode = new int[2];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal sealed class HuffmanDecoder
|
||||
{
|
||||
byte[] m_input;
|
||||
byte[] m_output;
|
||||
|
||||
int m_src;
|
||||
byte m_bits;
|
||||
int m_bit_count;
|
||||
|
||||
ulong m_readBytes;
|
||||
byte m_readBits;
|
||||
|
||||
|
||||
DXA8HuffmanNode[] nodes; //256+255 nodes
|
||||
|
||||
ulong originalSize;
|
||||
ulong compressedSize;
|
||||
ulong headerSize;
|
||||
|
||||
ulong srcSize;
|
||||
|
||||
//ushort token = 256;
|
||||
|
||||
public HuffmanDecoder (byte[] src,ulong srcSize)
|
||||
{
|
||||
m_input = src;
|
||||
m_output = null;
|
||||
|
||||
this.srcSize = srcSize;
|
||||
m_src = 0;
|
||||
m_bit_count = 0;
|
||||
m_readBytes = 0;
|
||||
m_readBits = 0;
|
||||
originalSize = compressedSize = headerSize = 0;
|
||||
ushort[] weights = new ushort[256];
|
||||
nodes = new DXA8HuffmanNode[256+255]; //256 data nodes, then 255 hierarchy nodes.
|
||||
for (int i = 0; i < nodes.Length; i++)
|
||||
{
|
||||
nodes[i] = new DXA8HuffmanNode();
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] Unpack ()
|
||||
{
|
||||
|
||||
for (int i=0; i<nodes.Length; i++)
|
||||
{
|
||||
nodes[i].ParentNode = -1;
|
||||
nodes[i].ChildNode[0] = -1;
|
||||
nodes[i].ChildNode[1] = -1;
|
||||
}
|
||||
SetupWeights();
|
||||
//check if compressedSize and src size match.
|
||||
if (srcSize!=(compressedSize+headerSize))
|
||||
{
|
||||
throw new FileSizeException(String.Format("Supplied srcSize does not match with compressedSize+headerSize. Expected {0} got {1}",compressedSize+headerSize,srcSize));
|
||||
}
|
||||
m_output = new byte[originalSize];
|
||||
CreateTree();
|
||||
PopulateDataNodes();
|
||||
DoUnpack();
|
||||
return m_output;
|
||||
}
|
||||
|
||||
private void DoUnpack()
|
||||
{
|
||||
var targetSize = originalSize;
|
||||
byte[] compressedData = new byte[compressedSize];
|
||||
Array.Copy(m_input, (long)headerSize, compressedData, 0, (long)(compressedSize));
|
||||
|
||||
int PressBitCounter=0, PressBitData=0, Index=0, NodeIndex=0;
|
||||
int PressSizeCounter = 0;
|
||||
ulong DestSizeCounter = 0;
|
||||
int[] NodeIndexTable=new int[512];
|
||||
{
|
||||
ushort[] bitMask = new ushort[9];
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
bitMask[i] = (ushort)((1<<i+1) - 1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 512; i++)
|
||||
{
|
||||
NodeIndexTable[i] = -1;
|
||||
|
||||
for (int j = 0; j < 256 + 254; j++)
|
||||
{
|
||||
ushort BitArrayFirstBatch;
|
||||
if (nodes[j].bitNumber > 9) continue;
|
||||
|
||||
BitArrayFirstBatch = (ushort)(nodes[j].bitArray[0] | (nodes[j].bitArray[1] << 8));
|
||||
|
||||
if ((i & bitMask[nodes[j].bitNumber - 1]) == (BitArrayFirstBatch & bitMask[nodes[j].bitNumber-1]))
|
||||
{
|
||||
NodeIndexTable[i] = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
PressBitData = compressedData[PressBitCounter];
|
||||
|
||||
for (DestSizeCounter = 0;DestSizeCounter < originalSize; DestSizeCounter++)
|
||||
{
|
||||
if (DestSizeCounter>= originalSize - 17)
|
||||
{
|
||||
NodeIndex = 510;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PressBitCounter==8)
|
||||
{
|
||||
PressSizeCounter++;
|
||||
PressBitData = compressedData[PressSizeCounter];
|
||||
PressBitCounter = 0;
|
||||
}
|
||||
|
||||
PressBitData = (PressBitData | (compressedData[PressSizeCounter+1]<<(8-PressBitCounter))) & 0x1ff;
|
||||
NodeIndex = NodeIndexTable[PressBitData];
|
||||
PressBitCounter += nodes[NodeIndex].bitNumber;
|
||||
if (PressBitCounter >= 16)
|
||||
{
|
||||
PressSizeCounter += 2;
|
||||
PressBitCounter -= 16;
|
||||
PressBitData = compressedData[PressSizeCounter] >> PressBitCounter;
|
||||
}
|
||||
else if (PressBitCounter >=8)
|
||||
{
|
||||
PressSizeCounter ++;
|
||||
PressBitCounter -= 8;
|
||||
PressBitData = compressedData[PressSizeCounter] >> PressBitCounter;
|
||||
}
|
||||
else
|
||||
{
|
||||
PressBitData >>= nodes[NodeIndex].bitNumber;
|
||||
}
|
||||
}
|
||||
|
||||
while (NodeIndex > 255)
|
||||
{
|
||||
if (PressBitCounter == 8)
|
||||
{
|
||||
PressSizeCounter++;
|
||||
PressBitData = compressedData[PressSizeCounter];
|
||||
PressBitCounter = 0;
|
||||
}
|
||||
Index = PressBitData & 1;
|
||||
PressBitData >>= 1;
|
||||
PressBitCounter++;
|
||||
NodeIndex = nodes[NodeIndex].ChildNode[Index];
|
||||
}
|
||||
m_output[DestSizeCounter] = (byte)NodeIndex;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void PopulateDataNodes()
|
||||
{
|
||||
//The data which is populated is path from root to target node in bits.
|
||||
byte[] ScratchSpace = new byte[32];
|
||||
int TempBitIndex, TempBitCount;
|
||||
|
||||
for (int i = 0; i < 256 + 254; i++) //root node is excluded.
|
||||
{
|
||||
nodes[i].bitNumber = 0;
|
||||
TempBitIndex = 0;
|
||||
TempBitCount = 0;
|
||||
ScratchSpace[TempBitIndex] = 0;
|
||||
|
||||
for (int j = i; nodes[j].ParentNode!=-1;j = nodes[j].ParentNode)
|
||||
{
|
||||
if (TempBitCount == 8)
|
||||
{
|
||||
TempBitCount = 0;
|
||||
TempBitIndex++;
|
||||
ScratchSpace[TempBitIndex] = 0;
|
||||
}
|
||||
ScratchSpace[TempBitIndex] <<= 1;
|
||||
ScratchSpace[TempBitIndex] |= (byte)nodes[j].Index;
|
||||
TempBitCount++;
|
||||
nodes[i].bitNumber++;
|
||||
|
||||
}
|
||||
//path is now backwards (target to root). Populate BitPath from root to target.
|
||||
int BitIndex=0, BitCount=0;
|
||||
nodes[i].bitArray[BitIndex] = 0;
|
||||
while (TempBitIndex >= 0)
|
||||
{
|
||||
if (BitCount == 8)
|
||||
{
|
||||
BitCount = 0;
|
||||
BitIndex++;
|
||||
nodes[i].bitArray[BitIndex] = 0;
|
||||
}
|
||||
nodes[i].bitArray[BitIndex] |= (byte)((ScratchSpace[TempBitIndex] & 1) << BitCount);
|
||||
ScratchSpace[TempBitIndex] >>= 1;
|
||||
TempBitCount--;
|
||||
if (TempBitCount == 0)
|
||||
{
|
||||
TempBitIndex--;
|
||||
TempBitCount = 8;
|
||||
}
|
||||
BitCount++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void SetupWeights()
|
||||
{
|
||||
int sizeA, sizeB;
|
||||
byte BitNum;
|
||||
byte Minus;
|
||||
ushort SaveData;
|
||||
ushort[] weights = new ushort[256];
|
||||
sizeA = (int)GetBits(6) + 1;
|
||||
originalSize = GetBits(sizeA);
|
||||
sizeB = (int)GetBits(6)+1;
|
||||
compressedSize = GetBits(sizeB);
|
||||
|
||||
BitNum = (byte)(((int)GetBits(3) + 1) * 2);
|
||||
Minus = (byte)GetBits(1);
|
||||
SaveData = (ushort)GetBits(BitNum);
|
||||
weights[0] = SaveData;
|
||||
for (int i = 1; i < 256; i++)
|
||||
{
|
||||
BitNum = (byte)(((int)GetBits(3) + 1) * 2);
|
||||
Minus = (byte)GetBits(1);
|
||||
SaveData = (ushort)GetBits(BitNum);
|
||||
weights[i] = (ushort)(Minus == 1 ? weights[i - 1] - SaveData : weights[i - 1] + SaveData);
|
||||
}
|
||||
headerSize = GetReadBytes();
|
||||
for (int i = 0;i < 256; i++)
|
||||
{
|
||||
nodes[i].Weight = weights[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CreateTree()
|
||||
{
|
||||
int NodeNum=256, DataNum=256;
|
||||
|
||||
while (DataNum > 1)
|
||||
{
|
||||
int MinNode1 = -1;
|
||||
int MinNode2 = -1;
|
||||
int NodeIndex = 0;
|
||||
|
||||
|
||||
for (int i = 0; i < DataNum; NodeIndex++) {
|
||||
//don't do anything if we already have a parent set.
|
||||
if (nodes[NodeIndex].ParentNode != -1) continue;
|
||||
i++;
|
||||
//we need to get the two lowest numbers for parenting.
|
||||
if (MinNode1 == -1 || nodes[MinNode1].Weight > nodes[NodeIndex].Weight)
|
||||
{
|
||||
{
|
||||
MinNode2 = MinNode1;
|
||||
MinNode1 = NodeIndex;
|
||||
}
|
||||
} else if (MinNode2 == -1 || nodes[MinNode2].Weight > nodes[NodeIndex].Weight)
|
||||
{
|
||||
MinNode2 = NodeIndex;
|
||||
}
|
||||
}
|
||||
nodes[NodeNum].ParentNode = -1;
|
||||
nodes[NodeNum].Weight = nodes[MinNode1].Weight + nodes[MinNode2].Weight;
|
||||
nodes[NodeNum].ChildNode[0] = MinNode1;
|
||||
nodes[NodeNum].ChildNode[1] = MinNode2;
|
||||
nodes[MinNode1].Index = 0;
|
||||
nodes[MinNode2].Index = 1;
|
||||
nodes[MinNode1].ParentNode = NodeNum;
|
||||
nodes[MinNode2].ParentNode = NodeNum;
|
||||
|
||||
NodeNum++;
|
||||
DataNum--;
|
||||
}
|
||||
}
|
||||
|
||||
ulong GetBits (int count)
|
||||
{
|
||||
ulong bits = 0;
|
||||
for (int i = 0; i < count;i++)
|
||||
{
|
||||
if (0 == m_bit_count)
|
||||
{
|
||||
m_bits = m_input[m_src];
|
||||
m_src++;
|
||||
m_bit_count = 8;
|
||||
}
|
||||
//bits are read backwards.
|
||||
bits |= ((ulong)((m_bits >> (7 - m_readBits)) & 1)) <<(count-1-i);
|
||||
--m_bit_count;
|
||||
m_readBits++;
|
||||
if (m_readBits ==8)
|
||||
{
|
||||
m_readBits = 0;
|
||||
m_readBytes++;
|
||||
}
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
ulong GetReadBytes()
|
||||
{
|
||||
return m_readBytes + (m_readBits != 0 ? 1ul : 0ul);
|
||||
}
|
||||
}
|
||||
}
|
19
ArcFormats/DxLib/WidgetDXA.xaml
Normal file
19
ArcFormats/DxLib/WidgetDXA.xaml
Normal file
@ -0,0 +1,19 @@
|
||||
<StackPanel x:Class="GameRes.Formats.GUI.WidgetDXA"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:GameRes.Formats.DxLib"
|
||||
mc:Ignorable="d"
|
||||
|
||||
xmlns:s="clr-namespace:GameRes.Formats.Strings"
|
||||
xmlns:p="clr-namespace:GameRes.Formats.Properties"
|
||||
xmlns:dxa="clr-namespace:GameRes.Formats.DxLib">
|
||||
<Label Content="{x:Static s:arcStrings.ArcTitleOrPassword}" HorizontalAlignment="Left"/>
|
||||
<ComboBox Name="Title" Width="200" HorizontalAlignment="Left"
|
||||
ItemsSource="{Binding RelativeSource={RelativeSource Self}, Path=DataContext}"
|
||||
SelectedValue="{Binding ElementName=Password, Path=Text, Mode=TwoWay}" SelectedValuePath="Value"
|
||||
DisplayMemberPath="Key"/>
|
||||
<TextBox x:Name="Password" Width="200" HorizontalAlignment="Left" Margin="0,5,0,0"
|
||||
Text="{Binding Source={x:Static p:Settings.Default}, Path=DXAPassword, Mode=OneWay}"/>
|
||||
</StackPanel>
|
29
ArcFormats/DxLib/WidgetDXA.xaml.cs
Normal file
29
ArcFormats/DxLib/WidgetDXA.xaml.cs
Normal file
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Logika interakcji dla klasy WidgetDXA.xaml
|
||||
/// </summary>
|
||||
public partial class WidgetDXA : StackPanel
|
||||
{
|
||||
public WidgetDXA()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
14
ArcFormats/Properties/Settings.Designer.cs
generated
14
ArcFormats/Properties/Settings.Designer.cs
generated
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -200,5 +200,8 @@
|
||||
<Setting Name="AFAEncodingCP" Type="System.Int32" Scope="User">
|
||||
<Value Profile="(Default)">932</Value>
|
||||
</Setting>
|
||||
<Setting Name="DXAPassword" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)">DXARC</Value>
|
||||
</Setting>
|
||||
</Settings>
|
||||
</SettingsFile>
|
@ -202,6 +202,9 @@
|
||||
<setting name="AFAEncodingCP" serializeAs="String">
|
||||
<value>932</value>
|
||||
</setting>
|
||||
<setting name="DXAPassword" serializeAs="String">
|
||||
<value>DXARC</value>
|
||||
</setting>
|
||||
</GameRes.Formats.Properties.Settings>
|
||||
</userSettings>
|
||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /></startup>
|
||||
|
@ -33,7 +33,7 @@ namespace GameRes
|
||||
{
|
||||
public static class Encodings
|
||||
{
|
||||
public static readonly Encoding cp932 = Encoding.GetEncoding (932);
|
||||
public static readonly Encoding cp932 = Encoding.GetEncoding(932);
|
||||
|
||||
public static Encoding WithFatalFallback (this Encoding enc)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user