Merge pull request #39 from SlawekNowy/master

Add support for DXArchive8.
This commit is contained in:
Crsky 2024-09-06 10:48:17 +08:00 committed by GitHub
commit 7b3bb7e730
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 834 additions and 40 deletions

View File

@ -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>

View File

@ -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);
}
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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.");
}
}
}

View 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);
}
}
}

View 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>

View 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();
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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)
{