From 8a4e4adeabca38648aa3d8fdd982b3ba205a2859 Mon Sep 17 00:00:00 2001 From: Xian55 <367101+Xian55@users.noreply.github.com> Date: Fri, 1 Sep 2023 12:48:22 +0200 Subject: [PATCH 1/2] PPather: Split StormDll into separate classes. Using LibraryImport over DllImport. Use MpqFileStream over SFileExtractFile API. --- PPather/StormDll/Archive.cs | 107 ++++++++++++ PPather/StormDll/ArchiveSet.cs | 51 ++++++ PPather/StormDll/MpqFileStream.cs | 108 ++++++++++++ PPather/StormDll/OpenArchive.cs | 30 ++++ PPather/StormDll/OpenFile.cs | 8 + PPather/StormDll/StormDllx64.cs | 53 ++++++ PPather/StormDll/StormDllx86.cs | 53 ++++++ PPather/Triangles/MPQTriangleSupplier.cs | 9 +- PPather/Triangles/StormDll.cs | 211 ----------------------- PPather/Triangles/WmoFile.cs | 88 ++++------ 10 files changed, 447 insertions(+), 271 deletions(-) create mode 100644 PPather/StormDll/Archive.cs create mode 100644 PPather/StormDll/ArchiveSet.cs create mode 100644 PPather/StormDll/MpqFileStream.cs create mode 100644 PPather/StormDll/OpenArchive.cs create mode 100644 PPather/StormDll/OpenFile.cs create mode 100644 PPather/StormDll/StormDllx64.cs create mode 100644 PPather/StormDll/StormDllx86.cs delete mode 100644 PPather/Triangles/StormDll.cs diff --git a/PPather/StormDll/Archive.cs b/PPather/StormDll/Archive.cs new file mode 100644 index 000000000..0678e2260 --- /dev/null +++ b/PPather/StormDll/Archive.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace StormDll; + +#nullable enable + +internal sealed class Archive +{ + public const uint SFILE_INVALID_SIZE = 0xFFFFFFFF; + + private readonly IntPtr handle; + + private readonly HashSet fileList = new(StringComparer.InvariantCultureIgnoreCase); + + private static readonly bool Is64Bit = Environment.Is64BitProcess; + + public Archive(string file, out bool open, uint prio, OpenArchive flags) + { + open = Is64Bit + ? StormDllx64.SFileOpenArchive(file, prio, flags, out handle) + : StormDllx86.SFileOpenArchive(file, prio, flags, out handle); + + if (!open) + return; + + using MpqFileStream mpq = GetStream("(listfile)"); + using MemoryStream ms = new(mpq.ReadAllBytes()); + using StreamReader stream = new(ms); + + while (!stream.EndOfStream) + { + fileList.Add(stream.ReadLine()!); + } + + if (fileList.Count == 0) + throw new InvalidOperationException($"{nameof(fileList)} contains no elements!"); + } + + public bool IsOpen() + { + return handle != IntPtr.Zero; + } + + public bool HasFile(string name) + { + return fileList.Contains(name); + } + + public bool SFileCloseArchive() + { + fileList.Clear(); + return Is64Bit + ? StormDllx64.SFileCloseArchive(handle) + : StormDllx86.SFileCloseArchive(handle); + } + + public MpqFileStream GetStream(string fileName) + { + return !SFileOpenFileEx(handle, fileName, OpenFile.SFILE_OPEN_FROM_MPQ, out IntPtr fileHandle) + ? throw new IOException("SFileOpenFileEx failed") + : new MpqFileStream(fileHandle); + } + + public static bool SFileReadFile(IntPtr fileHandle, Span buffer, long toRead, out long read) + { + return Is64Bit + ? StormDllx64.SFileReadFile(fileHandle, buffer, toRead, out read) + : StormDllx86.SFileReadFile(fileHandle, buffer, toRead, out read); + } + + public static bool SFileCloseFile(IntPtr fileHandle) + { + return Is64Bit + ? StormDllx64.SFileCloseFile(fileHandle) + : StormDllx86.SFileCloseFile(fileHandle); + } + + public static long SFileGetFileSize(IntPtr fileHandle, out long fileSizeHigh) + { + return Is64Bit + ? StormDllx64.SFileGetFileSize(fileHandle, out fileSizeHigh) + : StormDllx86.SFileGetFileSize(fileHandle, out fileSizeHigh); + } + + public static uint SFileSetFilePointer(IntPtr fileHandle, + long filePos, + ref uint plFilePosHigh, + SeekOrigin origin) + { + return Is64Bit + ? StormDllx64.SFileSetFilePointer(fileHandle, filePos, ref plFilePosHigh, origin) + : StormDllx86.SFileSetFilePointer(fileHandle, filePos, ref plFilePosHigh, origin); + } + + public static bool SFileOpenFileEx( + IntPtr archiveHandle, + string fileName, + OpenFile searchScope, + out IntPtr fileHandle) + { + return Is64Bit + ? StormDllx64.SFileOpenFileEx(archiveHandle, fileName, searchScope, out fileHandle) + : StormDllx86.SFileOpenFileEx(archiveHandle, fileName, searchScope, out fileHandle); + } +} \ No newline at end of file diff --git a/PPather/StormDll/ArchiveSet.cs b/PPather/StormDll/ArchiveSet.cs new file mode 100644 index 000000000..c80c08868 --- /dev/null +++ b/PPather/StormDll/ArchiveSet.cs @@ -0,0 +1,51 @@ +using System.IO; +using Microsoft.Extensions.Logging; + +namespace StormDll; + +public sealed class ArchiveSet +{ + private readonly Archive[] archives; + + public ArchiveSet(ILogger logger, string[] files) + { + archives = new Archive[files.Length]; + + for (int i = 0; i < files.Length; i++) + { + Archive a = new(files[i], out bool open, 0, + OpenArchive.MPQ_OPEN_NO_LISTFILE | + OpenArchive.MPQ_OPEN_NO_ATTRIBUTES | + OpenArchive.MPQ_OPEN_NO_HEADER_SEARCH | + OpenArchive.MPQ_OPEN_READ_ONLY); + + if (open && a.IsOpen()) + { + archives[i] = a; + + if (logger.IsEnabled(LogLevel.Trace)) + logger.LogTrace($"Archive[{i}] open {files[i]}"); + } + else if (logger.IsEnabled(LogLevel.Trace)) + logger.LogTrace($"Archive[{i}] openfail {files[i]}"); + } + } + + public MpqFileStream GetStream(string fileName) + { + for (int i = 0; i < archives.Length; i++) + { + Archive a = archives[i]; + if (a.HasFile(fileName)) + return a.GetStream(fileName); + } + + throw new FileNotFoundException(nameof(fileName)); + } + + public void Close() + { + for (int i = 0; i < archives.Length; i++) + archives[i].SFileCloseArchive(); + } +} \ No newline at end of file diff --git a/PPather/StormDll/MpqFileStream.cs b/PPather/StormDll/MpqFileStream.cs new file mode 100644 index 000000000..832e0c3b0 --- /dev/null +++ b/PPather/StormDll/MpqFileStream.cs @@ -0,0 +1,108 @@ +using System; +using System.ComponentModel; +using System.IO; +using System.Runtime.InteropServices; + +namespace StormDll; + +public sealed class MpqFileStream : Stream +{ + private const int ERROR_HANDLE_EOF = 38; + + private readonly long length; + + private nint fileHandle; + private long position; + + public MpqFileStream(nint fileHandle) + { + this.fileHandle = fileHandle; + + long low = Archive.SFileGetFileSize(fileHandle, out long high); + length = (high << 32) | low; + } + + public sealed override bool CanRead => true; + public sealed override bool CanSeek => true; + public sealed override bool CanWrite => false; + + public sealed override long Length => length; + + public sealed override long Position + { + get => position; + set => Seek(value, SeekOrigin.Begin); + } + + public sealed override void Flush() { } + + public sealed override int Read(byte[] buffer, int offset, int count) + { + if (offset > buffer.Length || (offset + count) > buffer.Length) + throw new ArgumentException(); + + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count)); + + Span bufferSpan = buffer.AsSpan(offset); + bool success = Archive.SFileReadFile(fileHandle, bufferSpan, count, out long bytesRead); + position += bytesRead; + + if (!success) + { + int lastError = Marshal.GetLastWin32Error(); + if (lastError != ERROR_HANDLE_EOF) + throw new Win32Exception(lastError); + } + + return unchecked((int)bytesRead); + } + + public sealed override long Seek(long offset, SeekOrigin origin) + { + if (origin == SeekOrigin.Current && offset < 0) + { + offset = Position + offset; + origin = SeekOrigin.Begin; + } + + uint low, high; + low = unchecked((uint)(offset & 0xffffffffu)); + high = unchecked((uint)(offset >> 32)); + + uint result = Archive.SFileSetFilePointer(fileHandle, low, ref high, origin); + if (result == Archive.SFILE_INVALID_SIZE) + throw new IOException("SFileSetFilePointer failed"); + + return position = result; + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + + public sealed override void Close() + { + base.Close(); + + if (fileHandle == nint.Zero) + return; + + Archive.SFileCloseFile(fileHandle); + fileHandle = nint.Zero; + } + + public byte[] ReadAllBytes() + { + byte[] bytes = new byte[Length]; + Read(bytes, 0, (int)Length); + + return bytes; + } +} \ No newline at end of file diff --git a/PPather/StormDll/OpenArchive.cs b/PPather/StormDll/OpenArchive.cs new file mode 100644 index 000000000..0e60c90fe --- /dev/null +++ b/PPather/StormDll/OpenArchive.cs @@ -0,0 +1,30 @@ +using System; + +namespace StormDll; + +[Flags] +internal enum OpenArchive : uint +{ + BASE_PROVIDER_FILE = 0x00000000, // Base data source is a file + BASE_PROVIDER_MAP = 0x00000001, // Base data source is memory-mapped file + BASE_PROVIDER_HTTP = 0x00000002, // Base data source is a file on web server + BASE_PROVIDER_MASK = 0x0000000F, // Mask for base provider value + STREAM_PROVIDER_FLAT = 0x00000000, // Stream is linear with no offset mapping + STREAM_PROVIDER_PARTIAL = 0x00000010, // Stream is partial file (.part) + STREAM_PROVIDER_MPQE = 0x00000020, // Stream is an encrypted MPQ + STREAM_PROVIDER_BLOCK4 = 0x00000030, // = 0x4000 per block, text MD5 after each block, max = 0x2000 blocks per file + STREAM_PROVIDER_MASK = 0x000000F0, // Mask for stream provider value + STREAM_FLAG_READ_ONLY = 0x00000100, // Stream is read only + STREAM_FLAG_WRITE_SHARE = 0x00000200, // Allow write sharing when open for write + STREAM_FLAG_USE_BITMAP = 0x00000400, // If the file has a file bitmap, load it and use it + STREAM_OPTIONS_MASK = 0x0000FF00, // Mask for stream options + STREAM_PROVIDERS_MASK = 0x000000FF, // Mask to get stream providers + STREAM_FLAGS_MASK = 0x0000FFFF, // Mask for all stream flags (providers+options) + MPQ_OPEN_NO_LISTFILE = 0x00010000, // Don't load the internal listfile + MPQ_OPEN_NO_ATTRIBUTES = 0x00020000, // Don't open the attributes + MPQ_OPEN_NO_HEADER_SEARCH = 0x00040000, // Don't search for the MPQ header past the begin of the file + MPQ_OPEN_FORCE_MPQ_V1 = 0x00080000, // Always open the archive as MPQ v 1.00, ignore the "wFormatVersion" variable in the header + MPQ_OPEN_CHECK_SECTOR_CRC = 0x00100000, // On files with MPQ_FILE_SECTOR_CRC, the CRC will be checked when reading file + MPQ_OPEN_FORCE_LISTFILE = 0x00400000, // Force add listfile even if there is none at the moment of opening + MPQ_OPEN_READ_ONLY = STREAM_FLAG_READ_ONLY +}; \ No newline at end of file diff --git a/PPather/StormDll/OpenFile.cs b/PPather/StormDll/OpenFile.cs new file mode 100644 index 000000000..268026cd6 --- /dev/null +++ b/PPather/StormDll/OpenFile.cs @@ -0,0 +1,8 @@ +namespace StormDll; + +internal enum OpenFile : uint +{ + SFILE_OPEN_FROM_MPQ = 0x00000000, // Open the file from the MPQ archive + SFILE_OPEN_CHECK_EXISTS = 0xFFFFFFFC, // Only check whether the file exists + SFILE_OPEN_LOCAL_FILE = 0xFFFFFFFF // Open a local file +}; \ No newline at end of file diff --git a/PPather/StormDll/StormDllx64.cs b/PPather/StormDll/StormDllx64.cs new file mode 100644 index 000000000..d9ad4a1d5 --- /dev/null +++ b/PPather/StormDll/StormDllx64.cs @@ -0,0 +1,53 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace StormDll; + +internal sealed partial class StormDllx64 +{ + [LibraryImport("MPQ\\StormLib_x64.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static partial bool SFileOpenArchive( + [MarshalAs(UnmanagedType.LPWStr)] string szMpqName, + uint dwPriority, + OpenArchive dwFlags, + out nint phMpq); + + [LibraryImport("MPQ\\StormLib_x64.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static partial bool SFileCloseArchive(nint hMpq); + + [LibraryImport("MPQ\\StormLib_x64.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static partial bool SFileReadFile( + nint fileHandle, + Span buffer, + [MarshalAs(UnmanagedType.I8)] long toRead, + out long read); + + [LibraryImport("MPQ\\StormLib_x64.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static partial bool SFileCloseFile( + nint fileHandle); + + [LibraryImport("MPQ\\StormLib_x64.dll")] + public static partial uint SFileGetFileSize( + nint fileHandle, + out long fileSizeHigh); + + [LibraryImport("MPQ\\StormLib_x64.dll")] + public static partial uint SFileSetFilePointer( + nint fileHandle, + long filePos, + ref uint plFilePosHigh, + SeekOrigin origin); + + [LibraryImport("MPQ\\StormLib_x64.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static partial bool SFileOpenFileEx( + nint archiveHandle, + [MarshalAs(UnmanagedType.LPStr)] string fileName, + OpenFile searchScope, + out nint fileHandle); +} \ No newline at end of file diff --git a/PPather/StormDll/StormDllx86.cs b/PPather/StormDll/StormDllx86.cs new file mode 100644 index 000000000..284a92784 --- /dev/null +++ b/PPather/StormDll/StormDllx86.cs @@ -0,0 +1,53 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace StormDll; + +internal sealed partial class StormDllx86 +{ + [LibraryImport("MPQ\\StormLib_x86.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static partial bool SFileOpenArchive( + [MarshalAs(UnmanagedType.LPWStr)] string szMpqName, + uint dwPriority, + OpenArchive dwFlags, + out nint phMpq); + + [LibraryImport("MPQ\\StormLib_x86.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static partial bool SFileCloseArchive(nint hMpq); + + [LibraryImport("MPQ\\StormLib_x86.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static partial bool SFileReadFile( + nint fileHandle, + Span buffer, + [MarshalAs(UnmanagedType.I8)] Int64 toRead, + out long read); + + [LibraryImport("MPQ\\StormLib_x86.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static partial bool SFileCloseFile( + nint fileHandle); + + [LibraryImport("MPQ\\StormLib_x86.dll")] + public static partial uint SFileGetFileSize( + nint fileHandle, + out long fileSizeHigh); + + [LibraryImport("MPQ\\StormLib_x86.dll")] + public static partial uint SFileSetFilePointer( + nint fileHandle, + long filePos, + ref uint plFilePosHigh, + SeekOrigin origin); + + [LibraryImport("MPQ\\StormLib_x86.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static partial bool SFileOpenFileEx( + nint archiveHandle, + [MarshalAs(UnmanagedType.LPStr)] string fileName, + OpenFile searchScope, + out nint fileHandle); +} \ No newline at end of file diff --git a/PPather/Triangles/MPQTriangleSupplier.cs b/PPather/Triangles/MPQTriangleSupplier.cs index 7214891da..93699f0b7 100644 --- a/PPather/Triangles/MPQTriangleSupplier.cs +++ b/PPather/Triangles/MPQTriangleSupplier.cs @@ -26,7 +26,6 @@ Copyright Pontus Borg 2008 using Microsoft.Extensions.Logging; using PPather.Triangles.Data; using static Wmo.MapTileFile; -using System.Buffers; using PPather; using System.Runtime.CompilerServices; @@ -48,12 +47,12 @@ public MPQTriangleSupplier(ILogger logger, DataConfig dataConfig, float mapId) this.logger = logger; this.mapId = mapId; - archive = new StormDll.ArchiveSet(this.logger, GetArchiveNames(dataConfig)); - modelmanager = new ModelManager(archive, dataConfig); - wmomanager = new WMOManager(archive, modelmanager, dataConfig); + archive = new StormDll.ArchiveSet(logger, GetArchiveNames(dataConfig)); + modelmanager = new ModelManager(archive); + wmomanager = new WMOManager(archive, modelmanager); wdt = new WDT(); - wdtf = new WDTFile(archive, this.mapId, wdt, wmomanager, modelmanager, this.logger, dataConfig); + wdtf = new WDTFile(archive, mapId, wdt, wmomanager, modelmanager, logger); // TODO: move this to WDTFile if (!wdtf.loaded) diff --git a/PPather/Triangles/StormDll.cs b/PPather/Triangles/StormDll.cs deleted file mode 100644 index 6e28a36c1..000000000 --- a/PPather/Triangles/StormDll.cs +++ /dev/null @@ -1,211 +0,0 @@ -/* - This file is part of ppather. - - PPather is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PPather is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with ppather. If not, see . - - Copyright Pontus Borg 2008 - - */ - -using Microsoft.Extensions.Logging; - -using System; -using System.IO; -using System.Runtime.InteropServices; -using System.Collections.Generic; - -namespace StormDll; - -internal sealed class StormDllx64 -{ - [DllImport("MPQ\\StormLib_x64.dll")] - public static extern bool SFileOpenArchive( - [MarshalAs(UnmanagedType.LPWStr)] string szMpqName, - uint dwPriority, - [MarshalAs(UnmanagedType.U4)] OpenArchive dwFlags, - out IntPtr phMpq); - - [DllImport("MPQ\\StormLib_x64.dll")] - public static extern bool SFileCloseArchive(IntPtr hMpq); - - [DllImport("MPQ\\StormLib_x64.dll")] - public static extern bool SFileExtractFile( - IntPtr hMpq, - [MarshalAs(UnmanagedType.LPStr)] string szToExtract, - [MarshalAs(UnmanagedType.LPWStr)] string szExtracted, - [MarshalAs(UnmanagedType.U4)] OpenFile dwSearchScope); -} - -internal sealed class StormDllx86 -{ - [DllImport("MPQ\\StormLib_x86.dll")] - public static extern bool SFileOpenArchive( - [MarshalAs(UnmanagedType.LPWStr)] string szMpqName, - uint dwPriority, - [MarshalAs(UnmanagedType.U4)] OpenArchive dwFlags, - out IntPtr phMpq); - - [DllImport("MPQ\\StormLib_x86.dll")] - public static extern bool SFileCloseArchive(IntPtr hMpq); - - [DllImport("MPQ\\StormLib_x86.dll")] - public static extern bool SFileExtractFile( - IntPtr hMpq, - [MarshalAs(UnmanagedType.LPStr)] string szToExtract, - [MarshalAs(UnmanagedType.LPWStr)] string szExtracted, - [MarshalAs(UnmanagedType.U4)] OpenFile dwSearchScope); -} - -// Flags for SFileOpenArchive -[Flags] -public enum OpenArchive : uint -{ - BASE_PROVIDER_FILE = 0x00000000, // Base data source is a file - BASE_PROVIDER_MAP = 0x00000001, // Base data source is memory-mapped file - BASE_PROVIDER_HTTP = 0x00000002, // Base data source is a file on web server - BASE_PROVIDER_MASK = 0x0000000F, // Mask for base provider value - STREAM_PROVIDER_FLAT = 0x00000000, // Stream is linear with no offset mapping - STREAM_PROVIDER_PARTIAL = 0x00000010, // Stream is partial file (.part) - STREAM_PROVIDER_MPQE = 0x00000020, // Stream is an encrypted MPQ - STREAM_PROVIDER_BLOCK4 = 0x00000030, // = 0x4000 per block, text MD5 after each block, max = 0x2000 blocks per file - STREAM_PROVIDER_MASK = 0x000000F0, // Mask for stream provider value - STREAM_FLAG_READ_ONLY = 0x00000100, // Stream is read only - STREAM_FLAG_WRITE_SHARE = 0x00000200, // Allow write sharing when open for write - STREAM_FLAG_USE_BITMAP = 0x00000400, // If the file has a file bitmap, load it and use it - STREAM_OPTIONS_MASK = 0x0000FF00, // Mask for stream options - STREAM_PROVIDERS_MASK = 0x000000FF, // Mask to get stream providers - STREAM_FLAGS_MASK = 0x0000FFFF, // Mask for all stream flags (providers+options) - MPQ_OPEN_NO_LISTFILE = 0x00010000, // Don't load the internal listfile - MPQ_OPEN_NO_ATTRIBUTES = 0x00020000, // Don't open the attributes - MPQ_OPEN_NO_HEADER_SEARCH = 0x00040000, // Don't search for the MPQ header past the begin of the file - MPQ_OPEN_FORCE_MPQ_V1 = 0x00080000, // Always open the archive as MPQ v 1.00, ignore the "wFormatVersion" variable in the header - MPQ_OPEN_CHECK_SECTOR_CRC = 0x00100000, // On files with MPQ_FILE_SECTOR_CRC, the CRC will be checked when reading file - MPQ_OPEN_FORCE_LISTFILE = 0x00400000, // Force add listfile even if there is none at the moment of opening - MPQ_OPEN_READ_ONLY = STREAM_FLAG_READ_ONLY -}; - -// Values for SFileExtractFile -public enum OpenFile : uint -{ - SFILE_OPEN_FROM_MPQ = 0x00000000, // Open the file from the MPQ archive - SFILE_OPEN_CHECK_EXISTS = 0xFFFFFFFC, // Only check whether the file exists - SFILE_OPEN_LOCAL_FILE = 0xFFFFFFFF // Open a local file -}; - -public sealed class ArchiveSet -{ - private readonly Archive[] archives; - - public ArchiveSet(ILogger logger, string[] files) - { - archives = new Archive[files.Length]; - - for (int i = 0; i < files.Length; i++) - { - Archive a = new(files[i], out bool open, 0, - OpenArchive.MPQ_OPEN_NO_LISTFILE | - OpenArchive.MPQ_OPEN_NO_ATTRIBUTES | - OpenArchive.MPQ_OPEN_NO_HEADER_SEARCH | - OpenArchive.MPQ_OPEN_READ_ONLY); - - if (open && a.IsOpen()) - { - archives[i] = a; - - if (logger.IsEnabled(LogLevel.Trace)) - logger.LogTrace($"Archive[{i}] open {files[i]}"); - } - else if (logger.IsEnabled(LogLevel.Trace)) - logger.LogTrace($"Archive[{i}] openfail {files[i]}"); - } - } - - public bool SFileExtractFile(string from, string to, OpenFile dwSearchScope = OpenFile.SFILE_OPEN_FROM_MPQ) - { - for (int i = 0; i < archives.Length; i++) - { - Archive a = archives[i]; - if (a.HasFile(from)) - { - return a.SFileExtractFile(from, to, dwSearchScope); - } - } - - return false; - } - - public void Close() - { - for (int i = 0; i < archives.Length; i++) - archives[i].SFileCloseArchive(); - } -} - -internal sealed class Archive -{ - private readonly IntPtr handle; - - private readonly HashSet fileList = new(StringComparer.InvariantCultureIgnoreCase); - - private static readonly bool Is64Bit = Environment.Is64BitProcess; - - public Archive(string file, out bool open, uint Prio, OpenArchive Flags) - { - open = Is64Bit - ? StormDllx64.SFileOpenArchive(file, Prio, Flags, out handle) - : StormDllx86.SFileOpenArchive(file, Prio, Flags, out handle); - - if (!open) - return; - - string temp = Path.GetTempFileName(); - - bool extracted = Is64Bit - ? StormDllx64.SFileExtractFile(handle, "(listfile)", temp, OpenFile.SFILE_OPEN_FROM_MPQ) - : StormDllx86.SFileExtractFile(handle, "(listfile)", temp, OpenFile.SFILE_OPEN_FROM_MPQ); - - if (extracted && File.Exists(temp)) - { - foreach (string line in File.ReadLines(temp)) - { - fileList.Add(line); - } - } - } - - public bool IsOpen() - { - return handle != IntPtr.Zero; - } - - public bool HasFile(string name) - { - return fileList.Contains(name); - } - - public bool SFileCloseArchive() - { - fileList.Clear(); - return Is64Bit - ? StormDllx64.SFileCloseArchive(handle) - : StormDllx86.SFileCloseArchive(handle); - } - - public bool SFileExtractFile(string from, string to, OpenFile dwSearchScope) - { - return Is64Bit - ? StormDllx64.SFileExtractFile(handle, from, to, dwSearchScope) - : StormDllx86.SFileExtractFile(handle, from, to, dwSearchScope); - } -} \ No newline at end of file diff --git a/PPather/Triangles/WmoFile.cs b/PPather/Triangles/WmoFile.cs index c76d40a9e..c96c7eddf 100644 --- a/PPather/Triangles/WmoFile.cs +++ b/PPather/Triangles/WmoFile.cs @@ -29,6 +29,7 @@ Copyright Pontus Borg 2008 using System.Text; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; +using StormDll; namespace Wmo; @@ -75,42 +76,29 @@ public static string ExtractString(ReadOnlySpan buff, int off) public sealed class WMOManager : Manager { - private readonly StormDll.ArchiveSet archive; + private readonly ArchiveSet archive; private readonly ModelManager modelmanager; - private readonly DataConfig dataConfig; - public WMOManager(StormDll.ArchiveSet archive, ModelManager modelmanager, DataConfig dataConfig) + public WMOManager(ArchiveSet archive, ModelManager modelmanager) { this.archive = archive; this.modelmanager = modelmanager; - this.dataConfig = dataConfig; } public override bool Load(string path, out WMO t) { - string tempFile = Path.Join(dataConfig.PPather, "wmo.tmp"); //wmo - if (!archive.SFileExtractFile(path, tempFile)) - { - t = default; - return false; - } - t = new() { fileName = path }; - _ = new WmoRootFile(tempFile, t, modelmanager); + _ = new WmoRootFile(archive, path, t, modelmanager); for (int i = 0; i < t.groups.Length; i++) { ReadOnlySpan part = path[..^4].AsSpan(); - string gf = string.Format("{0}_{1,3:000}.wmo", part.ToString(), i); - - if (!archive.SFileExtractFile(gf, tempFile)) - continue; - - _ = new WmoGroupFile(t.groups[i], tempFile); + string name = string.Format("{0}_{1,3:000}.wmo", part.ToString(), i); + _ = new WmoGroupFile(archive, name, t.groups[i]); } return true; } @@ -195,32 +183,23 @@ public T AddAndLoadIfNeeded(string path) public sealed class ModelManager : Manager { - private readonly StormDll.ArchiveSet archive; - private readonly DataConfig dataConfig; + private readonly ArchiveSet archive; - public ModelManager(StormDll.ArchiveSet archive, DataConfig dataConfig) + public ModelManager(ArchiveSet archive) { this.archive = archive; - this.dataConfig = dataConfig; } public override bool Load(string path, out Model t) { // change .mdx to .m2 - //string file=path.Substring(0, path.Length-4)+".m2"; - if (Path.GetExtension(path).Equals(".mdx") || Path.GetExtension(path).Equals(".mdl")) + if (Path.GetExtension(path).Equals(".mdx", StringComparison.OrdinalIgnoreCase) || + Path.GetExtension(path).Equals(".mdl", StringComparison.OrdinalIgnoreCase)) { path = Path.ChangeExtension(path, ".m2"); } - string tempFile = Path.Join(dataConfig.PPather, "model.tmp"); //model - if (!archive.SFileExtractFile(path, tempFile)) - { - t = default; - return false; - } - - t = ModelFile.Read(tempFile, path); + t = ModelFile.Read(archive, path); return true; } } @@ -274,9 +253,10 @@ public Model(string fileName, float[] vertices, ushort[] boundingTriangles, floa public static class ModelFile { - public static Model Read(string path, string fileName) + public static Model Read(ArchiveSet archive, string fileName) { - using Stream stream = File.OpenRead(path); + using MpqFileStream mpq = archive.GetStream(fileName); + using MemoryStream stream = new(mpq.ReadAllBytes()); using BinaryReader file = new(stream); // UPDATED FOR WOTLK 17.10.2008 by toblakai @@ -467,20 +447,18 @@ internal sealed class WDT internal sealed class WDTFile { private readonly ILogger logger; - private readonly DataConfig dataConfig; private readonly WMOManager wmomanager; private readonly ModelManager modelmanager; private readonly WDT wdt; - private readonly StormDll.ArchiveSet archive; + private readonly ArchiveSet archive; private readonly string pathName; public bool loaded; - public WDTFile(StormDll.ArchiveSet archive, float mapId, WDT wdt, WMOManager wmomanager, ModelManager modelmanager, ILogger logger, DataConfig dataConfig) + public WDTFile(ArchiveSet archive, float mapId, WDT wdt, WMOManager wmomanager, ModelManager modelmanager, ILogger logger) { this.logger = logger; - this.dataConfig = dataConfig; this.pathName = ContinentDB.IdToName[mapId]; this.wdt = wdt; @@ -489,11 +467,8 @@ public WDTFile(StormDll.ArchiveSet archive, float mapId, WDT wdt, WMOManager wmo this.archive = archive; string wdtfile = Path.Join("World", "Maps", pathName, pathName + ".wdt"); - string tempFile = Path.Join(dataConfig.PPather, "wdt.tmp"); //wdt - if (!archive.SFileExtractFile(wdtfile, tempFile)) - return; - - using Stream stream = File.OpenRead(tempFile); + using MpqFileStream mpq = archive.GetStream(wdtfile); + using MemoryStream stream = new(mpq.ReadAllBytes()); using BinaryReader file = new(stream); List gwmos = new(); @@ -535,14 +510,10 @@ public void LoadMapTile(int x, int y, int index) return; string filename = Path.Join("World", "Maps", pathName, $"{pathName}_{x}_{y}.adt"); - string tempFile = Path.Join(dataConfig.PPather, "adt.tmp"); //adt - if (!archive.SFileExtractFile(filename, tempFile)) - return; - if (logger.IsEnabled(LogLevel.Trace)) logger.LogTrace($"Reading adt: {filename}"); - wdt.maptiles[index] = MapTileFile.Read(tempFile, wmomanager, modelmanager); + wdt.maptiles[index] = MapTileFile.Read(archive, filename, wmomanager, modelmanager); wdt.loaded[index] = true; } @@ -675,7 +646,7 @@ internal static class MapTileFile // adt file private static readonly LiquidData eLiquidData = new(0, 0, 0, EmptyMH2OData1, Array.Empty(), Array.Empty()); public static ref readonly LiquidData EmptyLiquidData => ref eLiquidData; - public static MapTile Read(string name, WMOManager wmomanager, ModelManager modelmanager) + public static MapTile Read(ArchiveSet archive, string name, WMOManager wmomanager, ModelManager modelmanager) { LiquidData[] LiquidDataChunk = Array.Empty(); Span mcnk_offsets = stackalloc int[MapTile.SIZE * MapTile.SIZE]; @@ -690,7 +661,8 @@ public static MapTile Read(string name, WMOManager wmomanager, ModelManager mode MapChunk[] chunks = new MapChunk[MapTile.SIZE * MapTile.SIZE]; BitArray hasChunk = new(chunks.Length); - using Stream stream = File.OpenRead(name); + using MpqFileStream mpq = archive.GetStream(name); + using MemoryStream stream = new(mpq.ReadAllBytes()); using BinaryReader file = new(stream); do @@ -1037,7 +1009,11 @@ private static MapChunk ReadMapChunk(BinaryReader file, in LiquidData liquidData } } - file.BaseStream.Seek(curpos + size, SeekOrigin.Begin); + // TODO: MemoryStream.Seek offset + // Reading adt: World\Maps\Northrend\Northrend_36_21.adt + // Zul'Drak stairs + file.BaseStream.Seek(Math.Min(curpos + size, file.BaseStream.Length), SeekOrigin.Begin); + //file.BaseStream.Seek(curpos + size, SeekOrigin.Begin); } while (!file.EOF()); //set liquid info from the MH2O chunk since the old MCLQ is no more @@ -1102,9 +1078,10 @@ private static void HandleChunkMCLQ(BinaryReader file, out float water_height1, internal sealed class WmoRootFile { - public WmoRootFile(string name, WMO wmo, ModelManager modelmanager) + public WmoRootFile(ArchiveSet archive, string name, WMO wmo, ModelManager modelmanager) { - using Stream stream = File.OpenRead(name); + using MpqFileStream mpq = archive.GetStream(name); + using MemoryStream stream = new(mpq.ReadAllBytes()); using BinaryReader file = new(stream); do @@ -1247,9 +1224,10 @@ private static void HandleMOGI(BinaryReader file, WMO wmo, uint size) internal sealed class WmoGroupFile { - public WmoGroupFile(WMOGroup g, string name) + public WmoGroupFile(ArchiveSet archive, string name, WMOGroup g) { - using Stream stream = File.OpenRead(name); + using MpqFileStream mpq = archive.GetStream(name); + using MemoryStream stream = new(mpq.ReadAllBytes()); using BinaryReader file = new(stream); file.BaseStream.Seek(0x14, SeekOrigin.Begin); From 2e64f85d36427759a0dc11933470a30e277e3069 Mon Sep 17 00:00:00 2001 From: Xian55 <367101+Xian55@users.noreply.github.com> Date: Fri, 1 Sep 2023 13:02:25 +0200 Subject: [PATCH 2/2] PPather: Archive: remove nullable --- PPather/StormDll/Archive.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/PPather/StormDll/Archive.cs b/PPather/StormDll/Archive.cs index 0678e2260..b59cfd16f 100644 --- a/PPather/StormDll/Archive.cs +++ b/PPather/StormDll/Archive.cs @@ -4,8 +4,6 @@ namespace StormDll; -#nullable enable - internal sealed class Archive { public const uint SFILE_INVALID_SIZE = 0xFFFFFFFF;