diff --git a/Ultima/FileIndex.cs b/Ultima/FileIndex.cs index c4c5c28..4adec8f 100644 --- a/Ultima/FileIndex.cs +++ b/Ultima/FileIndex.cs @@ -8,12 +8,13 @@ namespace Ultima { public sealed class FileIndex { - public Entry3D[] Index { get; } + public IFileAccessor FileAccessor { get; } - 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,134 +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."); - } - - br.ReadInt64(); // version + signature - long nextBlock = br.ReadInt64(); - br.ReadInt32(); // block capacity - _ = 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(); - br.ReadUInt32(); // Adler32 - short flag = br.ReadInt16(); - - int entryLength = flag == 1 ? compressedLength : decompressedLength; - - 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!"); - } - - Index[idx].Lookup = (int)(offset + headerLength); - Index[idx].Length = entryLength; - - if (!hasExtra) - { - continue; - } - - long curPos = br.BaseStream.Position; - - br.BaseStream.Seek(offset + headerLength, 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; - // 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); - } - } - 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; } @@ -245,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); } } @@ -303,23 +180,10 @@ 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]; return; } @@ -330,27 +194,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) { @@ -376,19 +237,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; @@ -397,20 +258,71 @@ 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, ref IEntry entry) + { + if (index < 0 || index >= FileAccessor.IndexLength) + { + return null; + } + + IEntry e = FileAccessor.GetEntry(index); + + if (e.Lookup < 0) + { + entry = e.Invalid; + + return null; + } + entry = e; + + if ((e.Length & (1 << 31)) != 0) + { + Verdata.Seek(e.Lookup); + return Verdata.Stream; + } + + if (e.Length < 0) + { + entry = e.Invalid; + return null; + } + + if ((FileAccessor.Stream?.CanRead != true) || (!FileAccessor.Stream.CanSeek)) + { + FileAccessor.Stream = _mulPath == null ? null : new FileStream(_mulPath, FileMode.Open, FileAccess.Read, FileShare.Read); + } + + if (FileAccessor.Stream == null) + { + entry = e.Invalid; + return null; + } + + if (FileAccessor.Stream.Length < e.Lookup) + { + entry = e.Invalid; + return null; + } + + + 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) { @@ -442,12 +354,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; @@ -461,10 +373,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 Extra; + 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 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 b9403fb..9f7cc9e 100644 --- a/Ultima/Gumps.cs +++ b/Ultima/Gumps.cs @@ -3,6 +3,7 @@ using System.Drawing; using System.Drawing.Imaging; using System.IO; +using Ultima.Helpers; namespace Ultima { @@ -23,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 { @@ -41,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 { @@ -154,6 +155,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; } @@ -360,84 +362,120 @@ public static unsafe Bitmap GetGump(int index, out bool patched) { return _cache[index]; } + IEntry entry = null; - Stream stream = _fileIndex.Seek(index, out int length, out int extra, out patched); - if (stream == null) + Stream stream = _fileIndex.Seek(index, ref entry); + if (stream == null || entry == 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 + if (entry.Flag >= 1) + { + var result = UopUtils.Decompress(_streamBuffer); + if (result.success is false) + { + return null; + } + byte[] dbuf = result.data; - 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); // 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; + 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 = 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/UopUtils.cs b/Ultima/Helpers/UopUtils.cs index c095825..9b55570 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/Skills.cs b/Ultima/Skills.cs index 8990324..96abdb9 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 b65f189..dde1ddb 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)) {