Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ArcFormats/ArcFormats.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@
<Compile Include="aNCHOR\Blowfish.cs" />
<Compile Include="CatSystem\ArcBinV1.cs" />
<Compile Include="CatSystem\ArcPidaV1.cs" />
<Compile Include="Entergram\ArcPacV1.cs" />
<Compile Include="Entergram\QuickLZ.cs" />
<Compile Include="FrontierWorks\ArcPCARC.cs" />
<Compile Include="Artemis\ImageNekoPNG.cs" />
<Compile Include="CsWare\AudioWAV.cs" />
Expand Down
153 changes: 153 additions & 0 deletions ArcFormats/Entergram/ArcPacV1.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Text;

namespace GameRes.Formats.Entergram
{
[Export(typeof(ArchiveFormat))]
public class PacOpenerV1 : ArchiveFormat
{
public override string Tag => "PacV1/Entergram";
public override string Description => "Entergram Unity resource archive";
public override uint Signature => 0x20434150; // PAC/x20
public override bool IsHierarchic => true;
public override bool CanWrite => false;

private static readonly byte[] smHeader = new byte[]
{
0x50, 0x41, 0x43, 0x20, 0x56, 0x45, 0x52, 0x2D, 0x31, 0x2E, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00
};

public override ArcFile TryOpen(ArcView file)
{
if (!file.View.BytesEqual(0L, smHeader))
{
return null;
}

List<Entry> entries = this.ParseEntry(file);
if(entries == null)
{
return null;
}

return new ArcFile(file, this, entries);
}

public override Stream OpenEntry(ArcFile arc, Entry entry)
{
if(!(entry is PackedEntry e))
{
return base.OpenEntry(arc, entry);
}

if (e.IsPacked)
{
using(ArcViewStream s = arc.File.CreateStream(e.Offset, e.Size))
{
byte[] data = s.ReadBytes(int.MaxValue);
data = QLZCompressor.Decompress(data);
return new MemoryStream(data, false);
}
}
else
{
return base.OpenEntry(arc, entry);
}
}

private List<Entry> ParseEntry(ArcView file)
{
bool compressed = Path.GetFileNameWithoutExtension(file.Name).EndsWith("_c");
using(ArcViewStream stream = file.CreateStream(smHeader.LongLength))
{
List<PackedEntry> entries = new List<PackedEntry>();

// 8 Bytes Entry Mode (Default)
{
stream.Position = 0L;
while (stream.Position < stream.Length)
{
string name = ReadString(stream);

long offset = stream.ReadInt64() + 0x10L;
long length = stream.ReadInt64();
if (!CheckEntry(offset, length, file.MaxOffset))
{
entries.Clear();
break;
}

PackedEntry entry = Create<PackedEntry>(name);
entry.Offset = offset;
entry.Size = (uint)length;
entry.IsPacked = compressed;
entries.Add(entry);

stream.Seek(length, SeekOrigin.Current);
}
}

// 10 Bytes Entry Mode
if (!entries.Any())
{
stream.Position = 0L;
while (stream.Position < stream.Length)
{
string name = ReadString(stream);

long offset = stream.ReadInt64() + 0x14L;
stream.Position += 2L;
long length = stream.ReadInt64();
stream.Position += 2L;
if (!CheckEntry(offset, length, file.MaxOffset))
{
entries.Clear();
break;
}

PackedEntry entry = Create<PackedEntry>(name);
entry.Offset = offset;
entry.Size = (uint)length;
entry.IsPacked = compressed;
entries.Add(entry);

stream.Seek(length, SeekOrigin.Current);
}
}

return entries.Cast<Entry>().ToList();
}
}

private static string ReadString(ArcViewStream stream)
{
byte[] buf = new byte[0x20];
if (stream.Read(buf, 0, buf.Length) != buf.Length)
{
return string.Empty;
}

int len = Array.IndexOf<byte>(buf, 0);
if (len <= 0)
{
return string.Empty;
}

return Encoding.UTF8.GetString(buf, 0, len);
}

private static bool CheckEntry(long offset, long length, long max)
{
return offset >= 0L &&
length >= 0L &&
offset < max &&
length <= max &&
length <= uint.MaxValue &&
offset <= max - length;
}
}
}
170 changes: 170 additions & 0 deletions ArcFormats/Entergram/QuickLZ.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
using System;

namespace GameRes.Formats.Entergram
{
internal static class QLZCompressor
{
public struct QLZHeader
{
public QLZHeader(byte[] src)
{
byte b = src[0];
this.Compressible = (b & CONTAINER_Compressible) == CONTAINER_Compressible;
if (this.Compressible)
{
b -= CONTAINER_Compressible;
}
if (b != STATIC_HEADER_FIRSTBYTE)
{
throw new Exception("Invalid QLZ Header: " + b.ToString());
}
this.CompressedSize = BitConverter.ToInt32(src, 1);
this.RawSize = BitConverter.ToInt32(src, 5);
}

public const int HEADER_LENGTH = 9;
public const byte STATIC_HEADER_FIRSTBYTE = 0x5E;
public const byte CONTAINER_Compressible = 1;
public const int Level = 3;

public bool Compressible;
public int CompressedSize;
public int RawSize;
}

public static byte[] Decompress(byte[] compressed)
{
QLZCompressor.QLZHeader qlzheader = new QLZCompressor.QLZHeader(compressed);
if (qlzheader.RawSize == 0)
{
return new byte[0];
}
byte[] array = new byte[qlzheader.RawSize];
if (!qlzheader.Compressible)
{
Array.Copy(compressed, QLZHeader.HEADER_LENGTH, array, 0, qlzheader.RawSize);
}
else
{
QLZCompressor.Decompress_Unsafe(compressed, array, qlzheader.RawSize);
}
return array;
}

private unsafe static void Decompress_Unsafe(byte[] compressed, byte[] decompressed, int rawSize)
{
if (rawSize < decompressed.Length)
{
throw new Exception("Decompressed Array is not enough size");
}

fixed (byte* ptr = compressed)
{
fixed (byte* ptr2 = decompressed)
{
int src = QLZHeader.HEADER_LENGTH;
int dst = 0;
uint cword_val = 1U;
int last_matchstart = rawSize - UNCONDITIONAL_MATCHLEN - UNCOMPRESSED_END - 1;
uint fetch = 0U;
for (; ; )
{
if (cword_val == 1U)
{
cword_val = ReadUInt32(ptr + src);
src += 4;
if (dst <= last_matchstart)
{
fetch = ReadUInt32(ptr + src);
}
}
if ((cword_val & 1U) == 1U)
{
cword_val >>= 1;
uint offset;
uint matchlen;
if ((fetch & 3U) == 0U)
{
offset = (fetch & 0xFFU) >> 2;
matchlen = 3U;
src++;
}
else if ((fetch & 2U) == 0U)
{
offset = (fetch & 0xFFFFU) >> 2;
matchlen = 3U;
src += 2;
}
else if ((fetch & 1U) == 0U)
{
offset = (fetch & 0xFFFFU) >> 6;
matchlen = ((fetch >> 2) & 0xFU) + 3U;
src += 2;
}
else if ((fetch & 0x7FU) != 3U)
{
offset = (fetch >> 7) & 0x1FFFFU;
matchlen = ((fetch >> 2) & 0x1FU) + 2U;
src += 3;
}
else
{
offset = fetch >> 0xF;
matchlen = ((fetch >> 7) & 0xFFU) + 3U;
src += 4;
}
uint num7 = (uint)((long)dst - (long)((ulong)offset));
ptr2[dst] = ptr2[num7];
(ptr2 + dst)[1] = (ptr2 + num7)[1];
(ptr2 + dst)[2] = (ptr2 + num7)[2];
int num8 = 3;
while ((long)num8 < (long)((ulong)matchlen))
{
(ptr2 + dst)[num8] = (ptr2 + num7)[num8];
num8++;
}
dst += (int)matchlen;
fetch = ReadUInt32(ptr + src);
}
else
{
if (dst > last_matchstart)
{
break;
}
ptr2[dst] = ptr[src];
dst++;
src++;
cword_val >>= 1;
fetch = (uint)((((int)fetch >> 8) & 0xFFFF) | ((int)(ptr + src)[2] << 0x10) | ((int)(ptr + src)[3] << 0x18));
}
}
while (dst <= rawSize - 1)
{
if (cword_val == 1U)
{
src += 4;
cword_val = 0x80000000U;
}
ptr2[dst] = ptr[src];
dst++;
src++;
cword_val >>= 1;
}
}
}
}

private unsafe static uint ReadUInt32(byte* p)
{
return *(uint*)p;
}

private const int HASH_VALUES = 0x1000;
private const int MINOFFSET = 2;
private const int UNCONDITIONAL_MATCHLEN = 6;
private const int UNCOMPRESSED_END = 4;
private const int CWORD_LEN = 4;
private const int QLZ_POINTERS_3 = 0x10;
}
}
4 changes: 2 additions & 2 deletions GameRes/ArcStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,9 @@ public ArcViewStream (ArcView.Frame view, string name = null)
Name = name ?? "";
}

public ArcViewStream (ArcView file, long offset, uint size, string name = null)
public ArcViewStream (ArcView file, long offset, long size, string name = null)
{
m_view = new ArcView.Frame (file, offset, Math.Min (size, MaxFrameSize));
m_view = new ArcView.Frame (file, offset, (uint)Math.Min (size, MaxFrameSize));
m_start = offset;
m_size = size;
m_position = 0;
Expand Down
8 changes: 5 additions & 3 deletions GameRes/ArcView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,11 @@ public ArcViewStream CreateStream ()
public ArcViewStream CreateStream (long offset)
{
var size = this.MaxOffset - offset;
if (size > uint.MaxValue)
throw new ArgumentOutOfRangeException ("Too large memory mapped stream");
return new ArcViewStream (this, offset, (uint)size);
if (size < 0)
{
throw new ArgumentOutOfRangeException(nameof(offset), "Greater than archive size");
}
return new ArcViewStream (this, offset, size);
}

public ArcViewStream CreateStream (long offset, uint size, string name = null)
Expand Down
Loading