Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compressed Gump read support #131

Merged
merged 5 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from 2 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
114 changes: 90 additions & 24 deletions Ultima/FileIndex.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,11 @@ public FileIndex(string idxFile, string mulFile, string uopFile, int length, int
throw new ArgumentException("Bad UOP file.");
}

br.ReadInt64(); // version + signature
var version = br.ReadUInt32(); // version
var signature = br.ReadUInt32(); // signature
long nextBlock = br.ReadInt64();
br.ReadInt32(); // block capacity
_ = br.ReadInt32(); // TODO: check if we need value from here
var block_size = br.ReadUInt32(); // block capacity
var count = br.ReadInt32(); // TODO: check if we need value from here

if (idxLength > 0)
{
Expand Down Expand Up @@ -159,11 +160,9 @@ public FileIndex(string idxFile, string mulFile, string uopFile, int length, int
int compressedLength = br.ReadInt32();
int decompressedLength = br.ReadInt32();
ulong hash = br.ReadUInt64();
br.ReadUInt32(); // Adler32
uint data_hash = br.ReadUInt32();
short flag = br.ReadInt16();

int entryLength = flag == 1 ? compressedLength : decompressedLength;

if (offset == 0)
{
continue;
Expand All @@ -179,29 +178,37 @@ public FileIndex(string idxFile, string mulFile, string uopFile, int length, int
throw new IndexOutOfRangeException("hashes dictionary and files collection have different count of entries!");
}

Index[idx].Lookup = (int)(offset + headerLength);
Index[idx].Length = entryLength;

if (!hasExtra)
offset += headerLength;

if (hasExtra && flag != 3)
{
continue;
}
long curPos = br.BaseStream.Position;

long curPos = br.BaseStream.Position;
br.BaseStream.Seek(offset, SeekOrigin.Begin);

br.BaseStream.Seek(offset + headerLength, SeekOrigin.Begin);
var extra1 = br.ReadInt32();
var extra2 = br.ReadInt32();
Index[idx].Lookup = (int)(offset + 8);
Index[idx].Length = compressedLength - 8;
Index[idx].DecompressedLength = decompressedLength;
Index[idx].Flag = flag;

byte[] extra = br.ReadBytes(8);
// changed from int b = extra1 << 16 | extra2;
// int cast removes compiler warning
Index[idx].Extra = extra1 << 16 | (int)extra2;
Index[idx].Extra1 = extra1;
Index[idx].Extra2 = extra2;

var extra1 = (short)((extra[3] << 24) | (extra[2] << 16) | (extra[1] << 8) | extra[0]);
var extra2 = (short)((extra[7] << 24) | (extra[6] << 16) | (extra[5] << 8) | extra[4]);

Index[idx].Lookup += 8;
// changed from int b = extra1 << 16 | extra2;
// int cast removes compiler warning
Index[idx].Extra = extra1 << 16 | (int)extra2;

br.BaseStream.Seek(curPos, SeekOrigin.Begin);
br.BaseStream.Seek(curPos, SeekOrigin.Begin);
}
else
{
Index[idx].Lookup = (int)(offset);
Index[idx].Length = compressedLength;
Index[idx].DecompressedLength = decompressedLength;
Index[idx].Flag = flag;
Index[idx].Extra = 0x0FFFFFFF; //we cant read it right now, but -1 and 0 makes this entry invalid
}
}
}
while (br.BaseStream.Seek(nextBlock, SeekOrigin.Begin) != 0);
Expand Down Expand Up @@ -397,6 +404,59 @@ public Stream Seek(int index, out int length, out int extra, out bool patched)

patched = false;

_stream.Seek(e.Lookup, SeekOrigin.Begin);
return _stream;
}
public Stream Seek(int index, out Entry3D entry)
{
if (index < 0 || index >= Index.Length)
{
entry = Entry3D.Invalid;
return null;
}

Entry3D e = Index[index];

if (e.Lookup < 0)
{
entry = Entry3D.Invalid;

return null;
}

entry = e;


if ((e.Length & (1 << 31)) != 0)
{
Verdata.Seek(e.Lookup);
return Verdata.Stream;
}

if (e.Length < 0)
{
entry = Entry3D.Invalid;
return null;
}

if ((_stream?.CanRead != true) || (!_stream.CanSeek))
{
_stream = _mulPath == null ? null : new FileStream(_mulPath, FileMode.Open, FileAccess.Read, FileShare.Read);
}

if (_stream == null)
{
entry = Entry3D.Invalid;
return null;
}

if (_stream.Length < e.Lookup)
{
entry = Entry3D.Invalid;
return null;
}


_stream.Seek(e.Lookup, SeekOrigin.Begin);
return _stream;
}
Expand Down Expand Up @@ -465,6 +525,12 @@ public struct Entry3D
{
public int Lookup;
public int Length;
public int DecompressedLength;
public int Extra;
public int Extra1;
public int Extra2;
public int Flag;

public static Entry3D Invalid { get => new Entry3D(); }
}
}
122 changes: 80 additions & 42 deletions Ultima/Gumps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.IO.Compression;
using System.Reflection.Metadata.Ecma335;
AsYlum- marked this conversation as resolved.
Show resolved Hide resolved
using Ultima.Helpers;

namespace Ultima
{
Expand Down Expand Up @@ -154,6 +157,7 @@ public static byte[] GetRawGump(int index, out int width, out int height)
stream.Read(buffer, 0, length);
stream.Close();

buffer = BwtDecompress.Decompress(buffer);
return buffer;
}

Expand Down Expand Up @@ -361,83 +365,117 @@ public static unsafe Bitmap GetGump(int index, out bool patched)
return _cache[index];
}

Stream stream = _fileIndex.Seek(index, out int length, out int extra, out patched);
Stream stream = _fileIndex.Seek(index, out Entry3D entry);
if (stream == null)
{
return null;
}

if (extra == -1)
if (entry.Extra1 == -1)
{
stream.Close();
return null;
}

if (patched)
{
_patched[index] = true;
}

int width = (extra >> 16) & 0xFFFF;
int height = extra & 0xFFFF;

if (width <= 0 || height <= 0)
if (_streamBuffer == null || _streamBuffer.Length < entry.Length)
{
return null;
_streamBuffer = new byte[entry.Length];
}
long pos = stream.Position;
stream.Read(_streamBuffer, 0, entry.Length);

uint width = (uint)entry.Extra1;
uint height = (uint)entry.Extra2;

//Compressed UOPs
AsYlum- marked this conversation as resolved.
Show resolved Hide resolved
if (entry.Flag >= 1)
{
byte[] dbuf = new byte[entry.DecompressedLength];
AsYlum- marked this conversation as resolved.
Show resolved Hide resolved
var result = ZLib.Decompress(_streamBuffer, dbuf);
if (result != ZLib.ZLibError.Ok)
{
return default;
}

var bmp = new Bitmap(width, height, PixelFormat.Format16bppArgb1555);
BitmapData bd = bmp.LockBits(
new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format16bppArgb1555);
if (entry.Flag == 3)
{
_streamBuffer = BwtDecompress.Decompress(dbuf);
}
using (BinaryReader reader = new BinaryReader(new MemoryStream(_streamBuffer)))
{
byte[] extra = reader.ReadBytes(8);
width = (uint)((extra[3] << 24) | (extra[2] << 16) | (extra[1] << 8) | extra[0]);
height = (uint)((extra[7] << 24) | (extra[6] << 16) | (extra[5] << 8) | extra[4]);
_streamBuffer = reader.ReadBytes(_streamBuffer.Length - 8);
}

entry.Extra1 = (int)width;
entry.Extra2 = (int)height;
}

if (_streamBuffer == null || _streamBuffer.Length < length)
if (width <= 0 || height <= 0)
{
_streamBuffer = new byte[length];
return null;
}

stream.Read(_streamBuffer, 0, length);

fixed (byte* data = _streamBuffer)
try
{
var lookup = (int*)data;
var dat = (ushort*)data;
var bmp = new Bitmap((int)width, (int)height, PixelFormat.Format16bppArgb1555);
BitmapData bd = bmp.LockBits(
new Rectangle(0, 0, (int)width, (int)height), ImageLockMode.WriteOnly, PixelFormat.Format16bppArgb1555);

var line = (ushort*)bd.Scan0;
int delta = bd.Stride >> 1;
for (int y = 0; y < height; ++y, line += delta)
fixed (byte* data = _streamBuffer)
{
int count = (*lookup++ * 2);

ushort* cur = line;
ushort* end = line + bd.Width;
var lookup = (int*)data;
var dat = (ushort*)data;

while (cur < end)
var line = (ushort*)bd.Scan0;
int delta = bd.Stride >> 1;
for (int y = 0; y < height; ++y, line += delta)
{
ushort color = dat[count++];
ushort* next = cur + dat[count++];
int count = (*lookup++ * 2);

if (color == 0)
{
cur = next;
}
else
ushort* cur = line;
ushort* end = line + bd.Width;

while (cur < end)
{
color ^= 0x8000;
while (cur < next)
ushort color = dat[count++];
ushort* next = cur + dat[count++];

if (color == 0)
{
*cur++ = color;
cur = next;
}
else
{
color ^= 0x8000;
while (cur < next)
{
*cur++ = color;
}
}
}
}
}
}

bmp.UnlockBits(bd);
if (Files.CacheData)
bmp.UnlockBits(bd);

if (Files.CacheData)
{
return _cache[index] = bmp;
}

return bmp;
}
catch (Exception ex)
{
return _cache[index] = bmp;
return null;
}

return bmp;
}

public static unsafe void Save(string path)
Expand Down
Loading