From f7925df2e2d495428f7228eadde42952fa604230 Mon Sep 17 00:00:00 2001 From: Krubster Date: Fri, 10 Jan 2025 21:15:37 +0300 Subject: [PATCH 1/5] - added compressed gump support --- Ultima/FileIndex.cs | 78 +++++++++++++++++++++++++++++++++++++++++---- Ultima/Gumps.cs | 51 +++++++++++++++++++++-------- 2 files changed, 109 insertions(+), 20 deletions(-) diff --git a/Ultima/FileIndex.cs b/Ultima/FileIndex.cs index c4c5c286..296b92ab 100644 --- a/Ultima/FileIndex.cs +++ b/Ultima/FileIndex.cs @@ -159,10 +159,10 @@ 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; + int entryLength = flag >= 1 ? compressedLength : decompressedLength; if (offset == 0) { @@ -178,28 +178,33 @@ 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); + offset += headerLength; + Index[idx].Lookup = (int)(offset); Index[idx].Length = entryLength; + Index[idx].DecompressedLength = decompressedLength; + Index[idx].Flag = flag; - if (!hasExtra) + if (!hasExtra || flag >= 1) { continue; } long curPos = br.BaseStream.Position; - br.BaseStream.Seek(offset + headerLength, SeekOrigin.Begin); + br.BaseStream.Seek(offset, SeekOrigin.Begin); byte[] extra = br.ReadBytes(8); 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; + Index[idx].Length -= 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; br.BaseStream.Seek(curPos, SeekOrigin.Begin); } @@ -397,6 +402,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; } @@ -465,6 +523,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(); } } } \ No newline at end of file diff --git a/Ultima/Gumps.cs b/Ultima/Gumps.cs index b9403fb8..c8f0d0ec 100644 --- a/Ultima/Gumps.cs +++ b/Ultima/Gumps.cs @@ -3,6 +3,8 @@ using System.Drawing; using System.Drawing.Imaging; using System.IO; +using System.IO.Compression; +using Ultima.Helpers; namespace Ultima { @@ -154,6 +156,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; } @@ -361,40 +364,62 @@ 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); - var bmp = new Bitmap(width, height, PixelFormat.Format16bppArgb1555); - BitmapData bd = bmp.LockBits( - new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format16bppArgb1555); + uint width = (uint)entry.Extra1; + uint height = (uint)entry.Extra2; + if (entry.Flag >= 1) + { + stream.Position = pos; + _streamBuffer = new byte[entry.DecompressedLength]; + ZLibStream zlibStream = new ZLibStream(stream, CompressionMode.Decompress, true); + zlibStream.Read(_streamBuffer, 0, _streamBuffer.Length); - if (_streamBuffer == null || _streamBuffer.Length < length) + if (entry.Flag == 3) + { + _streamBuffer = BwtDecompress.Decompress(_streamBuffer); + } + 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]); + } + + entry.Extra1 = (int)width; + entry.Extra2 = (int)height; + } + + if (width <= 0 || height <= 0) { - _streamBuffer = new byte[length]; + return null; } - stream.Read(_streamBuffer, 0, length); + 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); fixed (byte* data = _streamBuffer) { From 3f35c25d5cd781100b55524b61b2d732952d9335 Mon Sep 17 00:00:00 2001 From: Krubster Date: Sat, 18 Jan 2025 13:42:12 +0300 Subject: [PATCH 2/5] Compressed gump support fixes --- Ultima/FileIndex.cs | 58 ++++---- Ultima/Gumps.cs | 89 +++++++----- Ultima/Helpers/ZLib.cs | 137 ++++++++++++++++++ Ultima/zlib.dll | Bin 0 -> 74240 bytes .../UserControls/GumpControl.cs | 2 +- 5 files changed, 219 insertions(+), 67 deletions(-) create mode 100644 Ultima/Helpers/ZLib.cs create mode 100644 Ultima/zlib.dll diff --git a/Ultima/FileIndex.cs b/Ultima/FileIndex.cs index 296b92ab..8917651a 100644 --- a/Ultima/FileIndex.cs +++ b/Ultima/FileIndex.cs @@ -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) { @@ -162,8 +163,6 @@ public FileIndex(string idxFile, string mulFile, string uopFile, int length, int uint data_hash = br.ReadUInt32(); short flag = br.ReadInt16(); - int entryLength = flag >= 1 ? compressedLength : decompressedLength; - if (offset == 0) { continue; @@ -178,35 +177,38 @@ public FileIndex(string idxFile, string mulFile, string uopFile, int length, int { throw new IndexOutOfRangeException("hashes dictionary and files collection have different count of entries!"); } - offset += headerLength; - Index[idx].Lookup = (int)(offset); - Index[idx].Length = entryLength; - Index[idx].DecompressedLength = decompressedLength; - Index[idx].Flag = flag; - if (!hasExtra || flag >= 1) + 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, 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; - Index[idx].Length -= 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; - - 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); diff --git a/Ultima/Gumps.cs b/Ultima/Gumps.cs index c8f0d0ec..2b4f3094 100644 --- a/Ultima/Gumps.cs +++ b/Ultima/Gumps.cs @@ -4,6 +4,7 @@ using System.Drawing.Imaging; using System.IO; using System.IO.Compression; +using System.Reflection.Metadata.Ecma335; using Ultima.Helpers; namespace Ultima @@ -390,24 +391,29 @@ public static unsafe Bitmap GetGump(int index, out bool patched) uint width = (uint)entry.Extra1; uint height = (uint)entry.Extra2; + + //Compressed UOPs if (entry.Flag >= 1) { - stream.Position = pos; - _streamBuffer = new byte[entry.DecompressedLength]; - ZLibStream zlibStream = new ZLibStream(stream, CompressionMode.Decompress, true); - zlibStream.Read(_streamBuffer, 0, _streamBuffer.Length); + byte[] dbuf = new byte[entry.DecompressedLength]; + var result = ZLib.Decompress(_streamBuffer, dbuf); + if (result != ZLib.ZLibError.Ok) + { + return default; + } if (entry.Flag == 3) { - _streamBuffer = BwtDecompress.Decompress(_streamBuffer); + _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; } @@ -416,53 +422,60 @@ public static unsafe Bitmap GetGump(int index, out bool patched) { return null; } - - 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); - - 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); + var lookup = (int*)data; + var dat = (ushort*)data; - ushort* cur = line; - ushort* end = line + bd.Width; - - 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 = next; + } + else { - *cur++ = color; + 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) diff --git a/Ultima/Helpers/ZLib.cs b/Ultima/Helpers/ZLib.cs new file mode 100644 index 00000000..6ba9701e --- /dev/null +++ b/Ultima/Helpers/ZLib.cs @@ -0,0 +1,137 @@ +// /*************************************************************************** +// * +// * $Author: Turley +// * +// * "THE BEER-WARE LICENSE" +// * As long as you retain this notice you can do whatever you want with +// * this stuff. If we meet some day, and you think this stuff is worth it, +// * you can buy me a beer in return. +// * +// ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Ultima.Helpers +{ + public static class ZLib + { + // thanks ServUO :) + + private static readonly ICompressor _compressor; + + static ZLib() + { + _compressor = new Compressor64(); + } + + public static ZLibError Decompress(byte[] source, int offset, byte[] dest, int length) + { + return _compressor.Decompress(dest, ref length, source, source.Length - offset); + } + + public static ZLibError Decompress(IntPtr source, int sourceLength, int offset, IntPtr dest, int length) + { + return _compressor.Decompress(dest, ref length, source, sourceLength - offset); + } + + public static unsafe ZLibError Decompress(ReadOnlySpan source, Span dest) + { + fixed (byte* srcPtr = source) + fixed (byte* destPtr = dest) + return Decompress((IntPtr)srcPtr, source.Length, 0, (IntPtr)destPtr, dest.Length); + } + + private enum ZLibQuality + { + Default = -1, + + None = 0, + + Speed = 1, + Size = 9 + } + + public enum ZLibError + { + VersionError = -6, + BufferError = -5, + MemoryError = -4, + DataError = -3, + StreamError = -2, + FileError = -1, + + Ok = 0, + + StreamEnd = 1, + NeedDictionary = 2 + } + + + private interface ICompressor + { + string Version { get; } + + ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength); + ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality); + + ZLibError Decompress(byte[] dest, ref int destLength, byte[] source, int sourceLength); + ZLibError Decompress(IntPtr dest, ref int destLength, IntPtr source, int sourceLength); + } + + + private sealed class Compressor64 : ICompressor + { + public string Version => SafeNativeMethods.zlibVersion(); + + public ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength) + { + return SafeNativeMethods.compress(dest, ref destLength, source, sourceLength); + } + + public ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality) + { + return SafeNativeMethods.compress2 + ( + dest, + ref destLength, + source, + sourceLength, + quality + ); + } + + public ZLibError Decompress(byte[] dest, ref int destLength, byte[] source, int sourceLength) + { + return SafeNativeMethods.uncompress(dest, ref destLength, source, sourceLength); + } + + public ZLibError Decompress(IntPtr dest, ref int destLength, IntPtr source, int sourceLength) + { + return SafeNativeMethods.uncompress(dest, ref destLength, source, sourceLength); + } + + private class SafeNativeMethods + { + [DllImport("zlib")] + public static extern string zlibVersion(); + + [DllImport("zlib")] + public static extern ZLibError compress(byte[] dest, ref int destLength, byte[] source, int sourceLength); + + [DllImport("zlib")] + public static extern ZLibError compress2(byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality); + + [DllImport("zlib")] + public static extern ZLibError uncompress(byte[] dest, ref int destLen, byte[] source, int sourceLen); + + [DllImport("zlib")] + public static extern ZLibError uncompress(IntPtr dest, ref int destLen, IntPtr source, int sourceLen); + } + } + } +} diff --git a/Ultima/zlib.dll b/Ultima/zlib.dll new file mode 100644 index 0000000000000000000000000000000000000000..a43d73dcc285c2c8f5c7efcd20166c64d2e92287 GIT binary patch literal 74240 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 Date: Sun, 19 Jan 2025 14:46:24 +0300 Subject: [PATCH 3/5] - removed external zlib dll use - fixed texture and map reading - reverted GumpControl --- Ultima/FileIndex.cs | 526 +++++++++++------- Ultima/Gumps.cs | 28 +- Ultima/Helpers/UopUtils.cs | 32 ++ Ultima/Helpers/ZLib.cs | 137 ----- Ultima/Skills.cs | 6 +- Ultima/Sound.cs | 6 +- .../UserControls/GumpControl.cs | 2 +- 7 files changed, 384 insertions(+), 353 deletions(-) delete mode 100644 Ultima/Helpers/ZLib.cs diff --git a/Ultima/FileIndex.cs b/Ultima/FileIndex.cs index 8917651a..942d9b92 100644 --- a/Ultima/FileIndex.cs +++ b/Ultima/FileIndex.cs @@ -8,12 +8,13 @@ namespace Ultima { public sealed class FileIndex { - public Entry3D[] Index { get; } + IFileAccessor FileAccessor { get; set; } - public long IdxLength { get; } + public long IndexLength { get => FileAccessor.IndexLength; } + public long IdxLength { get => FileAccessor.IdxLength; } + public IEntry this[int index] { get => FileAccessor[index]; set => FileAccessor[index] = (Entry6D)value; } private readonly string _mulPath; - private Stream _stream; public FileIndex(string idxFile, string mulFile, int length, int file) : this(idxFile, mulFile, null, length, file, ".dat", -1, false) @@ -23,7 +24,6 @@ public FileIndex(string idxFile, string mulFile, int length, int file) : this(id public FileIndex(string idxFile, string mulFile, string uopFile, int length, int file, string uopEntryExtension, int idxLength, bool hasExtra) { - Index = new Entry3D[length]; string idxPath = null; string uopPath = null; @@ -102,141 +102,14 @@ public FileIndex(string idxFile, string mulFile, string uopFile, int length, int */ if (_mulPath?.EndsWith(".uop") == true) { - _stream = new FileStream(_mulPath, FileMode.Open, FileAccess.Read, FileShare.Read); - - var fi = new FileInfo(_mulPath); - string uopPattern = fi.Name.Replace(fi.Extension, "").ToLowerInvariant(); - - using (var br = new BinaryReader(_stream)) - { - br.BaseStream.Seek(0, SeekOrigin.Begin); - - if (br.ReadInt32() != 0x50594D) - { - throw new ArgumentException("Bad UOP file."); - } - - var version = br.ReadUInt32(); // version - var signature = br.ReadUInt32(); // signature - long nextBlock = br.ReadInt64(); - var block_size = br.ReadUInt32(); // block capacity - var count = br.ReadInt32(); // TODO: check if we need value from here - - if (idxLength > 0) - { - IdxLength = idxLength * 12; - } - - var hashes = new Dictionary(); - - for (int i = 0; i < length; i++) - { - string entryName = $"build/{uopPattern}/{i:D8}{uopEntryExtension}"; - ulong hash = UopUtils.HashFileName(entryName); - - hashes.TryAdd(hash, i); - } - - br.BaseStream.Seek(nextBlock, SeekOrigin.Begin); - - // There are no invalid entries in .uop so we have to initialize all entries - // as invalid and then fill the valid ones - for (var i = 0; i < Index.Length; i++) - { - Index[i].Lookup = -1; - Index[i].Length = -1; - Index[i].Extra = -1; - } - - do - { - int filesCount = br.ReadInt32(); - nextBlock = br.ReadInt64(); - - for (int i = 0; i < filesCount; i++) - { - long offset = br.ReadInt64(); - int headerLength = br.ReadInt32(); - int compressedLength = br.ReadInt32(); - int decompressedLength = br.ReadInt32(); - ulong hash = br.ReadUInt64(); - uint data_hash = br.ReadUInt32(); - short flag = br.ReadInt16(); - - if (offset == 0) - { - continue; - } - - if (!hashes.TryGetValue(hash, out int idx)) - { - continue; - } - - if (idx < 0 || idx > Index.Length) - { - throw new IndexOutOfRangeException("hashes dictionary and files collection have different count of entries!"); - } - - offset += headerLength; - - if (hasExtra && flag != 3) - { - long curPos = br.BaseStream.Position; - - br.BaseStream.Seek(offset, 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; - - // 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; - - 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); - } + FileAccessor = new UOPFileAccessor(_mulPath, uopEntryExtension, length, idxLength, hasExtra); } else if ((idxPath != null) && (_mulPath != null)) { - using (var index = new FileStream(idxPath, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - _stream = new FileStream(_mulPath, FileMode.Open, FileAccess.Read, FileShare.Read); - var count = (int)(index.Length / 12); - IdxLength = index.Length; - GCHandle gc = GCHandle.Alloc(Index, GCHandleType.Pinned); - var buffer = new byte[index.Length]; - index.Read(buffer, 0, (int)index.Length); - Marshal.Copy(buffer, 0, gc.AddrOfPinnedObject(), (int)Math.Min(IdxLength, length * 12)); - gc.Free(); - for (int i = count; i < length; ++i) - { - Index[i].Lookup = -1; - Index[i].Length = -1; - Index[i].Extra = -1; - } - } + FileAccessor = new MULFileAccessor(idxPath, _mulPath, length); } else { - _stream = null; return; } @@ -252,10 +125,7 @@ public FileIndex(string idxFile, string mulFile, string uopFile, int length, int { continue; } - - Index[patch.Index].Lookup = patch.Lookup; - Index[patch.Index].Length = patch.Length | (1 << 31); - Index[patch.Index].Extra = patch.Extra; + FileAccessor.ApplyPatch(patch); } } @@ -310,23 +180,12 @@ public FileIndex(string idxFile, string mulFile, int file) if ((idxPath != null) && (_mulPath != null)) { - using (var index = new FileStream(idxPath, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - _stream = new FileStream(_mulPath, FileMode.Open, FileAccess.Read, FileShare.Read); - var count = (int)(index.Length / 12); - IdxLength = index.Length; - Index = new Entry3D[count]; - GCHandle gc = GCHandle.Alloc(Index, GCHandleType.Pinned); - var buffer = new byte[index.Length]; - index.Read(buffer, 0, (int)index.Length); - Marshal.Copy(buffer, 0, gc.AddrOfPinnedObject(), (int)index.Length); - gc.Free(); - } + FileAccessor = new MULFileAccessor(idxPath, _mulPath); } else { - _stream = null; - Index = new Entry3D[1]; + // _stream = null; + // Index = new Entry3D[1]; return; } @@ -337,27 +196,24 @@ public FileIndex(string idxFile, string mulFile, int file) foreach (var patch in Verdata.Patches) { - if (patch.File != file || patch.Index < 0 || patch.Index >= Index.Length) + if (patch.File != file || patch.Index < 0 || patch.Index >= FileAccessor.IndexLength) { continue; } - - Index[patch.Index].Lookup = patch.Lookup; - Index[patch.Index].Length = patch.Length | (1 << 31); - Index[patch.Index].Extra = patch.Extra; + FileAccessor.ApplyPatch(patch); } } public Stream Seek(int index, out int length, out int extra, out bool patched) { - if (index < 0 || index >= Index.Length) + if (index < 0 || index >= FileAccessor.IndexLength) { length = extra = 0; patched = false; return null; } - Entry3D e = Index[index]; + IEntry e = FileAccessor.GetEntry(index); if (e.Lookup < 0) { @@ -383,19 +239,19 @@ public Stream Seek(int index, out int length, out int extra, out bool patched) return null; } - if ((_stream?.CanRead != true) || (!_stream.CanSeek)) + if ((FileAccessor.Stream?.CanRead != true) || (!FileAccessor.Stream.CanSeek)) { - _stream = _mulPath == null ? null : new FileStream(_mulPath, FileMode.Open, FileAccess.Read, FileShare.Read); + FileAccessor.Stream = _mulPath == null ? null : new FileStream(_mulPath, FileMode.Open, FileAccess.Read, FileShare.Read); } - if (_stream == null) + if (FileAccessor.Stream == null) { length = extra = 0; patched = false; return null; } - if (_stream.Length < e.Lookup) + if (FileAccessor.Stream.Length < e.Lookup) { length = extra = 0; patched = false; @@ -404,29 +260,27 @@ public Stream Seek(int index, out int length, out int extra, out bool patched) patched = false; - _stream.Seek(e.Lookup, SeekOrigin.Begin); - return _stream; + FileAccessor.Stream.Seek(e.Lookup, SeekOrigin.Begin); + return FileAccessor.Stream; } - public Stream Seek(int index, out Entry3D entry) + + public Stream Seek(int index, ref IEntry entry) { - if (index < 0 || index >= Index.Length) + if (index < 0 || index >= FileAccessor.IndexLength) { - entry = Entry3D.Invalid; return null; } - Entry3D e = Index[index]; + IEntry e = FileAccessor.GetEntry(index); if (e.Lookup < 0) { - entry = Entry3D.Invalid; + entry = e.Invalid; return null; } - entry = e; - if ((e.Length & (1 << 31)) != 0) { Verdata.Seek(e.Lookup); @@ -435,42 +289,42 @@ public Stream Seek(int index, out Entry3D entry) if (e.Length < 0) { - entry = Entry3D.Invalid; + entry = e.Invalid; return null; } - if ((_stream?.CanRead != true) || (!_stream.CanSeek)) + if ((FileAccessor.Stream?.CanRead != true) || (!FileAccessor.Stream.CanSeek)) { - _stream = _mulPath == null ? null : new FileStream(_mulPath, FileMode.Open, FileAccess.Read, FileShare.Read); + FileAccessor.Stream = _mulPath == null ? null : new FileStream(_mulPath, FileMode.Open, FileAccess.Read, FileShare.Read); } - if (_stream == null) + if (FileAccessor.Stream == null) { - entry = Entry3D.Invalid; + entry = e.Invalid; return null; } - if (_stream.Length < e.Lookup) + if (FileAccessor.Stream.Length < e.Lookup) { - entry = Entry3D.Invalid; + entry = e.Invalid; return null; } - _stream.Seek(e.Lookup, SeekOrigin.Begin); - return _stream; + FileAccessor.Stream.Seek(e.Lookup, SeekOrigin.Begin); + return FileAccessor.Stream; } public bool Valid(int index, out int length, out int extra, out bool patched) { - if (index < 0 || index >= Index.Length) + if (index < 0 || index >= FileAccessor.IndexLength) { length = extra = 0; patched = false; return false; } - Entry3D e = Index[index]; + IEntry e = FileAccessor.GetEntry(index); if (e.Lookup < 0) { @@ -502,12 +356,12 @@ public bool Valid(int index, out int length, out int extra, out bool patched) return false; } - if ((_stream?.CanRead != true) || (!_stream.CanSeek)) + if ((FileAccessor.Stream?.CanRead != true) || (!FileAccessor.Stream.CanSeek)) { - _stream = new FileStream(_mulPath, FileMode.Open, FileAccess.Read, FileShare.Read); + FileAccessor.Stream = new FileStream(_mulPath, FileMode.Open, FileAccess.Read, FileShare.Read); } - if (_stream.Length < e.Lookup) + if (FileAccessor.Stream.Length < e.Lookup) { length = extra = 0; patched = false; @@ -521,16 +375,298 @@ public bool Valid(int index, out int length, out int extra, out bool patched) } [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct Entry3D + public struct Entry3D : IEntry + { + public int lookup; + public int length; + public int extra; + + public IEntry Invalid { get => new Entry3D(); } + public int Lookup { get => lookup; set => lookup = value; } + public int Length { get => length; set => length = value; } + public int Extra { get => extra; set => extra = value; } + public int DecompressedLength { get => length; set => length = value; } + public int Extra1 { get => extra & 0x0000FFFF; set => extra = (int)((extra & 0xFFFF0000) | value); } + public int Extra2 { get => (int)((extra & 0xFFFF0000) >> 16); set => extra = extra & 0x0000FFFF | (value << 16); } + public int Flag { get => 0; set { } } // No compression, means that we have only three first fields + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct Entry6D : IEntry + { + public int lookup; + public int length; + public int decompressedLength; + public int extra1; + public int extra2; + public int flag; + + public IEntry Invalid { get => new Entry6D(); } + public int Lookup { get => lookup; set => lookup = value; } + public int Length { get => length; set => length = value; } + public int Extra + { + get => extra1 << 16 | (int)extra2; + set + { + extra1 = value & 0x0000FFFF; + extra2 = (int)((value & 0xFFFF0000) >> 16); + } + } + public int DecompressedLength { get => decompressedLength; set => decompressedLength = value; } + public int Extra1 { get => extra1; set => extra1 = value; } + public int Extra2 { get => extra2; set => extra2 = value; } + public int Flag { get => flag; set => flag = value; } + } + + // Dumb access to all possible fields of entries + public interface IEntry + { + public int Lookup { get; set; } + public int Length { get; set; } + public int Extra { get; set; } + public int DecompressedLength { get; set; } + public int Extra1 { get; set; } + public int Extra2 { get; set; } + public int Flag { get; set; } + public IEntry Invalid { get; } + } + + public interface IFileAccessor + { + public IEntry GetEntry(int index); + void ApplyPatch(Entry5D patch); + public FileStream Stream { get; set; } + public int IndexLength { get; } + public long IdxLength { get; } + public IEntry this[int index] { get; set; } + } + + public class MULFileAccessor : IFileAccessor { - 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(); } + public Entry3D[] Index { get; } + + public long IdxLength { get; private set; } + public FileStream Stream { get; set; } + public int IndexLength { get => Index.Length; } + + public IEntry this[int index] { get => Index[index]; set => Index[index] = (Entry3D)value; } + + public MULFileAccessor(string idxPath, string path, int length) + { + Index = new Entry3D[length]; + using (var index = new FileStream(idxPath, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + Stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); + var count = (int)(index.Length / 12); + IdxLength = index.Length; + GCHandle gc = GCHandle.Alloc(Index, GCHandleType.Pinned); + var buffer = new byte[index.Length]; + index.Read(buffer, 0, (int)index.Length); + Marshal.Copy(buffer, 0, gc.AddrOfPinnedObject(), (int)Math.Min(IdxLength, Index.Length * 12)); + gc.Free(); + for (int i = count; i < Index.Length; ++i) + { + Index[i].Lookup = -1; + Index[i].Length = -1; + Index[i].Extra = -1; + } + } + } + + public MULFileAccessor(string idxPath, string path) + { + using (var index = new FileStream(idxPath, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + Stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); + var count = (int)(index.Length / 12); + IdxLength = index.Length; + Index = new Entry3D[count]; + GCHandle gc = GCHandle.Alloc(Index, GCHandleType.Pinned); + var buffer = new byte[index.Length]; + index.Read(buffer, 0, (int)index.Length); + Marshal.Copy(buffer, 0, gc.AddrOfPinnedObject(), (int)index.Length); + gc.Free(); + } + } + + public void ApplyPatch(Entry5D patch) + { + Index[patch.Index].Lookup = patch.Lookup; + Index[patch.Index].Length = patch.Length | (1 << 31); + Index[patch.Index].Extra = patch.Extra; + } + + public IEntry GetEntry(int index) + { + if (index < 0 || index >= Index.Length) + { + return new Entry3D(); + } + + Entry3D e = Index[index]; + + if (e.Lookup < 0) + { + return new Entry3D(); + } + return e; + } + } + + public class UOPFileAccessor : IFileAccessor + { + public Entry6D[] Index { get; } + public FileStream Stream { get; set; } + public long IdxLength { get; } + public int IndexLength { get => Index.Length; } + public IEntry this[int index] { get => Index[index]; set => Index[index] = (Entry6D)value; } + + private bool _hasExtra; + private int _length; + private string _uopEntryExtension; + + public UOPFileAccessor(string path, string uopEntryExtension, int length, int idxLength, bool hasextra) + { + Index = new Entry6D[length]; + _hasExtra = hasextra; + _length = length; + _uopEntryExtension = uopEntryExtension; + if (idxLength > 0) + { + IdxLength = idxLength * 12; + } + Stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); + + var fi = new FileInfo(path); + string uopPattern = fi.Name.Replace(fi.Extension, "").ToLowerInvariant(); + + using (var br = new BinaryReader(Stream)) + { + br.BaseStream.Seek(0, SeekOrigin.Begin); + + if (br.ReadInt32() != 0x50594D) + { + throw new ArgumentException("Bad UOP file."); + } + + var version = br.ReadUInt32(); // version + var signature = br.ReadUInt32(); // signature + long nextBlock = br.ReadInt64(); + var block_size = br.ReadUInt32(); // block capacity + var count = br.ReadInt32(); // TODO: check if we need value from here + + + var hashes = new Dictionary(); + + for (int i = 0; i < _length; i++) + { + string entryName = $"build/{uopPattern}/{i:D8}{_uopEntryExtension}"; + ulong hash = UopUtils.HashFileName(entryName); + + hashes.TryAdd(hash, i); + } + + br.BaseStream.Seek(nextBlock, SeekOrigin.Begin); + + // There are no invalid entries in .uop so we have to initialize all entries + // as invalid and then fill the valid ones + for (var i = 0; i < Index.Length; i++) + { + Index[i].Lookup = -1; + Index[i].Length = -1; + Index[i].Extra = -1; + } + + do + { + int filesCount = br.ReadInt32(); + nextBlock = br.ReadInt64(); + + for (int i = 0; i < filesCount; i++) + { + long offset = br.ReadInt64(); + int headerLength = br.ReadInt32(); + int compressedLength = br.ReadInt32(); + int decompressedLength = br.ReadInt32(); + ulong hash = br.ReadUInt64(); + uint data_hash = br.ReadUInt32(); + short flag = br.ReadInt16(); + + if (offset == 0) + { + continue; + } + + if (!hashes.TryGetValue(hash, out int idx)) + { + continue; + } + + if (idx < 0 || idx > Index.Length) + { + throw new IndexOutOfRangeException("hashes dictionary and files collection have different count of entries!"); + } + + offset += headerLength; + + if (_hasExtra && flag != 3) + { + long curPos = br.BaseStream.Position; + + br.BaseStream.Seek(offset, 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; + + // 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; + + 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); + } + } + + public void ApplyPatch(Entry5D patch) + { + Index[patch.Index].Lookup = patch.Lookup; + Index[patch.Index].Length = patch.Length | (1 << 31); + Index[patch.Index].Extra = patch.Extra; + } + + public IEntry GetEntry(int index) + { + if (index < 0 || index >= Index.Length) + { + return new Entry6D(); + } + + Entry6D e = Index[index]; + + if (e.Lookup < 0) + { + return new Entry6D(); + } + return e; + } + } } \ No newline at end of file diff --git a/Ultima/Gumps.cs b/Ultima/Gumps.cs index 2b4f3094..9f7cc9e1 100644 --- a/Ultima/Gumps.cs +++ b/Ultima/Gumps.cs @@ -3,8 +3,6 @@ using System.Drawing; using System.Drawing.Imaging; using System.IO; -using System.IO.Compression; -using System.Reflection.Metadata.Ecma335; using Ultima.Helpers; namespace Ultima @@ -26,8 +24,8 @@ static Gumps() { if (_fileIndex != null) { - _cache = new Bitmap[_fileIndex.Index.Length]; - _removed = new bool[_fileIndex.Index.Length]; + _cache = new Bitmap[_fileIndex.IndexLength]; + _removed = new bool[_fileIndex.IndexLength]; } else { @@ -44,8 +42,8 @@ public static void Reload() try { _fileIndex = new FileIndex("Gumpidx.mul", "Gumpart.mul", "gumpartLegacyMUL.uop", 0xFFFF, 12, ".tga", -1, true); - _cache = new Bitmap[_fileIndex.Index.Length]; - _removed = new bool[_fileIndex.Index.Length]; + _cache = new Bitmap[_fileIndex.IndexLength]; + _removed = new bool[_fileIndex.IndexLength]; } catch { @@ -364,9 +362,10 @@ public static unsafe Bitmap GetGump(int index, out bool patched) { return _cache[index]; } + IEntry entry = null; - Stream stream = _fileIndex.Seek(index, out Entry3D entry); - if (stream == null) + Stream stream = _fileIndex.Seek(index, ref entry); + if (stream == null || entry == null) { return null; } @@ -392,15 +391,15 @@ public static unsafe Bitmap GetGump(int index, out bool patched) uint width = (uint)entry.Extra1; uint height = (uint)entry.Extra2; - //Compressed UOPs + // Compressed UOPs if (entry.Flag >= 1) { - byte[] dbuf = new byte[entry.DecompressedLength]; - var result = ZLib.Decompress(_streamBuffer, dbuf); - if (result != ZLib.ZLibError.Ok) + var result = UopUtils.Decompress(_streamBuffer); + if (result.success is false) { - return default; + return null; } + byte[] dbuf = result.data; if (entry.Flag == 3) { @@ -411,7 +410,8 @@ public static unsafe Bitmap GetGump(int index, out bool patched) 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); + _streamBuffer = reader.ReadBytes(_streamBuffer.Length - 8); // Tbh, whole code needs to be reworked with readers, + // as we doing useless work here just rereading everyting but 8 first bytes } entry.Extra1 = (int)width; diff --git a/Ultima/Helpers/UopUtils.cs b/Ultima/Helpers/UopUtils.cs index c0958257..9b555709 100644 --- a/Ultima/Helpers/UopUtils.cs +++ b/Ultima/Helpers/UopUtils.cs @@ -1,3 +1,7 @@ +using System.IO.Compression; +using System.IO; +using System; + namespace Ultima.Helpers { static internal class UopUtils @@ -92,5 +96,33 @@ public static ulong HashFileName(string s) return ((ulong)esi << 32) | eax; } + + /// + /// Method for decompressing zlib byte arrays inside .uop + /// + /// Input compressed array of bytes + /// decompressed byte[] data + public static (bool success, byte[] data) Decompress(byte[] compressedData) + { + if (compressedData == null || compressedData.Length == 0) + { + return (false, Array.Empty()); + } + + try + { + using (var compressedStream = new MemoryStream(compressedData)) + using (var zlibStream = new ZLibStream(compressedStream, CompressionMode.Decompress)) + using (var resultStream = new MemoryStream()) + { + zlibStream.CopyTo(resultStream); + return (true, resultStream.ToArray()); + } + } + catch (Exception) + { + return (false, Array.Empty()); + } + } } } \ No newline at end of file diff --git a/Ultima/Helpers/ZLib.cs b/Ultima/Helpers/ZLib.cs deleted file mode 100644 index 6ba9701e..00000000 --- a/Ultima/Helpers/ZLib.cs +++ /dev/null @@ -1,137 +0,0 @@ -// /*************************************************************************** -// * -// * $Author: Turley -// * -// * "THE BEER-WARE LICENSE" -// * As long as you retain this notice you can do whatever you want with -// * this stuff. If we meet some day, and you think this stuff is worth it, -// * you can buy me a beer in return. -// * -// ***************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; - -namespace Ultima.Helpers -{ - public static class ZLib - { - // thanks ServUO :) - - private static readonly ICompressor _compressor; - - static ZLib() - { - _compressor = new Compressor64(); - } - - public static ZLibError Decompress(byte[] source, int offset, byte[] dest, int length) - { - return _compressor.Decompress(dest, ref length, source, source.Length - offset); - } - - public static ZLibError Decompress(IntPtr source, int sourceLength, int offset, IntPtr dest, int length) - { - return _compressor.Decompress(dest, ref length, source, sourceLength - offset); - } - - public static unsafe ZLibError Decompress(ReadOnlySpan source, Span dest) - { - fixed (byte* srcPtr = source) - fixed (byte* destPtr = dest) - return Decompress((IntPtr)srcPtr, source.Length, 0, (IntPtr)destPtr, dest.Length); - } - - private enum ZLibQuality - { - Default = -1, - - None = 0, - - Speed = 1, - Size = 9 - } - - public enum ZLibError - { - VersionError = -6, - BufferError = -5, - MemoryError = -4, - DataError = -3, - StreamError = -2, - FileError = -1, - - Ok = 0, - - StreamEnd = 1, - NeedDictionary = 2 - } - - - private interface ICompressor - { - string Version { get; } - - ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength); - ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality); - - ZLibError Decompress(byte[] dest, ref int destLength, byte[] source, int sourceLength); - ZLibError Decompress(IntPtr dest, ref int destLength, IntPtr source, int sourceLength); - } - - - private sealed class Compressor64 : ICompressor - { - public string Version => SafeNativeMethods.zlibVersion(); - - public ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength) - { - return SafeNativeMethods.compress(dest, ref destLength, source, sourceLength); - } - - public ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality) - { - return SafeNativeMethods.compress2 - ( - dest, - ref destLength, - source, - sourceLength, - quality - ); - } - - public ZLibError Decompress(byte[] dest, ref int destLength, byte[] source, int sourceLength) - { - return SafeNativeMethods.uncompress(dest, ref destLength, source, sourceLength); - } - - public ZLibError Decompress(IntPtr dest, ref int destLength, IntPtr source, int sourceLength) - { - return SafeNativeMethods.uncompress(dest, ref destLength, source, sourceLength); - } - - private class SafeNativeMethods - { - [DllImport("zlib")] - public static extern string zlibVersion(); - - [DllImport("zlib")] - public static extern ZLibError compress(byte[] dest, ref int destLength, byte[] source, int sourceLength); - - [DllImport("zlib")] - public static extern ZLibError compress2(byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality); - - [DllImport("zlib")] - public static extern ZLibError uncompress(byte[] dest, ref int destLen, byte[] source, int sourceLen); - - [DllImport("zlib")] - public static extern ZLibError uncompress(IntPtr dest, ref int destLen, IntPtr source, int sourceLen); - } - } - } -} diff --git a/Ultima/Skills.cs b/Ultima/Skills.cs index 89903244..96abdb98 100644 --- a/Ultima/Skills.cs +++ b/Ultima/Skills.cs @@ -20,7 +20,7 @@ public static List SkillEntries } _skillEntries = new List(); - for (int i = 0; i < _fileIndex.Index.Length; ++i) + for (int i = 0; i < _fileIndex.IndexLength; ++i) { SkillInfo info = GetSkill(i); if (info == null) @@ -42,7 +42,7 @@ public static void Reload() { _fileIndex = new FileIndex("skills.idx", "skills.mul", 16); _skillEntries = new List(); - for (int i = 0; i < _fileIndex.Index.Length; ++i) + for (int i = 0; i < _fileIndex.IndexLength; ++i) { SkillInfo info = GetSkill(i); if (info == null) @@ -105,7 +105,7 @@ public static void Save(string path) using (var binidx = new BinaryWriter(fsidx)) using (var binmul = new BinaryWriter(fsmul)) { - for (int i = 0; i < _fileIndex.Index.Length; ++i) + for (int i = 0; i < _fileIndex.IndexLength; ++i) { SkillInfo skill = (i < _skillEntries.Count) ? _skillEntries[i] : null; if (skill == null) diff --git a/Ultima/Sound.cs b/Ultima/Sound.cs index b65f189c..dde1ddbc 100644 --- a/Ultima/Sound.cs +++ b/Ultima/Sound.cs @@ -108,7 +108,7 @@ public static UoSound GetSound(int soundId, out bool translated) Stream stream = _fileIndex.Seek(soundId, out int length, out int _, out bool _); - if (_fileIndex.Index[soundId].Lookup < 0 || length <= 0) + if (_fileIndex[soundId].Lookup < 0 || length <= 0) { if (!_translations.TryGetValue(soundId, out soundId)) { @@ -215,7 +215,7 @@ public static bool IsValidSound(int soundId, out string name, out bool translate Stream stream = _fileIndex.Seek(soundId, out int length, out _, out _); - if (_fileIndex.Index[soundId].Lookup < 0 || length <= 0) + if (_fileIndex[soundId].Lookup < 0 || length <= 0) { if (!_translations.TryGetValue(soundId, out soundId)) { @@ -271,7 +271,7 @@ public static double GetSoundLength(int soundId) else { Stream stream = _fileIndex.Seek(soundId, out int length, out int _, out bool _); - if (_fileIndex.Index[soundId].Lookup < 0 || length <= 0) + if (_fileIndex[soundId].Lookup < 0 || length <= 0) { if (!_translations.TryGetValue(soundId, out soundId)) { diff --git a/UoFiddler.Controls/UserControls/GumpControl.cs b/UoFiddler.Controls/UserControls/GumpControl.cs index 589c7890..4079bfc8 100644 --- a/UoFiddler.Controls/UserControls/GumpControl.cs +++ b/UoFiddler.Controls/UserControls/GumpControl.cs @@ -610,7 +610,7 @@ private void PreLoaderDoWork(object sender, DoWorkEventArgs e) for (int i = 0; i < Gumps.GetCount(); ++i) { Gumps.GetGump(i); - PreLoader.ReportProgress(i); + PreLoader.ReportProgress(1); } } From e4f66f16fe9461f3b08b17dcafd3175a6387031f Mon Sep 17 00:00:00 2001 From: Krubster Date: Sun, 19 Jan 2025 14:54:48 +0300 Subject: [PATCH 4/5] - forgot to remove zlib.dll --- Ultima/zlib.dll | Bin 74240 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Ultima/zlib.dll diff --git a/Ultima/zlib.dll b/Ultima/zlib.dll deleted file mode 100644 index a43d73dcc285c2c8f5c7efcd20166c64d2e92287..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 74240 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 Date: Tue, 21 Jan 2025 19:51:29 +0300 Subject: [PATCH 5/5] Some cleanup --- Ultima/FileIndex.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Ultima/FileIndex.cs b/Ultima/FileIndex.cs index 942d9b92..4adec8f3 100644 --- a/Ultima/FileIndex.cs +++ b/Ultima/FileIndex.cs @@ -8,7 +8,7 @@ namespace Ultima { public sealed class FileIndex { - IFileAccessor FileAccessor { get; set; } + public IFileAccessor FileAccessor { get; } public long IndexLength { get => FileAccessor.IndexLength; } public long IdxLength { get => FileAccessor.IdxLength; } @@ -184,8 +184,6 @@ public FileIndex(string idxFile, string mulFile, int file) } else { - // _stream = null; - // Index = new Entry3D[1]; return; }