From 6b48164b973a87b4d45b9d77636abc8341d912d5 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sun, 2 Nov 2025 21:07:51 -0500 Subject: [PATCH 1/8] Remove StormLibSharp as submodule --- .gitmodules | 3 --- SabreTools.Serialization/_EXTERNAL/stormlibsharp | 1 - 2 files changed, 4 deletions(-) delete mode 160000 SabreTools.Serialization/_EXTERNAL/stormlibsharp diff --git a/.gitmodules b/.gitmodules index 9b45d6a2..e69de29b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "SabreTools.Serialization/_EXTERNAL/stormlibsharp"] - path = SabreTools.Serialization/_EXTERNAL/stormlibsharp - url = https://github.com/robpaveza/stormlibsharp.git diff --git a/SabreTools.Serialization/_EXTERNAL/stormlibsharp b/SabreTools.Serialization/_EXTERNAL/stormlibsharp deleted file mode 160000 index 3bf13753..00000000 --- a/SabreTools.Serialization/_EXTERNAL/stormlibsharp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3bf13753171d8d1e430f5f044fcbc10b1fd7089f From 1378b87ea67c902e764bec5e61c34491d06de12b Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sun, 2 Nov 2025 21:11:39 -0500 Subject: [PATCH 2/8] Directly include StormLibSharp code --- .../_EXTERNAL/CascLibSharp/CascApi.cs | 211 ++++++++ .../_EXTERNAL/CascLibSharp/CascException.cs | 56 ++ .../_EXTERNAL/CascLibSharp/CascFileStream.cs | 168 ++++++ .../_EXTERNAL/CascLibSharp/CascFoundFile.cs | 62 +++ .../CascLibSharp/CascStorageContext.cs | 403 ++++++++++++++ .../Native/CascFileEnumerationSafeHandle.cs | 32 ++ .../Native/CascStorageFileSafeHandle.cs | 36 ++ .../Native/CascStorageSafeHandle.cs | 36 ++ .../CascLibSharp/Native/CoTaskMem.cs | 64 +++ .../CascLibSharp/Native/NativeMethods.cs | 124 +++++ SabreTools.Serialization/_EXTERNAL/README.MD | 5 +- .../_EXTERNAL/StormLibSharp/MpqArchive.cs | 414 +++++++++++++++ .../MpqArchiveCompactingEventArgs.cs | 49 ++ .../_EXTERNAL/StormLibSharp/MpqFileStream.cs | 185 +++++++ .../StormLibSharp/Native/Callbacks.cs | 17 + .../Native/MpqArchiveSafeHandle.cs | 26 + .../StormLibSharp/Native/MpqFileSafeHandle.cs | 27 + .../StormLibSharp/Native/NativeMethods.cs | 497 ++++++++++++++++++ .../StormLibSharp/Native/SFileInfoClass.cs | 70 +++ .../Native/SFileOpenArchiveFlags.cs | 26 + .../StormLibSharp/Native/Win32Methods.cs | 60 +++ 21 files changed, 2567 insertions(+), 1 deletion(-) create mode 100644 SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascApi.cs create mode 100644 SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascException.cs create mode 100644 SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascFileStream.cs create mode 100644 SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascFoundFile.cs create mode 100644 SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascStorageContext.cs create mode 100644 SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CascFileEnumerationSafeHandle.cs create mode 100644 SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CascStorageFileSafeHandle.cs create mode 100644 SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CascStorageSafeHandle.cs create mode 100644 SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CoTaskMem.cs create mode 100644 SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/NativeMethods.cs create mode 100644 SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqArchive.cs create mode 100644 SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqArchiveCompactingEventArgs.cs create mode 100644 SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqFileStream.cs create mode 100644 SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/Callbacks.cs create mode 100644 SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/MpqArchiveSafeHandle.cs create mode 100644 SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/MpqFileSafeHandle.cs create mode 100644 SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/NativeMethods.cs create mode 100644 SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/SFileInfoClass.cs create mode 100644 SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/SFileOpenArchiveFlags.cs create mode 100644 SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/Win32Methods.cs diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascApi.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascApi.cs new file mode 100644 index 00000000..d3e1390b --- /dev/null +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascApi.cs @@ -0,0 +1,211 @@ +using CascLibSharp.Native; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace CascLibSharp +{ + internal sealed class CascApi + { + #region Imported method signature type definitions + [return: MarshalAs(UnmanagedType.I1)] + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + internal delegate bool FnCascOpenStorage( + [MarshalAs(UnmanagedType.LPTStr)] string szDataPath, + uint dwFlags, + out CascStorageSafeHandle phStorage); + [return: MarshalAs(UnmanagedType.I1)] + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + internal delegate bool FnCascGetStorageInfo( + CascStorageSafeHandle hStorage, + CascStorageInfoClass infoClass, + ref uint pvStorageInfo, + IntPtr cbStorageInfo, + ref uint pcbLengthNeeded); + [return: MarshalAs(UnmanagedType.I1)] + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + internal delegate bool FnCascCloseStorage( IntPtr hStorage); + + [return: MarshalAs(UnmanagedType.I1)] + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + internal delegate bool FnCascOpenFileByIndexKey( + CascStorageSafeHandle hStorage, + ref QueryKey pIndexKey, + uint dwFlags, + out CascStorageFileSafeHandle phFile); + [return: MarshalAs(UnmanagedType.I1)] + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + internal delegate bool FnCascOpenFileByEncodingKey( + CascStorageSafeHandle hStorage, + ref QueryKey pEncodingKey, + uint dwFlags, + out CascStorageFileSafeHandle phFile); + [return: MarshalAs(UnmanagedType.I1)] + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + internal delegate bool FnCascOpenFile( + CascStorageSafeHandle hStorage, + [MarshalAs(UnmanagedType.LPStr)] string szFileName, + uint dwLocale, + uint dwFlags, + out CascStorageFileSafeHandle phFile); + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + internal delegate uint FnCascGetFileSize( + CascStorageFileSafeHandle hFile, + out uint pdwFileSizeHigh); + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + internal delegate uint FnCascSetFilePointer( + CascStorageFileSafeHandle hFile, + uint lFilePos, + ref uint plFilePosHigh, + SeekOrigin dwMoveMethod); + [return: MarshalAs(UnmanagedType.I1)] + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + internal delegate bool FnCascReadFile( + CascStorageFileSafeHandle hFile, + IntPtr lpBuffer, + uint dwToRead, + out uint dwRead); + [return: MarshalAs(UnmanagedType.I1)] + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + internal delegate bool FnCascCloseFile( IntPtr hFile); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + internal delegate CascFileEnumerationSafeHandle FnCascFindFirstFile( + CascStorageSafeHandle hStorage, + [MarshalAs(UnmanagedType.LPStr)] string szMask, + ref CascFindData pFindData, + [MarshalAs(UnmanagedType.LPTStr)] string szListFile); + [return: MarshalAs(UnmanagedType.I1)] + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + internal delegate bool FnCascFindNextFile( + CascFileEnumerationSafeHandle hFind, + ref CascFindData pFindData); + [return: MarshalAs(UnmanagedType.I1)] + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + internal delegate bool FnCascFindClose( IntPtr hFind); + #endregion + + #region Field and method defs + public readonly FnCascOpenStorage CascOpenStorage; + public readonly FnCascGetStorageInfo CascGetStorageInfo; + public readonly FnCascCloseStorage CascCloseStorage; + + public readonly FnCascOpenFileByIndexKey CascOpenFileByIndexKey; + public readonly FnCascOpenFileByEncodingKey CascOpenFileByEncodingKey; + public readonly FnCascOpenFile CascOpenFile; + + public readonly FnCascGetFileSize CascGetFileSizeBase; + public long CascGetFileSize(CascStorageFileSafeHandle hFile) + { + uint high; + uint low = CascGetFileSizeBase(hFile, out high); + long result = (high << 32) | low; + + return result; + } + public readonly FnCascSetFilePointer CascSetFilePointerBase; + public long CascSetFilePointer(CascStorageFileSafeHandle hFile, long filePos, SeekOrigin moveMethod) + { + uint low, high; + unchecked + { + low = (uint)(filePos & 0xffffffff); + high = (uint)(((ulong)filePos & 0xffffffff00000000) >> 32); + } + + low = CascSetFilePointerBase(hFile, low, ref high, moveMethod); + + long result = (high << 32) | low; + + return result; + } + public readonly FnCascReadFile CascReadFile; + public readonly FnCascCloseFile CascCloseFile; + + public readonly FnCascFindFirstFile CascFindFirstFile; + public readonly FnCascFindNextFile CascFindNextFile; + public readonly FnCascFindClose CascFindClose; + #endregion + + internal CascApi(IntPtr hModule) + { + SetFn(ref CascOpenStorage, hModule, "CascOpenStorage"); + SetFn(ref CascGetStorageInfo, hModule, "CascGetStorageInfo"); + SetFn(ref CascCloseStorage, hModule, "CascCloseStorage"); + + SetFn(ref CascOpenFileByIndexKey, hModule, "CascOpenFileByIndexKey"); + SetFn(ref CascOpenFileByEncodingKey, hModule, "CascOpenFileByEncodingKey"); + SetFn(ref CascOpenFile, hModule, "CascOpenFile"); + SetFn(ref CascGetFileSizeBase, hModule, "CascGetFileSize"); + SetFn(ref CascSetFilePointerBase, hModule, "CascSetFilePointer"); + SetFn(ref CascReadFile, hModule, "CascReadFile"); + SetFn(ref CascCloseFile, hModule, "CascCloseFile"); + + SetFn(ref CascFindFirstFile, hModule, "CascFindFirstFile"); + SetFn(ref CascFindNextFile, hModule, "CascFindNextFile"); + SetFn(ref CascFindClose, hModule, "CascFindClose"); + } + + private static void SetFn(ref T target, IntPtr hModule, string procName) + where T : class + { + IntPtr procAddr = NativeMethods.GetProcAddress(hModule, procName); + if (procAddr == IntPtr.Zero) + throw new Win32Exception(); + target = Marshal.GetDelegateForFunctionPointer(procAddr, typeof(T)) as T; + } + + private static CascApi Load() + { + string myPath = Assembly.GetExecutingAssembly().Location; + string directory = Path.GetDirectoryName(myPath); + string arch = IntPtr.Size == 8 ? "x64" : "x86"; +#if DEBUG + string build = "dbg"; +#else + string build = "fre"; +#endif + + string mainPath = Path.Combine(directory, "CascLib", build, arch, "CascLib.dll"); + if (File.Exists(mainPath)) + return FromFile(mainPath); + + string alternatePath = Path.Combine(directory, "CascLib", arch, "CascLib.dll"); + if (File.Exists(mainPath)) + return FromFile(alternatePath); + + string localPath = Path.Combine(directory, "CascLib.dll"); + if (File.Exists(localPath)) + return FromFile(localPath); + + throw new FileNotFoundException(string.Format("Could not locate a copy of CascLib.dll to load. The following paths were tried:\n\t{0}\n\t{1}\n\t{2}\n\nEnsure that an architecture-appropriate copy of CascLib.dll is included in your project.", mainPath, alternatePath, localPath)); + } + + private static Lazy _sharedInstance = new Lazy(Load); + public static CascApi Instance + { + get + { + return _sharedInstance.Value; + } + } + + public static CascApi FromFile(string filePath) + { + if (!File.Exists(filePath)) + throw new FileNotFoundException("The CascLib.dll library could not be loaded at the specified path.", filePath); + + IntPtr hMod = NativeMethods.LoadLibraryEx(filePath, IntPtr.Zero, 0); + if (hMod == IntPtr.Zero) + throw new Win32Exception(); + + return new CascApi(hMod); + } + } +} diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascException.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascException.cs new file mode 100644 index 00000000..d6a3dec9 --- /dev/null +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascException.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace CascLibSharp +{ + /// + /// Represents an exception raised as part of a CASC operation. + /// + public class CascException : Win32Exception + { + /// + /// Creates a new CascException based on the last Win32 error. + /// + public CascException() + : base(Marshal.GetLastWin32Error()) + { + + } + + /// + /// Creates a new CascException based on the specified Win32 error code. + /// + /// A Win32 error code. + public CascException(int errorCode) + : base(errorCode) + { + + } + + /// + /// Creates a new CascException based on the specified message. + /// + /// The error message. + public CascException(string message) + : base(message) + { + + } + + /// + /// Creates a new CascException based on the specified Win32 error code and custom error message. + /// + /// A Win32 error code. + /// The error message. + public CascException(int errorCode, string message) + : base(errorCode, message) + { + + } + } +} diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascFileStream.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascFileStream.cs new file mode 100644 index 00000000..2a197720 --- /dev/null +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascFileStream.cs @@ -0,0 +1,168 @@ +using CascLibSharp.Native; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CascLibSharp +{ + /// + /// Provides a view of the data in a file contained within CASC storage. + /// + public class CascFileStream : Stream + { + private CascStorageFileSafeHandle _handle; + private CascApi _api; + + internal CascFileStream(CascStorageFileSafeHandle handle, CascApi api) + { + Debug.Assert(handle != null); + Debug.Assert(!handle.IsInvalid); + Debug.Assert(api != null); + + _api = api; + _handle = handle; + } + + private void AssertValidHandle() + { + if (_handle == null || _handle.IsInvalid) + throw new ObjectDisposedException("CascFileStream"); + } + + /// + /// Gets whether this Stream may be read. Always returns true as long as the Stream has not been disposed. + /// + public override bool CanRead + { + get { return _handle != null && !_handle.IsInvalid; } + } + + /// + /// Gets whether this Stream may seek. Always returns true as long as the Stream has not been disposed. + /// + public override bool CanSeek + { + get { return _handle != null && !_handle.IsInvalid; } + } + + /// + /// Gets whether this Stream may write. Always returns false. + /// + public override bool CanWrite + { + get { return false; } + } + + /// + /// Flushes writes to the backing store. This method always throws because writing is not supported. + /// + /// Always thrown. + public override void Flush() + { + throw new NotSupportedException(); + } + + /// + /// Gets the length of the Stream. + /// + /// Thrown if the Stream has been disposed. + public override long Length + { + get + { + AssertValidHandle(); + return _api.CascGetFileSize(_handle); + } + } + + /// + /// Gets or sets the current position within the Stream. + /// + /// Thrown if the Stream has been disposed. + /// Thrown if the attempt to seek fails. + public override long Position + { + get + { + AssertValidHandle(); + return _api.CascSetFilePointer(_handle, 0, SeekOrigin.Current); + } + set + { + AssertValidHandle(); + long result = _api.CascSetFilePointer(_handle, value, SeekOrigin.Begin); + if (result != value) + throw new CascException(); + } + } + + /// + /// Reads data into the buffer. + /// + /// The destination into which the data should be read. + /// The offset into the buffer to read from the current position of the Stream. + /// The number of bytes to read. + /// The number of bytes actually read. + /// Thrown if the Stream has been disposed. + /// Thrown if the CASC provider fails to read. + public override unsafe int Read(byte[] buffer, int offset, int count) + { + AssertValidHandle(); + long len = Length; + if (offset < 0 || offset > len) + throw new ArgumentException("offset"); + if (count < 0) + throw new ArgumentException("count"); + if (offset + count > len) + throw new ArgumentException("count"); + + uint read = 0; + fixed (byte* pBuffer = &buffer[0]) + { + if (!_api.CascReadFile(_handle, new IntPtr((void*)pBuffer), unchecked((uint)count), out read)) + throw new CascException(); + } + + return unchecked((int)read); + } + + /// + /// Seeks to a specific position within the Stream. + /// + /// The offset from the specified origin to seek. + /// The relative location (beginning, current, or end) from which to seek. + /// The new position in the stream. + /// Thrown if the Stream has been disposed. + public override long Seek(long offset, SeekOrigin origin) + { + AssertValidHandle(); + return _api.CascSetFilePointer(_handle, offset, origin); + } + + /// + /// Sets the length of the Stream. Always throws because writing is not supported. + /// + /// The new length of the Stream. + /// Always thrown. + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + /// + /// Writes the specified data to the Stream. Always throws because writing is not supported. + /// + /// The data to write. + /// The starting position in the buffer. + /// The number of bytes. + /// Always thrown. + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + } +} diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascFoundFile.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascFoundFile.cs new file mode 100644 index 00000000..af3594e4 --- /dev/null +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascFoundFile.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace CascLibSharp +{ + /// + /// Represents a file found in a CASC container search. + /// + public class CascFoundFile + { + private WeakReference _ownerContext; + + internal CascFoundFile(string fileName, IntPtr plainName, byte[] encodingKey, CascLocales locales, long fileSize, CascStorageContext ownerContext) + { + FileName = fileName; + PlainFileName = Marshal.PtrToStringAnsi(plainName); + EncodingKey = encodingKey; + Locales = locales; + FileSize = fileSize; + + _ownerContext = new WeakReference(ownerContext); + } + + /// + /// Gets the full path to this file. + /// + public string FileName { get; private set; } + /// + /// Gets the plain (no directory-qualified) file name of this file. + /// + public string PlainFileName { get; private set; } + /// + /// Gets the CASC encoding key for this file. + /// + public byte[] EncodingKey { get; private set; } + /// + /// Gets the locales supported by this resource. + /// + public CascLocales Locales { get; private set; } + /// + /// Gets the length of the file in bytes. + /// + public long FileSize { get; private set; } + + /// + /// Opens the found file for reading. + /// + /// A CascFileStream, which acts as a Stream for a CASC stored file. + public CascFileStream Open() + { + CascStorageContext context; + if (!_ownerContext.TryGetTarget(out context)) + throw new ObjectDisposedException("The owning context has been closed."); + + return context.OpenFileByEncodingKey(EncodingKey); + } + } +} diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascStorageContext.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascStorageContext.cs new file mode 100644 index 00000000..7dbae534 --- /dev/null +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascStorageContext.cs @@ -0,0 +1,403 @@ +using CascLibSharp.Native; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace CascLibSharp +{ + /// + /// Represents a CASC storage directory. + /// + public class CascStorageContext : IDisposable + { + private CascApi _api; + private CascStorageSafeHandle _handle; + private Lazy _hasListfile; + private Lazy _clientType; + private Lazy _fileCount; + private Lazy _gameBuild; + + /// + /// Creates a new CascStorageContext for the specified path. + /// + /// The path to a game's data directory. + /// An example directory is c:\Program Files (x86)\Heroes of the Storm\HeroesData. + public CascStorageContext(string dataPath) + { + _api = CascApi.Instance; + + if (!_api.CascOpenStorage(dataPath, 0, out _handle) || _handle.IsInvalid) + throw new CascException(); + _handle.Api = _api; + + _hasListfile = new Lazy(CheckHasListfile); + _clientType = new Lazy(GetClient); + _fileCount = new Lazy(GetFileCount); + _gameBuild = new Lazy(GetGameBuild); + } + + /// + /// Opens a file by its fully-qualified name. + /// + /// The name of the file. + /// The file's locale (defaults to English-United States). + /// A CascFileStream, which implements a Stream. + /// Thrown if the CascStorageContext has been disposed. + /// Thrown if the file does not exist within the CASC storage container. + public CascFileStream OpenFile(string fileName, CascLocales locale = CascLocales.EnUs) + { + if (_handle == null || _handle.IsInvalid) + throw new ObjectDisposedException("CascStorageContext"); + + CascStorageFileSafeHandle hFile; + if (!_api.CascOpenFile(_handle, fileName, (uint)locale, 0, out hFile)) + throw new CascException(); + + hFile.Api = _api; + + return new CascFileStream(hFile, _api); + } + + /// + /// Opens a file by its index key. + /// + /// + /// An index key is a binary representation of a file. I do not know what it comes from; I know that it's used to identify files inside of CASC, + /// but I don't know how someone obtains the index key. They are not produced in the public API of CascLib. + /// + /// The index key to search. + /// A CascFileStream, which implements a Stream. + /// Thrown if the CascStorageContext has been disposed. + /// Thrown if the file does not exist within the CASC storage container. + public CascFileStream OpenFileByIndexKey(byte[] indexKey) + { + if (_handle == null || _handle.IsInvalid) + throw new ObjectDisposedException("CascStorageContext"); + + using (CoTaskMem mem = CoTaskMem.FromBytes(indexKey)) + { + QueryKey qk = new QueryKey(); + qk.cbData = unchecked((uint)indexKey.Length); + qk.pbData = mem.Pointer; + + CascStorageFileSafeHandle hFile; + if (!_api.CascOpenFileByIndexKey(_handle, ref qk, 0, out hFile)) + throw new CascException(); + + hFile.Api = _api; + + return new CascFileStream(hFile, _api); + } + } + + /// + /// Opens a file by its encoding key. + /// + /// A 16-byte key representing the file. Encoding keys may be obtained via . + /// A CascFileStream, which implements a Stream. + /// Thrown if the CascStorageContext has been disposed. + /// Thrown if the file does not exist within the CASC storage container. + public CascFileStream OpenFileByEncodingKey(byte[] encodingKey) + { + if (_handle == null || _handle.IsInvalid) + throw new ObjectDisposedException("CascStorageContext"); + + using (CoTaskMem mem = CoTaskMem.FromBytes(encodingKey)) + { + QueryKey qk = new QueryKey(); + qk.cbData = unchecked((uint)encodingKey.Length); + qk.pbData = mem.Pointer; + + CascStorageFileSafeHandle hFile; + if (!_api.CascOpenFileByEncodingKey(_handle, ref qk, 0, out hFile)) + throw new CascException(); + + hFile.Api = _api; + + return new CascFileStream(hFile, _api); + } + } + + /// + /// Searches the files in the CASC container for files that match the specified pattern. + /// + /// The mask to search. * and ? are valid tokens for substitution. + /// A path to a listfile. Required if the CASC container is for World of Warcraft. + /// An enumeration of matching file references in the CASC container. + public IEnumerable SearchFiles(string mask, string listFilePath = null) + { + if (_handle == null || _handle.IsInvalid) + throw new ObjectDisposedException("CascStorageContext"); + + if (this.GameClient == CascKnownClient.WorldOfWarcraft && string.IsNullOrWhiteSpace(listFilePath)) + throw new ArgumentNullException("listFilePath"); + + CascFindData cfd = new CascFindData(); + using (var handle = _api.CascFindFirstFile(_handle, mask, ref cfd, listFilePath)) + { + if (handle.IsInvalid) + yield break; + + handle.Api = _api; + + yield return cfd.ToFoundFile(this); + + while (_api.CascFindNextFile(handle, ref cfd)) + { + yield return cfd.ToFoundFile(this); + } + } + } + + private bool CheckHasListfile() + { + if (_handle == null || _handle.IsInvalid) + throw new ObjectDisposedException("CascStorageContext"); + + uint storageInfo = 0, lengthNeeded = 4; + if (!_api.CascGetStorageInfo(_handle, CascStorageInfoClass.Features, ref storageInfo, new IntPtr(4), ref lengthNeeded)) + throw new CascException(); + + CascStorageFeatures features = (CascStorageFeatures)storageInfo; + if (features.HasFlag(CascStorageFeatures.HasListfile)) + return true; + + return false; + } + + private CascKnownClient GetClient() + { + if (_handle == null || _handle.IsInvalid) + throw new ObjectDisposedException("CascStorageContext"); + + uint storageInfo = 0, lengthNeeded = 4; + if (!_api.CascGetStorageInfo(_handle, CascStorageInfoClass.GameInfo, ref storageInfo, new IntPtr(4), ref lengthNeeded)) + throw new CascException(); + + CascGameId gameId = (CascGameId)storageInfo; + + return gameId.ToKnownClient(); + } + + private long GetFileCount() + { + if (_handle == null || _handle.IsInvalid) + throw new ObjectDisposedException("CascStorageContext"); + + uint storageInfo = 0, lengthNeeded = 4; + if (!_api.CascGetStorageInfo(_handle, CascStorageInfoClass.Features, ref storageInfo, new IntPtr(4), ref lengthNeeded)) + throw new CascException(); + + return storageInfo; + } + + private int GetGameBuild() + { + if (_handle == null || _handle.IsInvalid) + throw new ObjectDisposedException("CascStorageContext"); + + uint storageInfo = 0, lengthNeeded = 4; + if (!_api.CascGetStorageInfo(_handle, CascStorageInfoClass.Features, ref storageInfo, new IntPtr(4), ref lengthNeeded)) + throw new CascException(); + + return unchecked((int)storageInfo); + } + + /// + /// Gets whether the CASC container has a listfile or if one must be supplied while searching. + /// + public bool HasListfile + { + get + { + return _hasListfile.Value; + } + } + + /// + /// Gets the number of files in the CASC container. + /// + public long FileCount + { + get + { + return _fileCount.Value; + } + } + + /// + /// Gets the build number of the game. + /// + public int GameBuild + { + get + { + return _gameBuild.Value; + } + } + + /// + /// Gets the game client of the container, if it can be determined. + /// + public CascKnownClient GameClient + { + get + { + return _clientType.Value; + } + } + + #region IDisposable implementation + /// + /// Finalizes the storage context. + /// + ~CascStorageContext() + { + Dispose(false); + } + + /// + /// Disposes the object. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Disposes the object, cleaning up the unmanaged objects. + /// + /// True if this is being called via the Dispose() method; false if it's being called by the finalizer. + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (!_handle.IsInvalid) + { + _handle.Close(); + _handle = null; + } + } + } + #endregion + } + + /// + /// Known locales supported by CASC. + /// + [Flags] + public enum CascLocales + { + /// + /// All available locales. + /// + All = -1, + /// + /// No locales. + /// + None = 0, + /// + /// Unknown + /// + Unknown1 = 1, + /// + /// English, United States + /// + EnUs = 2, + /// + /// Korean, South Korea + /// + KoKr = 4, + /// + /// Reserved (unknown) + /// + Reserved = 8, + /// + /// French, France + /// + FrFr = 0x10, + /// + /// German, Germany + /// + DeDe = 0x20, + /// + /// Chinese, China + /// + ZhCn = 0x40, + /// + /// Spanish, Spain + /// + EsEs = 0x80, + /// + /// Chinese, Taiwan + /// + ZhTw = 0x100, + /// + /// English, Great Britain + /// + EnGb = 0x200, + /// + /// English, China + /// + EnCn = 0x400, + /// + /// English, Taiwan + /// + EnTw = 0x800, + /// + /// Spanish, Mexico + /// + EsMx = 0x1000, + /// + /// Russian, Russia + /// + RuRu = 0x2000, + /// + /// Portuguese, Brazil + /// + PtBr = 0x4000, + /// + /// Italian, Italy + /// + ItIt = 0x8000, + /// + /// Portuguese, Portugal + /// + PtPt = 0x10000, + } + + /// + /// Known clients supporting CASC + /// + public enum CascKnownClient + { + /// + /// The game client was unrecognized. + /// + Unknown = -1, + /// + /// Heroes of the Storm + /// + HeroesOfTheStorm = 0, + /// + /// Diablo 3 + /// + Diablo3 = 1, + /// + /// World of Warcraft + /// + WorldOfWarcraft = 2, + /// + /// Overwatch + /// + Overwatch = 3, + /// + /// Starcraft 2 + /// + Starcraft2 = 4, + } +} diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CascFileEnumerationSafeHandle.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CascFileEnumerationSafeHandle.cs new file mode 100644 index 00000000..a673cf70 --- /dev/null +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CascFileEnumerationSafeHandle.cs @@ -0,0 +1,32 @@ +using Microsoft.Win32.SafeHandles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CascLibSharp.Native +{ + internal class CascFileEnumerationSafeHandle : SafeHandleZeroOrMinusOneIsInvalid + { + public CascFileEnumerationSafeHandle() + : base(true) { } + public CascFileEnumerationSafeHandle(IntPtr handle) + : this() + { + SetHandle(handle); + } + + protected override bool ReleaseHandle() + { + var api = Api ?? CascApi.Instance; + return api.CascFindClose(DangerousGetHandle()); + } + + internal CascApi Api + { + get; + set; + } + } +} diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CascStorageFileSafeHandle.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CascStorageFileSafeHandle.cs new file mode 100644 index 00000000..b6d2cc67 --- /dev/null +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CascStorageFileSafeHandle.cs @@ -0,0 +1,36 @@ +using Microsoft.Win32.SafeHandles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CascLibSharp.Native +{ + internal class CascStorageFileSafeHandle : SafeHandleZeroOrMinusOneIsInvalid + { + public CascStorageFileSafeHandle() + : base(true) + { + + } + + public CascStorageFileSafeHandle(IntPtr handle) + : this() + { + SetHandle(handle); + } + + protected override bool ReleaseHandle() + { + var api = Api ?? CascApi.Instance; + return api.CascCloseFile(DangerousGetHandle()); + } + + internal CascApi Api + { + get; + set; + } + } +} diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CascStorageSafeHandle.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CascStorageSafeHandle.cs new file mode 100644 index 00000000..83361901 --- /dev/null +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CascStorageSafeHandle.cs @@ -0,0 +1,36 @@ +using Microsoft.Win32.SafeHandles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CascLibSharp.Native +{ + internal class CascStorageSafeHandle : SafeHandleZeroOrMinusOneIsInvalid + { + public CascStorageSafeHandle() + : base(true) + { + + } + + public CascStorageSafeHandle(IntPtr handle) + : this() + { + SetHandle(handle); + } + + protected override bool ReleaseHandle() + { + var api = Api ?? CascApi.Instance; + return api.CascCloseStorage(DangerousGetHandle()); + } + + internal CascApi Api + { + get; + set; + } + } +} diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CoTaskMem.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CoTaskMem.cs new file mode 100644 index 00000000..78ddf58c --- /dev/null +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CoTaskMem.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace CascLibSharp.Native +{ + internal sealed class CoTaskMem : IDisposable + { + private IntPtr _mem; + private int _size; + + public CoTaskMem(int size) + { + _mem = Marshal.AllocCoTaskMem(size); + if (_mem == IntPtr.Zero) + throw new OutOfMemoryException(); + + _size = size; + } + + public static CoTaskMem FromBytes(byte[] b) + { + CoTaskMem result = new CoTaskMem(b.Length); + Marshal.Copy(b, 0, result._mem, b.Length); + + return result; + } + + public IntPtr Pointer + { + get + { + if (_mem == IntPtr.Zero) + throw new ObjectDisposedException("CoTaskMem"); + + return _mem; + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (_mem != IntPtr.Zero) + { + Marshal.FreeCoTaskMem(_mem); + _mem = IntPtr.Zero; + _size = 0; + } + } + + ~CoTaskMem() + { + Dispose(false); + } + } +} diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/NativeMethods.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/NativeMethods.cs new file mode 100644 index 00000000..9bdf9409 --- /dev/null +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/NativeMethods.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace CascLibSharp.Native +{ + internal static class NativeMethods + { + [DllImport("kernel32", SetLastError = true)] + public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); + + [DllImport("kernel32", SetLastError = true)] + public static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags); + } + + internal enum CascStorageInfoClass + { + FileCount, + Features, + GameInfo, + GameBuild, + } + + internal enum CascStorageFeatures + { + None = 0x0, + HasListfile = 0x1, + } + + /* + * #define CASC_GAME_HOTS 0x00010000 // Heroes of the Storm +#define CASC_GAME_WOW6 0x00020000 // World of Warcraft - Warlords of Draenor +#define CASC_GAME_DIABLO3 0x00030000 // Diablo 3 since PTR 2.2.0 +#define CASC_GAME_OVERWATCH 0x00040000 // Overwatch since PTR 24919* + */ + internal enum CascGameId + { + Hots = 0x00010000, + Wow6 = 0x00020000, + Diablo3 = 0x00030000, + Overwatch = 0x00040000, + Starcraft2 = 0x00050000, + } + + internal static class GameConverterExtensions + { + private static Dictionary GameClientMap = new Dictionary + { + { CascGameId.Hots, CascKnownClient.HeroesOfTheStorm }, + { CascGameId.Wow6, CascKnownClient.WorldOfWarcraft }, + { CascGameId.Diablo3, CascKnownClient.Diablo3 }, + { CascGameId.Overwatch, CascKnownClient.Overwatch }, + { CascGameId.Starcraft2, CascKnownClient.Starcraft2 }, + }; + + private static Dictionary ClientGameMap = new Dictionary() + { + { CascKnownClient.HeroesOfTheStorm, CascGameId.Hots }, + { CascKnownClient.WorldOfWarcraft, CascGameId.Wow6 }, + { CascKnownClient.Diablo3, CascGameId.Diablo3 }, + { CascKnownClient.Overwatch, CascGameId.Overwatch }, + { CascKnownClient.Starcraft2, CascGameId.Starcraft2 }, + }; + + public static CascKnownClient ToKnownClient(this CascGameId gameId) + { + CascKnownClient result; + if (!GameClientMap.TryGetValue(gameId, out result)) + result = CascKnownClient.Unknown; + + return result; + } + + public static CascGameId ToGameId(this CascKnownClient knownClient) + { + CascGameId result; + if (!ClientGameMap.TryGetValue(knownClient, out result)) + throw new ArgumentException("Invalid client."); + + return result; + } + } + +#pragma warning disable 649 + internal struct QueryKey + { + public IntPtr pbData; + public uint cbData; + } + + internal unsafe struct CascFindData + { + const int MAX_PATH = 260; + + public fixed byte szFileName[MAX_PATH]; + public IntPtr szPlainName; + //public ulong FileNameHash; + public fixed byte EncodingKey[16]; + //public uint dwPackageIndex; + public uint dwLocaleFlags; + public uint dwFileSize; + + public unsafe CascFoundFile ToFoundFile(CascStorageContext ownerContext) + { + string fileName = null; + fixed (void* pFileName = szFileName) + { + fileName = Marshal.PtrToStringAnsi(new IntPtr(pFileName)); + } + byte[] encodingKey = new byte[16]; + fixed (void* pEncodingKey = EncodingKey) + { + Marshal.Copy(new IntPtr(pEncodingKey), encodingKey, 0, 16); + } + + return new CascFoundFile(fileName, szPlainName, encodingKey, (CascLocales)dwLocaleFlags, dwFileSize, ownerContext); + } + } +#pragma warning restore 649 +} diff --git a/SabreTools.Serialization/_EXTERNAL/README.MD b/SabreTools.Serialization/_EXTERNAL/README.MD index 74cbca55..06c3af9a 100644 --- a/SabreTools.Serialization/_EXTERNAL/README.MD +++ b/SabreTools.Serialization/_EXTERNAL/README.MD @@ -4,4 +4,7 @@ This directory contains multiple external libraries. Here is the status of each: | Directory | Library | Status | | --------- | ------- | ------ | -| stormlibsharp | [stormlibsharp](https://github.com/robpaveza/stormlibsharp) | External submodule | +| CascLibSharp | [stormlibsharp](https://github.com/robpaveza/stormlibsharp) | Direct code duplicate | +| StormLibSharp | [stormlibsharp](https://github.com/robpaveza/stormlibsharp) | Direct code duplicate | + +**CascLibSharp** and **StormLibSharp** have multiple changes made to accomodate modern .NET versions. The original code is MIT licensed. diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqArchive.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqArchive.cs new file mode 100644 index 00000000..4585dc50 --- /dev/null +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqArchive.cs @@ -0,0 +1,414 @@ +using StormLibSharp.Native; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.IO; +using System.IO.MemoryMappedFiles; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace StormLibSharp +{ + public class MpqArchive : IDisposable + { + private MpqArchiveSafeHandle _handle; + private List _openFiles = new List(); + private FileAccess _accessType; + private List _compactCallbacks = new List(); + private SFILE_COMPACT_CALLBACK _compactCallback; + + #region Constructors / Factories + public MpqArchive(string filePath, FileAccess accessType) + { + _accessType = accessType; + SFileOpenArchiveFlags flags = SFileOpenArchiveFlags.TypeIsFile; + if (accessType == FileAccess.Read) + flags |= SFileOpenArchiveFlags.AccessReadOnly; + else + flags |= SFileOpenArchiveFlags.AccessReadWriteShare; + + // constant 2 = SFILE_OPEN_HARD_DISK_FILE + if (!NativeMethods.SFileOpenArchive(filePath, 2, flags, out _handle)) + throw new Win32Exception(); // Implicitly calls GetLastError + } + + public MpqArchive(MemoryMappedFile file, FileAccess accessType) + { + _accessType = accessType; + string fileName = Win32Methods.GetFileNameOfMemoryMappedFile(file); + if (fileName == null) + throw new ArgumentException("Could not retrieve the name of the file to initialize."); + + SFileOpenArchiveFlags flags = SFileOpenArchiveFlags.TypeIsMemoryMapped; + if (accessType == FileAccess.Read) + flags |= SFileOpenArchiveFlags.AccessReadOnly; + else + flags |= SFileOpenArchiveFlags.AccessReadWriteShare; + + // constant 2 = SFILE_OPEN_HARD_DISK_FILE + if (!NativeMethods.SFileOpenArchive(fileName, 2, flags, out _handle)) + throw new Win32Exception(); // Implicitly calls GetLastError + } + + private MpqArchive(string filePath, MpqArchiveVersion version, MpqFileStreamAttributes listfileAttributes, MpqFileStreamAttributes attributesFileAttributes, int maxFileCount) + { + if (maxFileCount < 0) + throw new ArgumentException("maxFileCount"); + + SFileOpenArchiveFlags flags = SFileOpenArchiveFlags.TypeIsFile | SFileOpenArchiveFlags.AccessReadWriteShare; + flags |= (SFileOpenArchiveFlags)version; + + //SFILE_CREATE_MPQ create = new SFILE_CREATE_MPQ() + //{ + // cbSize = unchecked((uint)Marshal.SizeOf(typeof(SFILE_CREATE_MPQ))), + // dwMaxFileCount = unchecked((uint)maxFileCount), + // dwMpqVersion = (uint)version, + // dwFileFlags1 = (uint)listfileAttributes, + // dwFileFlags2 = (uint)attributesFileAttributes, + // dwStreamFlags = (uint)flags, + //}; + + //if (!NativeMethods.SFileCreateArchive2(filePath, ref create, out _handle)) + // throw new Win32Exception(); + if (!NativeMethods.SFileCreateArchive(filePath, (uint)flags, int.MaxValue, out _handle)) + throw new Win32Exception(); + } + + public static MpqArchive CreateNew(string mpqPath, MpqArchiveVersion version) + { + return CreateNew(mpqPath, version, MpqFileStreamAttributes.None, MpqFileStreamAttributes.None, int.MaxValue); + } + + public static MpqArchive CreateNew(string mpqPath, MpqArchiveVersion version, MpqFileStreamAttributes listfileAttributes, + MpqFileStreamAttributes attributesFileAttributes, int maxFileCount) + { + return new MpqArchive(mpqPath, version, listfileAttributes, attributesFileAttributes, maxFileCount); + } + #endregion + + #region Properties + // TODO: Move to common location. + // This is a global setting, not per-archive setting. + + //public int Locale + //{ + // get + // { + // throw new NotImplementedException(); + // } + // set + // { + // throw new NotImplementedException(); + // } + //} + + public long MaxFileCount + { + get + { + VerifyHandle(); + return NativeMethods.SFileGetMaxFileCount(_handle); + } + set + { + if (value < 0 || value > uint.MaxValue) + throw new ArgumentException("value"); + VerifyHandle(); + + if (!NativeMethods.SFileSetMaxFileCount(_handle, unchecked((uint)value))) + throw new Win32Exception(); + } + } + + private void VerifyHandle() + { + if (_handle == null || _handle.IsInvalid) + throw new ObjectDisposedException("MpqArchive"); + } + + public bool IsPatchedArchive + { + get + { + VerifyHandle(); + return NativeMethods.SFileIsPatchedArchive(_handle); + } + } + #endregion + + public void Flush() + { + VerifyHandle(); + if (!NativeMethods.SFileFlushArchive(_handle)) + throw new Win32Exception(); + } + + public int AddListFile(string listfileContents) + { + VerifyHandle(); + return NativeMethods.SFileAddListFile(_handle, listfileContents); + } + + public void AddFileFromDisk(string filePath, string archiveName) + { + VerifyHandle(); + + if (!NativeMethods.SFileAddFile(_handle, filePath, archiveName, 0)) + throw new Win32Exception(); + } + + public void Compact(string listfile) + { + VerifyHandle(); + if (!NativeMethods.SFileCompactArchive(_handle, listfile, false)) + throw new Win32Exception(); + } + + private void _OnCompact(IntPtr pvUserData, uint dwWorkType, ulong bytesProcessed, ulong totalBytes) + { + MpqArchiveCompactingEventArgs args = new MpqArchiveCompactingEventArgs(dwWorkType, bytesProcessed, totalBytes); + OnCompacting(args); + } + + protected virtual void OnCompacting(MpqArchiveCompactingEventArgs e) + { + foreach (var cb in _compactCallbacks) + { + cb(this, e); + } + } + + public event MpqArchiveCompactingEventHandler Compacting + { + add + { + VerifyHandle(); + _compactCallback = _OnCompact; + if (!NativeMethods.SFileSetCompactCallback(_handle, _compactCallback, IntPtr.Zero)) + throw new Win32Exception(); + + _compactCallbacks.Add(value); + } + remove + { + _compactCallbacks.Remove(value); + + VerifyHandle(); + if (_compactCallbacks.Count == 0) + { + if (!NativeMethods.SFileSetCompactCallback(_handle, null, IntPtr.Zero)) + { + // Don't do anything here. Remove shouldn't fail hard. + } + } + } + } + + // TODO: Determine if SFileGetAttributes/SFileSetAttributes/SFileUpdateFileAttributes deserves a projection. + // It's unclear - these seem to affect the (attributes) file but I can't figure out exactly what that means. + + public void AddPatchArchive(string patchPath) + { + VerifyHandle(); + + if (!NativeMethods.SFileOpenPatchArchive(_handle, patchPath, null, 0)) + throw new Win32Exception(); + } + + public void AddPatchArchives(IEnumerable patchPaths) + { + if (patchPaths == null) + throw new ArgumentNullException("patchPaths"); + + VerifyHandle(); + + foreach (string path in patchPaths) + { + // Don't sublet to AddPatchArchive to avoid having to repeatedly call VerifyHandle() + if (!NativeMethods.SFileOpenPatchArchive(_handle, path, null, 0)) + throw new Win32Exception(); + } + } + + public bool HasFile(string fileToFind) + { + VerifyHandle(); + + return NativeMethods.SFileHasFile(_handle, fileToFind); + } + + public MpqFileStream OpenFile(string fileName) + { + VerifyHandle(); + + MpqFileSafeHandle fileHandle; + if (!NativeMethods.SFileOpenFileEx(_handle, fileName, 0, out fileHandle)) + throw new Win32Exception(); + + MpqFileStream fs = new MpqFileStream(fileHandle, _accessType, this); + _openFiles.Add(fs); + return fs; + } + + public void ExtractFile(string fileToExtract, string destinationPath) + { + VerifyHandle(); + + if (!NativeMethods.SFileExtractFile(_handle, fileToExtract, destinationPath, 0)) + throw new Win32Exception(); + } + + public MpqFileVerificationResults VerifyFile(string fileToVerify) + { + VerifyHandle(); + + return (MpqFileVerificationResults)NativeMethods.SFileVerifyFile(_handle, fileToVerify, 0); + } + + // TODO: Consider SFileVerifyRawData + + public MpqArchiveVerificationResult VerifyArchive() + { + VerifyHandle(); + + return (MpqArchiveVerificationResult)NativeMethods.SFileVerifyArchive(_handle); + } + + + #region IDisposable implementation + public void Dispose() + { + Dispose(true); + } + + ~MpqArchive() + { + Dispose(false); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + // Release owned files first. + if (_openFiles != null) + { + foreach (var file in _openFiles) + { + file.Dispose(); + } + + _openFiles.Clear(); + _openFiles = null; + } + + // Release + if (_handle != null && !_handle.IsInvalid) + { + _handle.Close(); + _handle = null; + } + } + } + + internal void RemoveOwnedFile(MpqFileStream file) + { + _openFiles.Remove(file); + } + + #endregion + } + + public enum MpqArchiveVersion + { + Version1 = 0, + Version2 = 0x01000000, + Version3 = 0x02000000, + Version4 = 0x03000000, + } + + [Flags] + public enum MpqFileStreamAttributes + { + None = 0x0, + } + + [Flags] + public enum MpqFileVerificationResults + { + /// + /// There were no errors with the file. + /// + Verified = 0, + /// + /// Failed to open the file + /// + Error = 0x1, + /// + /// Failed to read all data from the file + /// + ReadError = 0x2, + /// + /// File has sector CRC + /// + HasSectorCrc = 0x4, + /// + /// Sector CRC check failed + /// + SectorCrcError = 0x8, + /// + /// File has CRC32 + /// + HasChecksum = 0x10, + /// + /// CRC32 check failed + /// + ChecksumError = 0x20, + /// + /// File has data MD5 + /// + HasMd5 = 0x40, + /// + /// MD5 check failed + /// + Md5Error = 0x80, + /// + /// File has raw data MD5 + /// + HasRawMd5 = 0x100, + /// + /// Raw MD5 check failed + /// + RawMd5Error = 0x200, + } + + public enum MpqArchiveVerificationResult + { + /// + /// There is no signature in the MPQ + /// + NoSignature = 0, + /// + /// There was an error during verifying signature (like no memory) + /// + VerificationFailed = 1, + /// + /// There is a weak signature and sign check passed + /// + WeakSignatureVerified = 2, + /// + /// There is a weak signature but sign check failed + /// + WeakSignatureFailed = 3, + /// + /// There is a strong signature and sign check passed + /// + StrongSignatureVerified = 4, + /// + /// There is a strong signature but sign check failed + /// + StrongSignatureFailed = 5, + } +} diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqArchiveCompactingEventArgs.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqArchiveCompactingEventArgs.cs new file mode 100644 index 00000000..c2a2ee7b --- /dev/null +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqArchiveCompactingEventArgs.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace StormLibSharp +{ + public delegate void MpqArchiveCompactingEventHandler(MpqArchive sender, MpqArchiveCompactingEventArgs e); + + public class MpqArchiveCompactingEventArgs : EventArgs + { + internal MpqArchiveCompactingEventArgs(uint dwWorkType, ulong processed, ulong total) + { + unchecked + { + WorkType = (MpqCompactingWorkType)dwWorkType; + BytesProcessed = (long)processed; + TotalBytes = (long)total; + } + } + + public MpqCompactingWorkType WorkType + { + get; + private set; + } + + public long BytesProcessed + { + get; + private set; + } + + public long TotalBytes + { + get; + private set; + } + } + + public enum MpqCompactingWorkType + { + CheckingFiles = 1, + CheckingHashTable = 2, + CopyingNonMpqData = 3, + CompactingFiles = 4, + ClosingArchive = 5, + } +} diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqFileStream.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqFileStream.cs new file mode 100644 index 00000000..18f6de25 --- /dev/null +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqFileStream.cs @@ -0,0 +1,185 @@ +using StormLibSharp.Native; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; + +namespace StormLibSharp +{ + public class MpqFileStream : Stream + { + private MpqFileSafeHandle _handle; + private FileAccess _accessType; + private MpqArchive _owner; + + internal MpqFileStream(MpqFileSafeHandle handle, FileAccess accessType, MpqArchive owner) + { + _handle = handle; + _accessType = accessType; + _owner = owner; + } + + private void VerifyHandle() + { + if (_handle == null || _handle.IsInvalid || _handle.IsClosed) + throw new ObjectDisposedException("MpqFileStream"); + } + + public override bool CanRead + { + get { VerifyHandle(); return true; } + } + + public override bool CanSeek + { + get { VerifyHandle(); return true; } + } + + public override bool CanWrite + { + get { VerifyHandle(); return _accessType != FileAccess.Read; } + } + + public override void Flush() + { + VerifyHandle(); + + _owner.Flush(); + } + + public override long Length + { + get + { + VerifyHandle(); + + uint high = 0; + uint low = NativeMethods.SFileGetFileSize(_handle, ref high); + + ulong val = (high << 32) | low; + return unchecked((long)val); + } + } + + public override long Position + { + get + { + VerifyHandle(); + + return NativeMethods.SFileGetFilePointer(_handle); + } + set + { + Seek(value, SeekOrigin.Begin); + } + } + + public override unsafe int Read(byte[] buffer, int offset, int count) + { + if (buffer == null) + throw new ArgumentNullException("buffer"); + if (offset > buffer.Length || (offset + count) > buffer.Length) + throw new ArgumentException(); + if (count < 0) + throw new ArgumentOutOfRangeException("count"); + + VerifyHandle(); + + bool success; + uint read; + fixed (byte* pb = &buffer[offset]) + { + NativeOverlapped overlapped = default(NativeOverlapped); + success = NativeMethods.SFileReadFile(_handle, new IntPtr(pb), unchecked((uint)count), out read, ref overlapped); + } + + if (!success) + { + int lastError = Win32Methods.GetLastError(); + if (lastError != 38) // EOF + throw new Win32Exception(lastError); + } + + return unchecked((int)read); + } + + public override long Seek(long offset, SeekOrigin origin) + { + VerifyHandle(); + + uint low, high; + low = unchecked((uint)(offset & 0xffffffffu)); + high = unchecked((uint)(offset >> 32)); + return NativeMethods.SFileSetFilePointer(_handle, low, ref high, (uint)origin); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override unsafe void Write(byte[] buffer, int offset, int count) + { + VerifyHandle(); + + if (buffer == null) + throw new ArgumentNullException("buffer"); + if (offset > buffer.Length || (offset + count) > buffer.Length) + throw new ArgumentException(); + if (count < 0) + throw new ArgumentOutOfRangeException("count"); + + VerifyHandle(); + + bool success; + fixed (byte* pb = &buffer[offset]) + { + success = NativeMethods.SFileWriteFile(_handle, new IntPtr(pb), unchecked((uint)count), 0u); + } + + if (!success) + throw new Win32Exception(); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (disposing) + { + if (_handle != null && !_handle.IsInvalid) + { + _handle.Close(); + _handle = null; + } + + if (_owner != null) + { + _owner.RemoveOwnedFile(this); + _owner = null; + } + } + } + + // TODO: Seems like the right place for SFileGetFileInfo, but will need to determine + // what value add these features have except for sophisticated debugging purposes + // (like in Ladis' MPQ Editor app). + + public int ChecksumCrc32 + { + get + { + throw new NotImplementedException(); + } + } + + public byte[] GetMd5Hash() + { + throw new NotImplementedException(); + } + } +} diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/Callbacks.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/Callbacks.cs new file mode 100644 index 00000000..58e7fa5e --- /dev/null +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/Callbacks.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace StormLibSharp.Native +{ + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + internal delegate void SFILE_DOWNLOAD_CALLBACK(IntPtr pvUserData, ulong byteOffset, uint dwTotalBytes); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + internal delegate void SFILE_COMPACT_CALLBACK(IntPtr pvUserData, uint dwWorkType, ulong bytesProcessed, ulong totalBytes); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + internal delegate void SFILE_ADDFILE_CALLBACK(IntPtr pvUserData, uint dwBytesWritte, uint dwTotalBytes, bool bFinalCall); +} diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/MpqArchiveSafeHandle.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/MpqArchiveSafeHandle.cs new file mode 100644 index 00000000..5e2df9c4 --- /dev/null +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/MpqArchiveSafeHandle.cs @@ -0,0 +1,26 @@ +using Microsoft.Win32.SafeHandles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace StormLibSharp.Native +{ + internal sealed class MpqArchiveSafeHandle : SafeHandleZeroOrMinusOneIsInvalid + { + public MpqArchiveSafeHandle(IntPtr handle) + : base(true) + { + this.SetHandle(handle); + } + + public MpqArchiveSafeHandle() + : base(true) { } + + protected override bool ReleaseHandle() + { + return NativeMethods.SFileCloseArchive(this.handle); + } + } +} diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/MpqFileSafeHandle.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/MpqFileSafeHandle.cs new file mode 100644 index 00000000..495dd129 --- /dev/null +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/MpqFileSafeHandle.cs @@ -0,0 +1,27 @@ +using Microsoft.Win32.SafeHandles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace StormLibSharp.Native +{ + internal sealed class MpqFileSafeHandle : SafeHandleZeroOrMinusOneIsInvalid + { + public MpqFileSafeHandle(IntPtr handle) + : base(true) + { + this.SetHandle(handle); + } + + public MpqFileSafeHandle() + : base(true) + { + } + + protected override bool ReleaseHandle() + { + return NativeMethods.SFileCloseFile(this.handle); + } + } +} diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/NativeMethods.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/NativeMethods.cs new file mode 100644 index 00000000..acef5fbc --- /dev/null +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/NativeMethods.cs @@ -0,0 +1,497 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace StormLibSharp.Native +{ + internal static class NativeMethods + { + private const string STORMLIB = "stormlib.dll"; + + #region Functions for manipulation with StormLib global flags + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern uint SFileGetLocale(); + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern uint SFileSetLocale(uint lcNewLocale); + #endregion + + #region Functions for archive manipulation + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileOpenArchive( + [MarshalAs(UnmanagedType.LPTStr)] string szMpqName, + uint dwPriority, + SFileOpenArchiveFlags dwFlags, + out MpqArchiveSafeHandle phMpq + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileCreateArchive( + [MarshalAs(UnmanagedType.LPTStr)] string szMpqName, + uint dwCreateFlags, + uint dwMaxFileCount, + out MpqArchiveSafeHandle phMpq + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileCreateArchive2( + [MarshalAs(UnmanagedType.LPTStr)] string szMpqName, + ref SFILE_CREATE_MPQ pCreateInfo, + out MpqArchiveSafeHandle phMpq + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileSetDownloadCallback( + MpqArchiveSafeHandle hMpq, + [MarshalAs(UnmanagedType.FunctionPtr)] SFILE_DOWNLOAD_CALLBACK pfnCallback, + IntPtr pvUserData + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileFlushArchive(MpqArchiveSafeHandle hMpq); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileCloseArchive(IntPtr hMpq); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileCloseArchive(MpqArchiveSafeHandle hMpq); + #endregion + + #region Adds another listfile into MPQ. + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern int SFileAddListFile( + MpqArchiveSafeHandle hMpq, + [MarshalAs(UnmanagedType.LPStr)] string szListFile + ); + #endregion + + #region Archive compacting + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileSetCompactCallback( + MpqArchiveSafeHandle hMpq, + SFILE_COMPACT_CALLBACK compactCB, + IntPtr pvUserData + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileCompactArchive( + MpqArchiveSafeHandle hMpq, + [MarshalAs(UnmanagedType.LPStr)] string szListFile, + bool bReserved + ); + #endregion + + #region Maximum file count + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern uint SFileGetMaxFileCount(MpqArchiveSafeHandle hMpq); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileSetMaxFileCount(MpqArchiveSafeHandle hMpq, uint dwMaxFileCount); + #endregion + + #region Changing (attributes) file + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern uint SFileGetAttributes(MpqArchiveSafeHandle hMpq); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileSetAttributes(MpqArchiveSafeHandle hMpq, uint dwFlags); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileUpdateFileAttributes( + MpqArchiveSafeHandle hMpq, + [MarshalAs(UnmanagedType.LPStr)] string szFileName + ); + #endregion + + #region Functions for manipulation with patch archives + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileOpenPatchArchive( + MpqArchiveSafeHandle hMpq, + [MarshalAs(UnmanagedType.LPTStr)] string szPatchMpqName, + [MarshalAs(UnmanagedType.LPStr)] string szPatchPathPrefix, + uint dwFlags + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileIsPatchedArchive(MpqArchiveSafeHandle hMpq); + #endregion + + #region Functions for file manipulation + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileHasFile( + MpqArchiveSafeHandle hMpq, + [MarshalAs(UnmanagedType.LPStr)] string szFileName + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileOpenFileEx( + MpqArchiveSafeHandle hMpq, + [MarshalAs(UnmanagedType.LPStr)] string szFileName, + uint dwSearchScope, + out MpqFileSafeHandle phFile + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern uint SFileGetFileSize(MpqFileSafeHandle hFile, ref uint pdwFileSizeHigh); + + public static unsafe uint SFileGetFilePointer( + MpqFileSafeHandle hFile + ) + { + if (hFile.IsInvalid || hFile.IsClosed) + throw new InvalidOperationException(); + + IntPtr handle = hFile.DangerousGetHandle(); + _TMPQFileHeader* header = (_TMPQFileHeader*)handle.ToPointer(); + return header->dwFilePos; + } + + //public static unsafe uint SFileGetFileSize( + // MpqFileSafeHandle hFile + // ) + //{ + // if (hFile.IsInvalid || hFile.IsClosed) + // throw new InvalidOperationException(); + + // IntPtr handle = hFile.DangerousGetHandle(); + // _TMPQFileHeader* header = (_TMPQFileHeader*)handle.ToPointer(); + // return header->pFileEntry->dwFileSize; + //} + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern uint SFileSetFilePointer( + MpqFileSafeHandle hFile, + uint lFilePos, + ref uint plFilePosHigh, + uint dwMoveMethod + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileReadFile( + MpqFileSafeHandle hFile, + IntPtr lpBuffer, + uint dwToRead, + out uint pdwRead, + ref System.Threading.NativeOverlapped lpOverlapped + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileCloseFile(IntPtr hFile); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileCloseFile(MpqFileSafeHandle hFile); + + #region Retrieving info about a file in the archive + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileGetFileInfo( + IntPtr hMpqOrFile, + SFileInfoClass InfoClass, + IntPtr pvFileInfo, + uint cbFileInfoSize, + out uint pcbLengthNeeded + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileGetFileInfo( + MpqArchiveSafeHandle hMpqOrFile, + SFileInfoClass InfoClass, + IntPtr pvFileInfo, + uint cbFileInfoSize, + out uint pcbLengthNeeded + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileGetFileInfo( + MpqFileSafeHandle hMpqOrFile, + SFileInfoClass InfoClass, + IntPtr pvFileInfo, + uint cbFileInfoSize, + out uint pcbLengthNeeded + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileGetFileName( + MpqFileSafeHandle hFile, + [MarshalAs(UnmanagedType.LPStr)] out string szFileName + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileFreeFileInfo( + IntPtr pvFileInfo, + SFileInfoClass infoClass + ); + #endregion + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileExtractFile( + MpqArchiveSafeHandle hMpq, + [MarshalAs(UnmanagedType.LPStr)] string szToExtract, + [MarshalAs(UnmanagedType.LPTStr)] string szExtracted, + uint dwSearchScope + ); + + #endregion + + #region Functions for file and archive verification + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileGetFileChecksums( + MpqArchiveSafeHandle hMpq, + [MarshalAs(UnmanagedType.LPStr)] string szFileName, + out uint pdwCrc32, + IntPtr pMD5 + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern uint SFileVerifyFile( + MpqArchiveSafeHandle hMpq, + [MarshalAs(UnmanagedType.LPStr)] string szFileName, + uint dwFlags + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern int SFileVerifyRawData( + MpqArchiveSafeHandle hMpq, + uint dwWhatToVerify, + [MarshalAs(UnmanagedType.LPStr)] string szFileName + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern uint SFileVerifyArchive(MpqArchiveSafeHandle hMpq); + #endregion + + #region Functions for file searching + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern IntPtr SFileFindFirstFile( + MpqArchiveSafeHandle hMpq, + [MarshalAs(UnmanagedType.LPStr)] string szMask, + out _SFILE_FIND_DATA lpFindFileData, + [MarshalAs(UnmanagedType.LPStr)] string szListFile + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileFindNextFile( + IntPtr hFind, + [In, Out] ref _SFILE_FIND_DATA lpFindFileData + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileFindClose(IntPtr hFind); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern IntPtr SListFileFindFirstFile( + MpqArchiveSafeHandle hMpq, + [MarshalAs(UnmanagedType.LPStr)] string szListFile, + [MarshalAs(UnmanagedType.LPStr)] string szMask, + [In, Out] ref _SFILE_FIND_DATA lpFindFileData + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SListFileFindNextFile( + IntPtr hFind, + [In, Out] ref _SFILE_FIND_DATA lpFindFileData + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SListFileFindClose(IntPtr hFind); + #endregion + + #region Locale support + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern int SFileEnumLocales( + MpqArchiveSafeHandle hMpq, + [MarshalAs(UnmanagedType.LPStr)] string szFileName, + IntPtr plcLocales, + ref uint pdwMaxLocales, + uint dwSearchScope + ); + #endregion + + #region Support for adding files to the MPQ + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileCreateFile( + MpqArchiveSafeHandle hMpq, + [MarshalAs(UnmanagedType.LPStr)] string szArchiveName, + ulong fileTime, + uint dwFileSize, + uint lcLocale, + uint dwFlags, + out IntPtr phFile + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileWriteFile( + MpqFileSafeHandle hFile, + IntPtr pvData, + uint dwSize, + uint dwCompression + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileFinishFile(MpqFileSafeHandle hFile); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileAddFileEx( + MpqArchiveSafeHandle hMpq, + [MarshalAs(UnmanagedType.LPTStr)] string szFileName, + [MarshalAs(UnmanagedType.LPStr)] string szArchivedName, + uint dwFlags, + uint dwCompression, + uint dwCompressionNext + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileAddFile( + MpqArchiveSafeHandle hMpq, + [MarshalAs(UnmanagedType.LPTStr)] string szFileName, + [MarshalAs(UnmanagedType.LPStr)] string szArchivedName, + uint dwFlags + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileAddWave( + MpqArchiveSafeHandle hMpq, + [MarshalAs(UnmanagedType.LPTStr)] string szFileName, + [MarshalAs(UnmanagedType.LPStr)] string szArchivedName, + uint dwFlags, + uint dwQuality + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileRemoveFile( + MpqArchiveSafeHandle hMpq, + [MarshalAs(UnmanagedType.LPStr)] string szFileName, + uint dwSearchScope + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileRenameFile( + MpqArchiveSafeHandle hMpq, + [MarshalAs(UnmanagedType.LPStr)] string szOldFileName, + [MarshalAs(UnmanagedType.LPStr)] string szNewFileName + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileSetFileLocale( + MpqFileSafeHandle hFile, + uint lcNewLocale + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileSetDataCompression(uint DataCompression); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern bool SFileSetAddFileCallback( + MpqArchiveSafeHandle hMpq, + SFILE_ADDFILE_CALLBACK AddFileCB, + IntPtr pvUserData + ); + #endregion + + #region Compression and decompression + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern int SCompImplode( + IntPtr pvOutBuffer, + ref int pcbOutBuffer, + IntPtr pvInBuffer, + int cbInBuffer + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern int SCompExplode( + IntPtr pvOutBuffer, + ref int pcbOutBuffer, + IntPtr pvInBuffer, + int cbInBuffer + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern int SCompCompress( + IntPtr pvOutBuffer, + ref int pcbOutBuffer, + IntPtr pvInBuffer, + int cbInBuffer, + uint uCompressionMask, + int nCmpType, + int nCmpLevel + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern int SCompDecompress( + IntPtr pvOutBuffer, + ref int pcbOutBuffer, + IntPtr pvInBuffer, + int cbInBuffer + ); + + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] + public static extern int SCompDecompress2( + IntPtr pvOutBuffer, + ref int pcbOutBuffer, + IntPtr pvInBuffer, + int cbInBuffer + ); + + + #endregion + } + +#pragma warning disable 0169,0649 + internal struct SFILE_CREATE_MPQ + { + public uint cbSize; + public uint dwMpqVersion; + private IntPtr pvUserData; + private uint cbUserData; + public uint dwStreamFlags; + public uint dwFileFlags1; + public uint dwFileFlags2; + public uint dwAttrFlags; + public uint dwSectorSize; + public uint dwRawChunkSize; + public uint dwMaxFileCount; + } + + internal unsafe struct _SFILE_FIND_DATA + { + public fixed char cFileName[260]; // Full name of the found file + + public IntPtr szPlainName; // Plain name of the found file + public uint dwHashIndex; // Hash table index for the file + public uint dwBlockIndex; // Block table index for the file + public uint dwFileSize; // File size in bytes + public uint dwFileFlags; // MPQ file flags + public uint dwCompSize; // Compressed file size + public uint dwFileTimeLo; // Low 32-bits of the file time (0 if not present) + public uint dwFileTimeHi; // High 32-bits of the file time (0 if not present) + public uint lcLocale; // Locale version + } + + internal unsafe struct _TFileEntry + { + public ulong FileNameHash; + public ulong ByteOffset; + public ulong FileTime; + public uint dwHashIndex; + public uint dwFileSize; + public uint dwCmpSize; + public uint dwFlags; + public ushort lcLocale; + public ushort wPlatform; + public uint dwCrc32; + public fixed byte md5[16]; + public IntPtr szFileName; + } + + // Provides enough of _TMPQFile to get to the file size and current position. + internal unsafe struct _TMPQFileHeader + { + public IntPtr pStream; + public IntPtr ha; + public _TFileEntry* pFileEntry; + public uint dwFileKey; + public uint dwFilePos; + } +#pragma warning restore 0169,0649 + +} diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/SFileInfoClass.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/SFileInfoClass.cs new file mode 100644 index 00000000..f8ef7a92 --- /dev/null +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/SFileInfoClass.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace StormLibSharp.Native +{ + internal enum SFileInfoClass + { + // Info classes for archives + SFileMpqFileName, // Name of the archive file (TCHAR []) + SFileMpqStreamBitmap, // Array of bits, each bit means availability of one block (BYTE []) + SFileMpqUserDataOffset, // Offset of the user data header (ULONGLONG) + SFileMpqUserDataHeader, // Raw (unfixed) user data header (TMPQUserData) + SFileMpqUserData, // MPQ USer data, without the header (BYTE []) + SFileMpqHeaderOffset, // Offset of the MPQ header (ULONGLONG) + SFileMpqHeaderSize, // Fixed size of the MPQ header + SFileMpqHeader, // Raw (unfixed) archive header (TMPQHeader) + SFileMpqHetTableOffset, // Offset of the HET table, relative to MPQ header (ULONGLONG) + SFileMpqHetTableSize, // Compressed size of the HET table (ULONGLONG) + SFileMpqHetHeader, // HET table header (TMPQHetHeader) + SFileMpqHetTable, // HET table as pointer. Must be freed using SFileFreeFileInfo + SFileMpqBetTableOffset, // Offset of the BET table, relative to MPQ header (ULONGLONG) + SFileMpqBetTableSize, // Compressed size of the BET table (ULONGLONG) + SFileMpqBetHeader, // BET table header, followed by the flags (TMPQBetHeader + DWORD[]) + SFileMpqBetTable, // BET table as pointer. Must be freed using SFileFreeFileInfo + SFileMpqHashTableOffset, // Hash table offset, relative to MPQ header (ULONGLONG) + SFileMpqHashTableSize64, // Compressed size of the hash table (ULONGLONG) + SFileMpqHashTableSize, // Size of the hash table, in entries (DWORD) + SFileMpqHashTable, // Raw (unfixed) hash table (TMPQBlock []) + SFileMpqBlockTableOffset, // Block table offset, relative to MPQ header (ULONGLONG) + SFileMpqBlockTableSize64, // Compressed size of the block table (ULONGLONG) + SFileMpqBlockTableSize, // Size of the block table, in entries (DWORD) + SFileMpqBlockTable, // Raw (unfixed) block table (TMPQBlock []) + SFileMpqHiBlockTableOffset, // Hi-block table offset, relative to MPQ header (ULONGLONG) + SFileMpqHiBlockTableSize64, // Compressed size of the hi-block table (ULONGLONG) + SFileMpqHiBlockTable, // The hi-block table (USHORT []) + SFileMpqSignatures, // Signatures present in the MPQ (DWORD) + SFileMpqStrongSignatureOffset, // Byte offset of the strong signature, relative to begin of the file (ULONGLONG) + SFileMpqStrongSignatureSize, // Size of the strong signature (DWORD) + SFileMpqStrongSignature, // The strong signature (BYTE []) + SFileMpqArchiveSize64, // Archive size from the header (ULONGLONG) + SFileMpqArchiveSize, // Archive size from the header (DWORD) + SFileMpqMaxFileCount, // Max number of files in the archive (DWORD) + SFileMpqFileTableSize, // Number of entries in the file table (DWORD) + SFileMpqSectorSize, // Sector size (DWORD) + SFileMpqNumberOfFiles, // Number of files (DWORD) + SFileMpqRawChunkSize, // Size of the raw data chunk for MD5 + SFileMpqStreamFlags, // Stream flags (DWORD) + SFileMpqIsReadOnly, // Nonzero if the MPQ is read only (DWORD) + + // Info classes for files + SFileInfoPatchChain, // Chain of patches where the file is (TCHAR []) + SFileInfoFileEntry, // The file entry for the file (TFileEntry) + SFileInfoHashEntry, // Hash table entry for the file (TMPQHash) + SFileInfoHashIndex, // Index of the hash table entry (DWORD) + SFileInfoNameHash1, // The first name hash in the hash table (DWORD) + SFileInfoNameHash2, // The second name hash in the hash table (DWORD) + SFileInfoNameHash3, // 64-bit file name hash for the HET/BET tables (ULONGLONG) + SFileInfoLocale, // File locale (DWORD) + SFileInfoFileIndex, // Block index (DWORD) + SFileInfoByteOffset, // File position in the archive (ULONGLONG) + SFileInfoFileTime, // File time (ULONGLONG) + SFileInfoFileSize, // Size of the file (DWORD) + SFileInfoCompressedSize, // Compressed file size (DWORD) + SFileInfoFlags, // File flags from (DWORD) + SFileInfoEncryptionKey, // File encryption key + SFileInfoEncryptionKeyRaw, // Unfixed value of the file key + } +} diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/SFileOpenArchiveFlags.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/SFileOpenArchiveFlags.cs new file mode 100644 index 00000000..d444f2be --- /dev/null +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/SFileOpenArchiveFlags.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace StormLibSharp.Native +{ + [Flags] + internal enum SFileOpenArchiveFlags : uint + { + None = 0, + TypeIsFile = None, + TypeIsMemoryMapped = 1, + TypeIsHttp = 2, + + AccessReadOnly = 0x100, + AccessReadWriteShare = 0x200, + AccessUseBitmap = 0x400, + + DontOpenListfile = 0x10000, + DontOpenAttributes = 0x20000, + DontSearchHeader = 0x40000, + ForceVersion1 = 0x80000, + CheckSectorCRC = 0x100000, + } +} diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/Win32Methods.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/Win32Methods.cs new file mode 100644 index 00000000..15596c07 --- /dev/null +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/Win32Methods.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.IO.MemoryMappedFiles; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace StormLibSharp.Native +{ + internal static class Win32Methods + { + [DllImport("kernel32", ExactSpelling = false, SetLastError = true)] + public static extern uint GetMappedFileName( + IntPtr hProcess, + IntPtr fileHandle, + IntPtr lpFilename, + uint nSize + ); + + [DllImport("kernel32", ExactSpelling = false, SetLastError = true)] + public static extern uint GetFinalPathNameByHandle( + IntPtr hFile, + IntPtr lpszFilePath, + uint cchFilePath, + uint dwFlags + ); + + [DllImport("kernel32", SetLastError = false, ExactSpelling = false)] + public static extern int GetLastError(); + + public static string GetFileNameOfMemoryMappedFile(MemoryMappedFile file) + { + const uint size = 522; + IntPtr path = Marshal.AllocCoTaskMem(unchecked((int)size)); // MAX_PATH + 1 char + + string result = null; + try + { + // constant 0x2 = VOLUME_NAME_NT + uint test = GetFinalPathNameByHandle(file.SafeMemoryMappedFileHandle.DangerousGetHandle(), path, size, 0x2); + if (test != 0) + throw new Win32Exception(); + + result = Marshal.PtrToStringAuto(path); + } + catch + { + uint test = GetMappedFileName(Process.GetCurrentProcess().Handle, file.SafeMemoryMappedFileHandle.DangerousGetHandle(), path, size); + if (test != 0) + throw new Win32Exception(); + + result = Marshal.PtrToStringAuto(path); + } + + return result; + } + } +} From 4840d9df6efbc7f2f5aca4ad8814f89403cee7d1 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sun, 2 Nov 2025 21:41:58 -0500 Subject: [PATCH 3/8] Remove NoWarn and fix issues --- .../Models/CDROM/DataTrack.cs | 2 +- .../Readers/CDROMVolume.cs | 2 +- .../SabreTools.Serialization.csproj | 15 +-- .../_EXTERNAL/CascLibSharp/CascApi.cs | 50 +++++----- .../_EXTERNAL/CascLibSharp/CascException.cs | 7 +- .../_EXTERNAL/CascLibSharp/CascFileStream.cs | 24 ++--- .../_EXTERNAL/CascLibSharp/CascFoundFile.cs | 27 ++++-- .../CascLibSharp/CascStorageContext.cs | 38 ++++---- .../Native/CascFileEnumerationSafeHandle.cs | 13 +-- .../Native/CascStorageFileSafeHandle.cs | 12 +-- .../Native/CascStorageSafeHandle.cs | 12 +-- .../CascLibSharp/Native/CoTaskMem.cs | 8 +- .../CascLibSharp/Native/NativeMethods.cs | 10 +- .../_EXTERNAL/StormLibSharp/MpqArchive.cs | 15 +-- .../MpqArchiveCompactingEventArgs.cs | 3 - .../_EXTERNAL/StormLibSharp/MpqFileStream.cs | 17 ++-- .../StormLibSharp/Native/Callbacks.cs | 3 - .../Native/MpqArchiveSafeHandle.cs | 8 +- .../StormLibSharp/Native/MpqFileSafeHandle.cs | 7 +- .../StormLibSharp/Native/NativeMethods.cs | 91 +++++++++---------- .../StormLibSharp/Native/SFileInfoClass.cs | 7 +- .../Native/SFileOpenArchiveFlags.cs | 3 - .../StormLibSharp/Native/Win32Methods.cs | 7 +- 23 files changed, 153 insertions(+), 228 deletions(-) diff --git a/SabreTools.Serialization/Models/CDROM/DataTrack.cs b/SabreTools.Serialization/Models/CDROM/DataTrack.cs index 982f0f30..95bf5cbf 100644 --- a/SabreTools.Serialization/Models/CDROM/DataTrack.cs +++ b/SabreTools.Serialization/Models/CDROM/DataTrack.cs @@ -16,6 +16,6 @@ namespace SabreTools.Data.Models.CDROM /// /// ISO9660 volume within the data track /// - public Volume Volume { get; set; } + public Volume Volume { get; set; } = new(); } } diff --git a/SabreTools.Serialization/Readers/CDROMVolume.cs b/SabreTools.Serialization/Readers/CDROMVolume.cs index e16c489e..73874b57 100644 --- a/SabreTools.Serialization/Readers/CDROMVolume.cs +++ b/SabreTools.Serialization/Readers/CDROMVolume.cs @@ -58,7 +58,7 @@ namespace SabreTools.Serialization.Readers /// /// Stream to parse /// Filled byte[] on success, null on error - public static byte[]? ParseCDROMSystemArea(Stream data) + public static byte[] ParseCDROMSystemArea(Stream data) { var systemArea = new byte[Constants.SystemAreaSectors * Constants.MinimumSectorSize]; // Process in sectors diff --git a/SabreTools.Serialization/SabreTools.Serialization.csproj b/SabreTools.Serialization/SabreTools.Serialization.csproj index 0132febc..96eab79e 100644 --- a/SabreTools.Serialization/SabreTools.Serialization.csproj +++ b/SabreTools.Serialization/SabreTools.Serialization.csproj @@ -8,8 +8,6 @@ false true latest - - CS8600;CS8601;CS8603;CS8604;CS8618;CS8625;CS8634;IL3000 enable true snupkg @@ -43,18 +41,7 @@ net6.0;net7.0;net8.0;net9.0 - - - - $(DefaultItemExcludes); - **\AssemblyInfo.cs; - _EXTERNAL\stormlibsharp\lib\**; - _EXTERNAL\stormlibsharp\src\CascLibSharp\**; - _EXTERNAL\stormlibsharp\src\TestConsole\** - - - - + $(DefaultItemExcludes); diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascApi.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascApi.cs index d3e1390b..92d2ca7b 100644 --- a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascApi.cs +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascApi.cs @@ -1,13 +1,9 @@ -using CascLibSharp.Native; -using System; -using System.Collections.Generic; +using System; using System.ComponentModel; using System.IO; -using System.Linq; using System.Reflection; using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; +using CascLibSharp.Native; namespace CascLibSharp { @@ -30,7 +26,7 @@ namespace CascLibSharp ref uint pcbLengthNeeded); [return: MarshalAs(UnmanagedType.I1)] [UnmanagedFunctionPointer(CallingConvention.Winapi)] - internal delegate bool FnCascCloseStorage( IntPtr hStorage); + internal delegate bool FnCascCloseStorage(IntPtr hStorage); [return: MarshalAs(UnmanagedType.I1)] [UnmanagedFunctionPointer(CallingConvention.Winapi)] @@ -73,14 +69,14 @@ namespace CascLibSharp out uint dwRead); [return: MarshalAs(UnmanagedType.I1)] [UnmanagedFunctionPointer(CallingConvention.Winapi)] - internal delegate bool FnCascCloseFile( IntPtr hFile); + internal delegate bool FnCascCloseFile(IntPtr hFile); [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate CascFileEnumerationSafeHandle FnCascFindFirstFile( CascStorageSafeHandle hStorage, [MarshalAs(UnmanagedType.LPStr)] string szMask, ref CascFindData pFindData, - [MarshalAs(UnmanagedType.LPTStr)] string szListFile); + [MarshalAs(UnmanagedType.LPTStr)] string? szListFile); [return: MarshalAs(UnmanagedType.I1)] [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate bool FnCascFindNextFile( @@ -88,28 +84,28 @@ namespace CascLibSharp ref CascFindData pFindData); [return: MarshalAs(UnmanagedType.I1)] [UnmanagedFunctionPointer(CallingConvention.Winapi)] - internal delegate bool FnCascFindClose( IntPtr hFind); + internal delegate bool FnCascFindClose(IntPtr hFind); #endregion #region Field and method defs - public readonly FnCascOpenStorage CascOpenStorage; - public readonly FnCascGetStorageInfo CascGetStorageInfo; - public readonly FnCascCloseStorage CascCloseStorage; + public readonly FnCascOpenStorage? CascOpenStorage; + public readonly FnCascGetStorageInfo? CascGetStorageInfo; + public readonly FnCascCloseStorage? CascCloseStorage; - public readonly FnCascOpenFileByIndexKey CascOpenFileByIndexKey; - public readonly FnCascOpenFileByEncodingKey CascOpenFileByEncodingKey; - public readonly FnCascOpenFile CascOpenFile; + public readonly FnCascOpenFileByIndexKey? CascOpenFileByIndexKey; + public readonly FnCascOpenFileByEncodingKey? CascOpenFileByEncodingKey; + public readonly FnCascOpenFile? CascOpenFile; - public readonly FnCascGetFileSize CascGetFileSizeBase; + public readonly FnCascGetFileSize? CascGetFileSizeBase; public long CascGetFileSize(CascStorageFileSafeHandle hFile) { uint high; - uint low = CascGetFileSizeBase(hFile, out high); + uint low = CascGetFileSizeBase!(hFile, out high); long result = (high << 32) | low; return result; } - public readonly FnCascSetFilePointer CascSetFilePointerBase; + public readonly FnCascSetFilePointer? CascSetFilePointerBase; public long CascSetFilePointer(CascStorageFileSafeHandle hFile, long filePos, SeekOrigin moveMethod) { uint low, high; @@ -119,18 +115,18 @@ namespace CascLibSharp high = (uint)(((ulong)filePos & 0xffffffff00000000) >> 32); } - low = CascSetFilePointerBase(hFile, low, ref high, moveMethod); + low = CascSetFilePointerBase!(hFile, low, ref high, moveMethod); long result = (high << 32) | low; return result; } - public readonly FnCascReadFile CascReadFile; - public readonly FnCascCloseFile CascCloseFile; + public readonly FnCascReadFile? CascReadFile; + public readonly FnCascCloseFile? CascCloseFile; - public readonly FnCascFindFirstFile CascFindFirstFile; - public readonly FnCascFindNextFile CascFindNextFile; - public readonly FnCascFindClose CascFindClose; + public readonly FnCascFindFirstFile? CascFindFirstFile; + public readonly FnCascFindNextFile? CascFindNextFile; + public readonly FnCascFindClose? CascFindClose; #endregion internal CascApi(IntPtr hModule) @@ -152,7 +148,7 @@ namespace CascLibSharp SetFn(ref CascFindClose, hModule, "CascFindClose"); } - private static void SetFn(ref T target, IntPtr hModule, string procName) + private static void SetFn(ref T? target, IntPtr hModule, string procName) where T : class { IntPtr procAddr = NativeMethods.GetProcAddress(hModule, procName); @@ -164,7 +160,7 @@ namespace CascLibSharp private static CascApi Load() { string myPath = Assembly.GetExecutingAssembly().Location; - string directory = Path.GetDirectoryName(myPath); + string directory = Path.GetDirectoryName(myPath) ?? string.Empty; string arch = IntPtr.Size == 8 ? "x64" : "x86"; #if DEBUG string build = "dbg"; diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascException.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascException.cs index d6a3dec9..83a94531 100644 --- a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascException.cs +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascException.cs @@ -1,10 +1,5 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; +using System.ComponentModel; using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; namespace CascLibSharp { diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascFileStream.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascFileStream.cs index 2a197720..6f4b46da 100644 --- a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascFileStream.cs +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascFileStream.cs @@ -1,11 +1,7 @@ -using CascLibSharp.Native; -using System; -using System.Collections.Generic; +using System; using System.Diagnostics; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using CascLibSharp.Native; namespace CascLibSharp { @@ -17,13 +13,13 @@ namespace CascLibSharp private CascStorageFileSafeHandle _handle; private CascApi _api; - internal CascFileStream(CascStorageFileSafeHandle handle, CascApi api) + internal CascFileStream(CascStorageFileSafeHandle? handle, CascApi? api) { Debug.Assert(handle != null); - Debug.Assert(!handle.IsInvalid); + Debug.Assert(!handle!.IsInvalid); Debug.Assert(api != null); - _api = api; + _api = api!; _handle = handle; } @@ -72,10 +68,10 @@ namespace CascLibSharp /// Thrown if the Stream has been disposed. public override long Length { - get + get { AssertValidHandle(); - return _api.CascGetFileSize(_handle); + return _api!.CascGetFileSize(_handle); } } @@ -89,12 +85,12 @@ namespace CascLibSharp get { AssertValidHandle(); - return _api.CascSetFilePointer(_handle, 0, SeekOrigin.Current); + return _api!.CascSetFilePointer(_handle, 0, SeekOrigin.Current); } set { AssertValidHandle(); - long result = _api.CascSetFilePointer(_handle, value, SeekOrigin.Begin); + long result = _api!.CascSetFilePointer(_handle, value, SeekOrigin.Begin); if (result != value) throw new CascException(); } @@ -123,7 +119,7 @@ namespace CascLibSharp uint read = 0; fixed (byte* pBuffer = &buffer[0]) { - if (!_api.CascReadFile(_handle, new IntPtr((void*)pBuffer), unchecked((uint)count), out read)) + if (!_api.CascReadFile!(_handle, new IntPtr((void*)pBuffer), unchecked((uint)count), out read)) throw new CascException(); } diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascFoundFile.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascFoundFile.cs index af3594e4..a7576400 100644 --- a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascFoundFile.cs +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascFoundFile.cs @@ -1,9 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; namespace CascLibSharp { @@ -12,9 +8,13 @@ namespace CascLibSharp /// public class CascFoundFile { +#if NET20 || NET35 || NET40 + private WeakReference _ownerContext; +#else private WeakReference _ownerContext; +#endif - internal CascFoundFile(string fileName, IntPtr plainName, byte[] encodingKey, CascLocales locales, long fileSize, CascStorageContext ownerContext) + internal CascFoundFile(string? fileName, IntPtr plainName, byte[] encodingKey, CascLocales locales, long fileSize, CascStorageContext ownerContext) { FileName = fileName; PlainFileName = Marshal.PtrToStringAnsi(plainName); @@ -22,17 +22,21 @@ namespace CascLibSharp Locales = locales; FileSize = fileSize; +#if NET20 || NET35 || NET40 + _ownerContext = new WeakReference(ownerContext); +#else _ownerContext = new WeakReference(ownerContext); +#endif } /// /// Gets the full path to this file. /// - public string FileName { get; private set; } + public string? FileName { get; private set; } /// /// Gets the plain (no directory-qualified) file name of this file. /// - public string PlainFileName { get; private set; } + public string? PlainFileName { get; private set; } /// /// Gets the CASC encoding key for this file. /// @@ -52,9 +56,14 @@ namespace CascLibSharp /// A CascFileStream, which acts as a Stream for a CASC stored file. public CascFileStream Open() { - CascStorageContext context; - if (!_ownerContext.TryGetTarget(out context)) +#if NET20 || NET35 || NET40 + CascStorageContext? context = _ownerContext.Target as CascStorageContext; + if (context == null) throw new ObjectDisposedException("The owning context has been closed."); +#else + if (!_ownerContext.TryGetTarget(out CascStorageContext? context) || context == null) + throw new ObjectDisposedException("The owning context has been closed."); +#endif return context.OpenFileByEncodingKey(EncodingKey); } diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascStorageContext.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascStorageContext.cs index 7dbae534..27665adb 100644 --- a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascStorageContext.cs +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascStorageContext.cs @@ -1,10 +1,6 @@ -using CascLibSharp.Native; -using System; +using System; using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; +using CascLibSharp.Native; namespace CascLibSharp { @@ -14,7 +10,7 @@ namespace CascLibSharp public class CascStorageContext : IDisposable { private CascApi _api; - private CascStorageSafeHandle _handle; + private CascStorageSafeHandle? _handle; private Lazy _hasListfile; private Lazy _clientType; private Lazy _fileCount; @@ -29,7 +25,7 @@ namespace CascLibSharp { _api = CascApi.Instance; - if (!_api.CascOpenStorage(dataPath, 0, out _handle) || _handle.IsInvalid) + if (!_api.CascOpenStorage!(dataPath, 0, out _handle) || _handle.IsInvalid) throw new CascException(); _handle.Api = _api; @@ -53,7 +49,7 @@ namespace CascLibSharp throw new ObjectDisposedException("CascStorageContext"); CascStorageFileSafeHandle hFile; - if (!_api.CascOpenFile(_handle, fileName, (uint)locale, 0, out hFile)) + if (!_api.CascOpenFile!(_handle, fileName, (uint)locale, 0, out hFile)) throw new CascException(); hFile.Api = _api; @@ -65,7 +61,7 @@ namespace CascLibSharp /// Opens a file by its index key. /// /// - /// An index key is a binary representation of a file. I do not know what it comes from; I know that it's used to identify files inside of CASC, + /// An index key is a binary representation of a file. I do not know what it comes from; I know that it's used to identify files inside of CASC, /// but I don't know how someone obtains the index key. They are not produced in the public API of CascLib. /// /// The index key to search. @@ -84,7 +80,7 @@ namespace CascLibSharp qk.pbData = mem.Pointer; CascStorageFileSafeHandle hFile; - if (!_api.CascOpenFileByIndexKey(_handle, ref qk, 0, out hFile)) + if (!_api.CascOpenFileByIndexKey!(_handle, ref qk, 0, out hFile)) throw new CascException(); hFile.Api = _api; @@ -112,7 +108,7 @@ namespace CascLibSharp qk.pbData = mem.Pointer; CascStorageFileSafeHandle hFile; - if (!_api.CascOpenFileByEncodingKey(_handle, ref qk, 0, out hFile)) + if (!_api.CascOpenFileByEncodingKey!(_handle, ref qk, 0, out hFile)) throw new CascException(); hFile.Api = _api; @@ -127,7 +123,7 @@ namespace CascLibSharp /// The mask to search. * and ? are valid tokens for substitution. /// A path to a listfile. Required if the CASC container is for World of Warcraft. /// An enumeration of matching file references in the CASC container. - public IEnumerable SearchFiles(string mask, string listFilePath = null) + public IEnumerable SearchFiles(string mask, string? listFilePath = null) { if (_handle == null || _handle.IsInvalid) throw new ObjectDisposedException("CascStorageContext"); @@ -136,7 +132,7 @@ namespace CascLibSharp throw new ArgumentNullException("listFilePath"); CascFindData cfd = new CascFindData(); - using (var handle = _api.CascFindFirstFile(_handle, mask, ref cfd, listFilePath)) + using (var handle = _api.CascFindFirstFile!(_handle, mask, ref cfd, listFilePath)) { if (handle.IsInvalid) yield break; @@ -145,20 +141,20 @@ namespace CascLibSharp yield return cfd.ToFoundFile(this); - while (_api.CascFindNextFile(handle, ref cfd)) + while (_api.CascFindNextFile!(handle, ref cfd)) { yield return cfd.ToFoundFile(this); } } } - + private bool CheckHasListfile() { if (_handle == null || _handle.IsInvalid) throw new ObjectDisposedException("CascStorageContext"); uint storageInfo = 0, lengthNeeded = 4; - if (!_api.CascGetStorageInfo(_handle, CascStorageInfoClass.Features, ref storageInfo, new IntPtr(4), ref lengthNeeded)) + if (!_api.CascGetStorageInfo!(_handle, CascStorageInfoClass.Features, ref storageInfo, new IntPtr(4), ref lengthNeeded)) throw new CascException(); CascStorageFeatures features = (CascStorageFeatures)storageInfo; @@ -174,7 +170,7 @@ namespace CascLibSharp throw new ObjectDisposedException("CascStorageContext"); uint storageInfo = 0, lengthNeeded = 4; - if (!_api.CascGetStorageInfo(_handle, CascStorageInfoClass.GameInfo, ref storageInfo, new IntPtr(4), ref lengthNeeded)) + if (!_api.CascGetStorageInfo!(_handle, CascStorageInfoClass.GameInfo, ref storageInfo, new IntPtr(4), ref lengthNeeded)) throw new CascException(); CascGameId gameId = (CascGameId)storageInfo; @@ -188,7 +184,7 @@ namespace CascLibSharp throw new ObjectDisposedException("CascStorageContext"); uint storageInfo = 0, lengthNeeded = 4; - if (!_api.CascGetStorageInfo(_handle, CascStorageInfoClass.Features, ref storageInfo, new IntPtr(4), ref lengthNeeded)) + if (!_api.CascGetStorageInfo!(_handle, CascStorageInfoClass.Features, ref storageInfo, new IntPtr(4), ref lengthNeeded)) throw new CascException(); return storageInfo; @@ -200,7 +196,7 @@ namespace CascLibSharp throw new ObjectDisposedException("CascStorageContext"); uint storageInfo = 0, lengthNeeded = 4; - if (!_api.CascGetStorageInfo(_handle, CascStorageInfoClass.Features, ref storageInfo, new IntPtr(4), ref lengthNeeded)) + if (!_api.CascGetStorageInfo!(_handle, CascStorageInfoClass.Features, ref storageInfo, new IntPtr(4), ref lengthNeeded)) throw new CascException(); return unchecked((int)storageInfo); @@ -274,7 +270,7 @@ namespace CascLibSharp /// True if this is being called via the Dispose() method; false if it's being called by the finalizer. protected virtual void Dispose(bool disposing) { - if (disposing) + if (disposing && _handle != null) { if (!_handle.IsInvalid) { diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CascFileEnumerationSafeHandle.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CascFileEnumerationSafeHandle.cs index a673cf70..41ea1269 100644 --- a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CascFileEnumerationSafeHandle.cs +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CascFileEnumerationSafeHandle.cs @@ -1,9 +1,5 @@ -using Microsoft.Win32.SafeHandles; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System; +using Microsoft.Win32.SafeHandles; namespace CascLibSharp.Native { @@ -11,6 +7,7 @@ namespace CascLibSharp.Native { public CascFileEnumerationSafeHandle() : base(true) { } + public CascFileEnumerationSafeHandle(IntPtr handle) : this() { @@ -20,10 +17,10 @@ namespace CascLibSharp.Native protected override bool ReleaseHandle() { var api = Api ?? CascApi.Instance; - return api.CascFindClose(DangerousGetHandle()); + return api.CascFindClose!(DangerousGetHandle()); } - internal CascApi Api + internal CascApi? Api { get; set; diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CascStorageFileSafeHandle.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CascStorageFileSafeHandle.cs index b6d2cc67..c72cc2da 100644 --- a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CascStorageFileSafeHandle.cs +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CascStorageFileSafeHandle.cs @@ -1,9 +1,5 @@ -using Microsoft.Win32.SafeHandles; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System; +using Microsoft.Win32.SafeHandles; namespace CascLibSharp.Native { @@ -24,10 +20,10 @@ namespace CascLibSharp.Native protected override bool ReleaseHandle() { var api = Api ?? CascApi.Instance; - return api.CascCloseFile(DangerousGetHandle()); + return api.CascCloseFile!(DangerousGetHandle()); } - internal CascApi Api + internal CascApi? Api { get; set; diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CascStorageSafeHandle.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CascStorageSafeHandle.cs index 83361901..1360e96a 100644 --- a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CascStorageSafeHandle.cs +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CascStorageSafeHandle.cs @@ -1,9 +1,5 @@ -using Microsoft.Win32.SafeHandles; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System; +using Microsoft.Win32.SafeHandles; namespace CascLibSharp.Native { @@ -24,10 +20,10 @@ namespace CascLibSharp.Native protected override bool ReleaseHandle() { var api = Api ?? CascApi.Instance; - return api.CascCloseStorage(DangerousGetHandle()); + return api.CascCloseStorage!(DangerousGetHandle()); } - internal CascApi Api + internal CascApi? Api { get; set; diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CoTaskMem.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CoTaskMem.cs index 78ddf58c..361d7654 100644 --- a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CoTaskMem.cs +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CoTaskMem.cs @@ -1,9 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; namespace CascLibSharp.Native { @@ -31,12 +27,12 @@ namespace CascLibSharp.Native public IntPtr Pointer { - get + get { if (_mem == IntPtr.Zero) throw new ObjectDisposedException("CoTaskMem"); - return _mem; + return _mem; } } diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/NativeMethods.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/NativeMethods.cs index 9bdf9409..1096d05f 100644 --- a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/NativeMethods.cs +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/NativeMethods.cs @@ -1,10 +1,6 @@ using System; using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; namespace CascLibSharp.Native { @@ -48,7 +44,7 @@ namespace CascLibSharp.Native internal static class GameConverterExtensions { - private static Dictionary GameClientMap = new Dictionary + private static Dictionary GameClientMap = new Dictionary { { CascGameId.Hots, CascKnownClient.HeroesOfTheStorm }, { CascGameId.Wow6, CascKnownClient.WorldOfWarcraft }, @@ -57,7 +53,7 @@ namespace CascLibSharp.Native { CascGameId.Starcraft2, CascKnownClient.Starcraft2 }, }; - private static Dictionary ClientGameMap = new Dictionary() + private static Dictionary ClientGameMap = new Dictionary() { { CascKnownClient.HeroesOfTheStorm, CascGameId.Hots }, { CascKnownClient.WorldOfWarcraft, CascGameId.Wow6 }, @@ -106,7 +102,7 @@ namespace CascLibSharp.Native public unsafe CascFoundFile ToFoundFile(CascStorageContext ownerContext) { - string fileName = null; + string? fileName = null; fixed (void* pFileName = szFileName) { fileName = Marshal.PtrToStringAnsi(new IntPtr(pFileName)); diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqArchive.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqArchive.cs index 4585dc50..d3866e11 100644 --- a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqArchive.cs +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqArchive.cs @@ -1,23 +1,19 @@ -using StormLibSharp.Native; -using System; +using System; using System.Collections.Generic; using System.ComponentModel; -using System.Globalization; using System.IO; using System.IO.MemoryMappedFiles; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; +using StormLibSharp.Native; namespace StormLibSharp { public class MpqArchive : IDisposable { - private MpqArchiveSafeHandle _handle; + private MpqArchiveSafeHandle? _handle; private List _openFiles = new List(); private FileAccess _accessType; private List _compactCallbacks = new List(); - private SFILE_COMPACT_CALLBACK _compactCallback; + private SFILE_COMPACT_CALLBACK? _compactCallback; #region Constructors / Factories public MpqArchive(string filePath, FileAccess accessType) @@ -37,7 +33,7 @@ namespace StormLibSharp public MpqArchive(MemoryMappedFile file, FileAccess accessType) { _accessType = accessType; - string fileName = Win32Methods.GetFileNameOfMemoryMappedFile(file); + string? fileName = Win32Methods.GetFileNameOfMemoryMappedFile(file); if (fileName == null) throw new ArgumentException("Could not retrieve the name of the file to initialize."); @@ -301,7 +297,6 @@ namespace StormLibSharp } _openFiles.Clear(); - _openFiles = null; } // Release diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqArchiveCompactingEventArgs.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqArchiveCompactingEventArgs.cs index c2a2ee7b..9a88fa15 100644 --- a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqArchiveCompactingEventArgs.cs +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqArchiveCompactingEventArgs.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace StormLibSharp { diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqFileStream.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqFileStream.cs index 18f6de25..97616360 100644 --- a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqFileStream.cs +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqFileStream.cs @@ -1,19 +1,16 @@ -using StormLibSharp.Native; -using System; -using System.Collections.Generic; +using System; using System.ComponentModel; using System.IO; -using System.Linq; -using System.Text; using System.Threading; +using StormLibSharp.Native; namespace StormLibSharp { public class MpqFileStream : Stream { - private MpqFileSafeHandle _handle; + private MpqFileSafeHandle? _handle; private FileAccess _accessType; - private MpqArchive _owner; + private MpqArchive? _owner; internal MpqFileStream(MpqFileSafeHandle handle, FileAccess accessType, MpqArchive owner) { @@ -47,12 +44,12 @@ namespace StormLibSharp { VerifyHandle(); - _owner.Flush(); + _owner?.Flush(); } public override long Length { - get + get { VerifyHandle(); @@ -166,7 +163,7 @@ namespace StormLibSharp } // TODO: Seems like the right place for SFileGetFileInfo, but will need to determine - // what value add these features have except for sophisticated debugging purposes + // what value add these features have except for sophisticated debugging purposes // (like in Ladis' MPQ Editor app). public int ChecksumCrc32 diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/Callbacks.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/Callbacks.cs index 58e7fa5e..2d257781 100644 --- a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/Callbacks.cs +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/Callbacks.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; namespace StormLibSharp.Native { diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/MpqArchiveSafeHandle.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/MpqArchiveSafeHandle.cs index 5e2df9c4..6813e927 100644 --- a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/MpqArchiveSafeHandle.cs +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/MpqArchiveSafeHandle.cs @@ -1,9 +1,5 @@ -using Microsoft.Win32.SafeHandles; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; +using System; +using Microsoft.Win32.SafeHandles; namespace StormLibSharp.Native { diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/MpqFileSafeHandle.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/MpqFileSafeHandle.cs index 495dd129..90d5e3a2 100644 --- a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/MpqFileSafeHandle.cs +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/MpqFileSafeHandle.cs @@ -1,8 +1,5 @@ -using Microsoft.Win32.SafeHandles; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System; +using Microsoft.Win32.SafeHandles; namespace StormLibSharp.Native { diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/NativeMethods.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/NativeMethods.cs index acef5fbc..2e820e19 100644 --- a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/NativeMethods.cs +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/NativeMethods.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; namespace StormLibSharp.Native { @@ -43,25 +40,25 @@ namespace StormLibSharp.Native [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileSetDownloadCallback( - MpqArchiveSafeHandle hMpq, + MpqArchiveSafeHandle? hMpq, [MarshalAs(UnmanagedType.FunctionPtr)] SFILE_DOWNLOAD_CALLBACK pfnCallback, IntPtr pvUserData ); [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] - public static extern bool SFileFlushArchive(MpqArchiveSafeHandle hMpq); + public static extern bool SFileFlushArchive(MpqArchiveSafeHandle? hMpq); [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileCloseArchive(IntPtr hMpq); [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] - public static extern bool SFileCloseArchive(MpqArchiveSafeHandle hMpq); + public static extern bool SFileCloseArchive(MpqArchiveSafeHandle? hMpq); #endregion #region Adds another listfile into MPQ. [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern int SFileAddListFile( - MpqArchiveSafeHandle hMpq, + MpqArchiveSafeHandle? hMpq, [MarshalAs(UnmanagedType.LPStr)] string szListFile ); #endregion @@ -69,14 +66,14 @@ namespace StormLibSharp.Native #region Archive compacting [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileSetCompactCallback( - MpqArchiveSafeHandle hMpq, - SFILE_COMPACT_CALLBACK compactCB, + MpqArchiveSafeHandle? hMpq, + SFILE_COMPACT_CALLBACK? compactCB, IntPtr pvUserData ); [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileCompactArchive( - MpqArchiveSafeHandle hMpq, + MpqArchiveSafeHandle? hMpq, [MarshalAs(UnmanagedType.LPStr)] string szListFile, bool bReserved ); @@ -84,22 +81,22 @@ namespace StormLibSharp.Native #region Maximum file count [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] - public static extern uint SFileGetMaxFileCount(MpqArchiveSafeHandle hMpq); + public static extern uint SFileGetMaxFileCount(MpqArchiveSafeHandle? hMpq); [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] - public static extern bool SFileSetMaxFileCount(MpqArchiveSafeHandle hMpq, uint dwMaxFileCount); + public static extern bool SFileSetMaxFileCount(MpqArchiveSafeHandle? hMpq, uint dwMaxFileCount); #endregion #region Changing (attributes) file [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] - public static extern uint SFileGetAttributes(MpqArchiveSafeHandle hMpq); + public static extern uint SFileGetAttributes(MpqArchiveSafeHandle? hMpq); [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] - public static extern bool SFileSetAttributes(MpqArchiveSafeHandle hMpq, uint dwFlags); + public static extern bool SFileSetAttributes(MpqArchiveSafeHandle? hMpq, uint dwFlags); [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileUpdateFileAttributes( - MpqArchiveSafeHandle hMpq, + MpqArchiveSafeHandle? hMpq, [MarshalAs(UnmanagedType.LPStr)] string szFileName ); #endregion @@ -107,39 +104,39 @@ namespace StormLibSharp.Native #region Functions for manipulation with patch archives [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileOpenPatchArchive( - MpqArchiveSafeHandle hMpq, + MpqArchiveSafeHandle? hMpq, [MarshalAs(UnmanagedType.LPTStr)] string szPatchMpqName, - [MarshalAs(UnmanagedType.LPStr)] string szPatchPathPrefix, + [MarshalAs(UnmanagedType.LPStr)] string? szPatchPathPrefix, uint dwFlags ); [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] - public static extern bool SFileIsPatchedArchive(MpqArchiveSafeHandle hMpq); + public static extern bool SFileIsPatchedArchive(MpqArchiveSafeHandle? hMpq); #endregion #region Functions for file manipulation [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileHasFile( - MpqArchiveSafeHandle hMpq, + MpqArchiveSafeHandle? hMpq, [MarshalAs(UnmanagedType.LPStr)] string szFileName ); [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileOpenFileEx( - MpqArchiveSafeHandle hMpq, + MpqArchiveSafeHandle? hMpq, [MarshalAs(UnmanagedType.LPStr)] string szFileName, uint dwSearchScope, out MpqFileSafeHandle phFile ); [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] - public static extern uint SFileGetFileSize(MpqFileSafeHandle hFile, ref uint pdwFileSizeHigh); + public static extern uint SFileGetFileSize(MpqFileSafeHandle? hFile, ref uint pdwFileSizeHigh); public static unsafe uint SFileGetFilePointer( - MpqFileSafeHandle hFile + MpqFileSafeHandle? hFile ) { - if (hFile.IsInvalid || hFile.IsClosed) + if (hFile == null || hFile.IsInvalid || hFile.IsClosed) throw new InvalidOperationException(); IntPtr handle = hFile.DangerousGetHandle(); @@ -148,7 +145,7 @@ namespace StormLibSharp.Native } //public static unsafe uint SFileGetFileSize( - // MpqFileSafeHandle hFile + // MpqFileSafeHandle? hFile // ) //{ // if (hFile.IsInvalid || hFile.IsClosed) @@ -161,7 +158,7 @@ namespace StormLibSharp.Native [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern uint SFileSetFilePointer( - MpqFileSafeHandle hFile, + MpqFileSafeHandle? hFile, uint lFilePos, ref uint plFilePosHigh, uint dwMoveMethod @@ -169,7 +166,7 @@ namespace StormLibSharp.Native [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileReadFile( - MpqFileSafeHandle hFile, + MpqFileSafeHandle? hFile, IntPtr lpBuffer, uint dwToRead, out uint pdwRead, @@ -180,7 +177,7 @@ namespace StormLibSharp.Native public static extern bool SFileCloseFile(IntPtr hFile); [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] - public static extern bool SFileCloseFile(MpqFileSafeHandle hFile); + public static extern bool SFileCloseFile(MpqFileSafeHandle? hFile); #region Retrieving info about a file in the archive [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] @@ -194,7 +191,7 @@ namespace StormLibSharp.Native [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileGetFileInfo( - MpqArchiveSafeHandle hMpqOrFile, + MpqArchiveSafeHandle? hMpqOrFile, SFileInfoClass InfoClass, IntPtr pvFileInfo, uint cbFileInfoSize, @@ -212,7 +209,7 @@ namespace StormLibSharp.Native [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileGetFileName( - MpqFileSafeHandle hFile, + MpqFileSafeHandle? hFile, [MarshalAs(UnmanagedType.LPStr)] out string szFileName ); @@ -225,7 +222,7 @@ namespace StormLibSharp.Native [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileExtractFile( - MpqArchiveSafeHandle hMpq, + MpqArchiveSafeHandle? hMpq, [MarshalAs(UnmanagedType.LPStr)] string szToExtract, [MarshalAs(UnmanagedType.LPTStr)] string szExtracted, uint dwSearchScope @@ -236,7 +233,7 @@ namespace StormLibSharp.Native #region Functions for file and archive verification [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileGetFileChecksums( - MpqArchiveSafeHandle hMpq, + MpqArchiveSafeHandle? hMpq, [MarshalAs(UnmanagedType.LPStr)] string szFileName, out uint pdwCrc32, IntPtr pMD5 @@ -244,26 +241,26 @@ namespace StormLibSharp.Native [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern uint SFileVerifyFile( - MpqArchiveSafeHandle hMpq, + MpqArchiveSafeHandle? hMpq, [MarshalAs(UnmanagedType.LPStr)] string szFileName, uint dwFlags ); [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern int SFileVerifyRawData( - MpqArchiveSafeHandle hMpq, + MpqArchiveSafeHandle? hMpq, uint dwWhatToVerify, [MarshalAs(UnmanagedType.LPStr)] string szFileName ); [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] - public static extern uint SFileVerifyArchive(MpqArchiveSafeHandle hMpq); + public static extern uint SFileVerifyArchive(MpqArchiveSafeHandle? hMpq); #endregion #region Functions for file searching [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern IntPtr SFileFindFirstFile( - MpqArchiveSafeHandle hMpq, + MpqArchiveSafeHandle? hMpq, [MarshalAs(UnmanagedType.LPStr)] string szMask, out _SFILE_FIND_DATA lpFindFileData, [MarshalAs(UnmanagedType.LPStr)] string szListFile @@ -280,7 +277,7 @@ namespace StormLibSharp.Native [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern IntPtr SListFileFindFirstFile( - MpqArchiveSafeHandle hMpq, + MpqArchiveSafeHandle? hMpq, [MarshalAs(UnmanagedType.LPStr)] string szListFile, [MarshalAs(UnmanagedType.LPStr)] string szMask, [In, Out] ref _SFILE_FIND_DATA lpFindFileData @@ -299,7 +296,7 @@ namespace StormLibSharp.Native #region Locale support [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern int SFileEnumLocales( - MpqArchiveSafeHandle hMpq, + MpqArchiveSafeHandle? hMpq, [MarshalAs(UnmanagedType.LPStr)] string szFileName, IntPtr plcLocales, ref uint pdwMaxLocales, @@ -310,7 +307,7 @@ namespace StormLibSharp.Native #region Support for adding files to the MPQ [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileCreateFile( - MpqArchiveSafeHandle hMpq, + MpqArchiveSafeHandle? hMpq, [MarshalAs(UnmanagedType.LPStr)] string szArchiveName, ulong fileTime, uint dwFileSize, @@ -321,18 +318,18 @@ namespace StormLibSharp.Native [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileWriteFile( - MpqFileSafeHandle hFile, + MpqFileSafeHandle? hFile, IntPtr pvData, uint dwSize, uint dwCompression ); [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] - public static extern bool SFileFinishFile(MpqFileSafeHandle hFile); + public static extern bool SFileFinishFile(MpqFileSafeHandle? hFile); [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileAddFileEx( - MpqArchiveSafeHandle hMpq, + MpqArchiveSafeHandle? hMpq, [MarshalAs(UnmanagedType.LPTStr)] string szFileName, [MarshalAs(UnmanagedType.LPStr)] string szArchivedName, uint dwFlags, @@ -342,7 +339,7 @@ namespace StormLibSharp.Native [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileAddFile( - MpqArchiveSafeHandle hMpq, + MpqArchiveSafeHandle? hMpq, [MarshalAs(UnmanagedType.LPTStr)] string szFileName, [MarshalAs(UnmanagedType.LPStr)] string szArchivedName, uint dwFlags @@ -350,7 +347,7 @@ namespace StormLibSharp.Native [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileAddWave( - MpqArchiveSafeHandle hMpq, + MpqArchiveSafeHandle? hMpq, [MarshalAs(UnmanagedType.LPTStr)] string szFileName, [MarshalAs(UnmanagedType.LPStr)] string szArchivedName, uint dwFlags, @@ -359,21 +356,21 @@ namespace StormLibSharp.Native [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileRemoveFile( - MpqArchiveSafeHandle hMpq, + MpqArchiveSafeHandle? hMpq, [MarshalAs(UnmanagedType.LPStr)] string szFileName, uint dwSearchScope ); [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileRenameFile( - MpqArchiveSafeHandle hMpq, + MpqArchiveSafeHandle? hMpq, [MarshalAs(UnmanagedType.LPStr)] string szOldFileName, [MarshalAs(UnmanagedType.LPStr)] string szNewFileName ); [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileSetFileLocale( - MpqFileSafeHandle hFile, + MpqFileSafeHandle? hFile, uint lcNewLocale ); @@ -382,7 +379,7 @@ namespace StormLibSharp.Native [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileSetAddFileCallback( - MpqArchiveSafeHandle hMpq, + MpqArchiveSafeHandle? hMpq, SFILE_ADDFILE_CALLBACK AddFileCB, IntPtr pvUserData ); diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/SFileInfoClass.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/SFileInfoClass.cs index f8ef7a92..a53c2ba2 100644 --- a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/SFileInfoClass.cs +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/SFileInfoClass.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace StormLibSharp.Native +namespace StormLibSharp.Native { internal enum SFileInfoClass { diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/SFileOpenArchiveFlags.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/SFileOpenArchiveFlags.cs index d444f2be..24238c75 100644 --- a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/SFileOpenArchiveFlags.cs +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/SFileOpenArchiveFlags.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace StormLibSharp.Native { diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/Win32Methods.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/Win32Methods.cs index 15596c07..f0d19f26 100644 --- a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/Win32Methods.cs +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/Win32Methods.cs @@ -1,11 +1,8 @@ using System; -using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO.MemoryMappedFiles; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; namespace StormLibSharp.Native { @@ -30,12 +27,12 @@ namespace StormLibSharp.Native [DllImport("kernel32", SetLastError = false, ExactSpelling = false)] public static extern int GetLastError(); - public static string GetFileNameOfMemoryMappedFile(MemoryMappedFile file) + public static string? GetFileNameOfMemoryMappedFile(MemoryMappedFile file) { const uint size = 522; IntPtr path = Marshal.AllocCoTaskMem(unchecked((int)size)); // MAX_PATH + 1 char - string result = null; + string? result; try { // constant 0x2 = VOLUME_NAME_NT From 42239919e5669c1ec390f2f226eb10ea1ffae7ee Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sun, 2 Nov 2025 22:07:40 -0500 Subject: [PATCH 4/8] Enable StormLib for all .NET versions --- README.MD | 2 +- .../SabreTools.Serialization.csproj | 8 --- .../_EXTERNAL/CascLibSharp/CascApi.cs | 19 +++++++ .../CascLibSharp/CascStorageContext.cs | 33 +++++++++++++ .../_EXTERNAL/StormLibSharp/MpqArchive.cs | 19 ------- .../StormLibSharp/Native/Win32Methods.cs | 49 +------------------ 6 files changed, 54 insertions(+), 76 deletions(-) diff --git a/README.MD b/README.MD index f0180318..a3f96126 100644 --- a/README.MD +++ b/README.MD @@ -9,7 +9,7 @@ Find the link to the Nuget package [here](https://www.nuget.org/packages/SabreTo The following non-project libraries (or ports thereof) are used for file handling: - [GrindCore.SharpCompress](https://github.com/Nanook/GrindCore.SharpCompress) - Common archive format extraction -- [StormLibSharp](https://github.com/robpaveza/stormlibsharp) - MoPaQ extraction [Unused in .NET Framework 2.0/3.5/4.0 and non-Windows builds due to Windows-specific libraries] +- [StormLibSharp](https://github.com/robpaveza/stormlibsharp) - MoPaQ extraction [Unused in non-Windows builds due to Windows-specific libraries] The following projects have influenced this library: diff --git a/SabreTools.Serialization/SabreTools.Serialization.csproj b/SabreTools.Serialization/SabreTools.Serialization.csproj index 96eab79e..605d3393 100644 --- a/SabreTools.Serialization/SabreTools.Serialization.csproj +++ b/SabreTools.Serialization/SabreTools.Serialization.csproj @@ -41,14 +41,6 @@ net6.0;net7.0;net8.0;net9.0 - - - - $(DefaultItemExcludes); - _EXTERNAL\** - - - diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascApi.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascApi.cs index 92d2ca7b..0b390eee 100644 --- a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascApi.cs +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascApi.cs @@ -168,11 +168,19 @@ namespace CascLibSharp string build = "fre"; #endif +#if NET20 || NET35 + string mainPath = Path.Combine(directory, Path.Combine("CascLib", Path.Combine(build, Path.Combine(arch, "CascLib.dll")))); +#else string mainPath = Path.Combine(directory, "CascLib", build, arch, "CascLib.dll"); +#endif if (File.Exists(mainPath)) return FromFile(mainPath); +#if NET20 || NET35 + string alternatePath = Path.Combine(directory, Path.Combine("CascLib", Path.Combine(arch, "CascLib.dll"))); +#else string alternatePath = Path.Combine(directory, "CascLib", arch, "CascLib.dll"); +#endif if (File.Exists(mainPath)) return FromFile(alternatePath); @@ -183,12 +191,23 @@ namespace CascLibSharp throw new FileNotFoundException(string.Format("Could not locate a copy of CascLib.dll to load. The following paths were tried:\n\t{0}\n\t{1}\n\t{2}\n\nEnsure that an architecture-appropriate copy of CascLib.dll is included in your project.", mainPath, alternatePath, localPath)); } +#if NET20 || NET35 + private static CascApi? _sharedInstance = null; +#else private static Lazy _sharedInstance = new Lazy(Load); +#endif public static CascApi Instance { get { +#if NET20 || NET35 + if (_sharedInstance == null) + _sharedInstance = Load(); + + return _sharedInstance; +#else return _sharedInstance.Value; +#endif } } diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascStorageContext.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascStorageContext.cs index 27665adb..6ef5b531 100644 --- a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascStorageContext.cs +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascStorageContext.cs @@ -11,10 +11,17 @@ namespace CascLibSharp { private CascApi _api; private CascStorageSafeHandle? _handle; +#if NET20 || NET35 + private bool? _hasListfile = null; + private CascKnownClient? _clientType = null; + private long? _fileCount = null; + private int? _gameBuild = null; +#else private Lazy _hasListfile; private Lazy _clientType; private Lazy _fileCount; private Lazy _gameBuild; +#endif /// /// Creates a new CascStorageContext for the specified path. @@ -29,10 +36,12 @@ namespace CascLibSharp throw new CascException(); _handle.Api = _api; +#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER _hasListfile = new Lazy(CheckHasListfile); _clientType = new Lazy(GetClient); _fileCount = new Lazy(GetFileCount); _gameBuild = new Lazy(GetGameBuild); +#endif } /// @@ -128,7 +137,11 @@ namespace CascLibSharp if (_handle == null || _handle.IsInvalid) throw new ObjectDisposedException("CascStorageContext"); +#if NET20 || NET35 + if (this.GameClient == CascKnownClient.WorldOfWarcraft && string.IsNullOrEmpty(listFilePath)) +#else if (this.GameClient == CascKnownClient.WorldOfWarcraft && string.IsNullOrWhiteSpace(listFilePath)) +#endif throw new ArgumentNullException("listFilePath"); CascFindData cfd = new CascFindData(); @@ -158,7 +171,11 @@ namespace CascLibSharp throw new CascException(); CascStorageFeatures features = (CascStorageFeatures)storageInfo; +#if NET20 || NET35 + if ((features & CascStorageFeatures.HasListfile) != 0) +#else if (features.HasFlag(CascStorageFeatures.HasListfile)) +#endif return true; return false; @@ -209,6 +226,10 @@ namespace CascLibSharp { get { +#if NET20 || NET35 + if (_hasListfile == null) + _hasListfile = CheckHasListfile(); +#endif return _hasListfile.Value; } } @@ -220,6 +241,10 @@ namespace CascLibSharp { get { +#if NET20 || NET35 + if (_fileCount == null) + _fileCount = GetFileCount(); +#endif return _fileCount.Value; } } @@ -231,6 +256,10 @@ namespace CascLibSharp { get { +#if NET20 || NET35 + if (_gameBuild == null) + _gameBuild = GetGameBuild(); +#endif return _gameBuild.Value; } } @@ -242,6 +271,10 @@ namespace CascLibSharp { get { +#if NET20 || NET35 + if (_clientType == null) + _clientType = GetClient(); +#endif return _clientType.Value; } } diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqArchive.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqArchive.cs index d3866e11..5c3671ce 100644 --- a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqArchive.cs +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqArchive.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.ComponentModel; using System.IO; -using System.IO.MemoryMappedFiles; using StormLibSharp.Native; namespace StormLibSharp @@ -30,24 +29,6 @@ namespace StormLibSharp throw new Win32Exception(); // Implicitly calls GetLastError } - public MpqArchive(MemoryMappedFile file, FileAccess accessType) - { - _accessType = accessType; - string? fileName = Win32Methods.GetFileNameOfMemoryMappedFile(file); - if (fileName == null) - throw new ArgumentException("Could not retrieve the name of the file to initialize."); - - SFileOpenArchiveFlags flags = SFileOpenArchiveFlags.TypeIsMemoryMapped; - if (accessType == FileAccess.Read) - flags |= SFileOpenArchiveFlags.AccessReadOnly; - else - flags |= SFileOpenArchiveFlags.AccessReadWriteShare; - - // constant 2 = SFILE_OPEN_HARD_DISK_FILE - if (!NativeMethods.SFileOpenArchive(fileName, 2, flags, out _handle)) - throw new Win32Exception(); // Implicitly calls GetLastError - } - private MpqArchive(string filePath, MpqArchiveVersion version, MpqFileStreamAttributes listfileAttributes, MpqFileStreamAttributes attributesFileAttributes, int maxFileCount) { if (maxFileCount < 0) diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/Win32Methods.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/Win32Methods.cs index f0d19f26..bf7ba6d0 100644 --- a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/Win32Methods.cs +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/Win32Methods.cs @@ -1,57 +1,10 @@ -using System; -using System.ComponentModel; -using System.Diagnostics; -using System.IO.MemoryMappedFiles; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; namespace StormLibSharp.Native { internal static class Win32Methods { - [DllImport("kernel32", ExactSpelling = false, SetLastError = true)] - public static extern uint GetMappedFileName( - IntPtr hProcess, - IntPtr fileHandle, - IntPtr lpFilename, - uint nSize - ); - - [DllImport("kernel32", ExactSpelling = false, SetLastError = true)] - public static extern uint GetFinalPathNameByHandle( - IntPtr hFile, - IntPtr lpszFilePath, - uint cchFilePath, - uint dwFlags - ); - [DllImport("kernel32", SetLastError = false, ExactSpelling = false)] public static extern int GetLastError(); - - public static string? GetFileNameOfMemoryMappedFile(MemoryMappedFile file) - { - const uint size = 522; - IntPtr path = Marshal.AllocCoTaskMem(unchecked((int)size)); // MAX_PATH + 1 char - - string? result; - try - { - // constant 0x2 = VOLUME_NAME_NT - uint test = GetFinalPathNameByHandle(file.SafeMemoryMappedFileHandle.DangerousGetHandle(), path, size, 0x2); - if (test != 0) - throw new Win32Exception(); - - result = Marshal.PtrToStringAuto(path); - } - catch - { - uint test = GetMappedFileName(Process.GetCurrentProcess().Handle, file.SafeMemoryMappedFileHandle.DangerousGetHandle(), path, size); - if (test != 0) - throw new Win32Exception(); - - result = Marshal.PtrToStringAuto(path); - } - - return result; - } } } From 48bea551339ed5fd0734303023b4ff6b0574d0a7 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sun, 2 Nov 2025 22:20:21 -0500 Subject: [PATCH 5/8] Enable extraction on all .NET versions (not OSes) --- .../Wrappers/MoPaQ.Extraction.cs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/SabreTools.Serialization/Wrappers/MoPaQ.Extraction.cs b/SabreTools.Serialization/Wrappers/MoPaQ.Extraction.cs index 5a98ed6b..2f47019b 100644 --- a/SabreTools.Serialization/Wrappers/MoPaQ.Extraction.cs +++ b/SabreTools.Serialization/Wrappers/MoPaQ.Extraction.cs @@ -1,8 +1,6 @@ using System; -#if NET452_OR_GREATER || NETCOREAPP using System.IO; using StormLibSharp; -#endif namespace SabreTools.Serialization.Wrappers { @@ -11,7 +9,6 @@ namespace SabreTools.Serialization.Wrappers /// public bool Extract(string outputDirectory, bool includeDebug) { -#if NET452_OR_GREATER || NETCOREAPP try { // Limit use to Windows only @@ -62,24 +59,19 @@ namespace SabreTools.Serialization.Wrappers { mpqArchive.ExtractFile(sub, filename); } - catch (System.Exception ex) + catch (Exception ex) { - if (includeDebug) System.Console.WriteLine(ex); + if (includeDebug) Console.WriteLine(ex); } } return true; } - catch (System.Exception ex) + catch (Exception ex) { - if (includeDebug) System.Console.WriteLine(ex); + if (includeDebug) Console.WriteLine(ex); return false; } -#else - Console.WriteLine("Extraction is not supported for this framework!"); - Console.WriteLine(); - return false; -#endif } } } From 22167a9617db201d47a934538827266f7315a7ed Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sun, 2 Nov 2025 22:27:02 -0500 Subject: [PATCH 6/8] Recursion isn't needed now --- .github/workflows/build_and_test.yml | 1 - .github/workflows/check_pr.yml | 2 -- 2 files changed, 3 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 5e52f4a8..3c75ff14 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -12,7 +12,6 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - submodules: recursive - name: Setup .NET uses: actions/setup-dotnet@v4 diff --git a/.github/workflows/check_pr.yml b/.github/workflows/check_pr.yml index cccf5f5d..c9e23ecd 100644 --- a/.github/workflows/check_pr.yml +++ b/.github/workflows/check_pr.yml @@ -7,8 +7,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - with: - submodules: recursive - name: Setup .NET uses: actions/setup-dotnet@v4 From d3e340ae3917fd56c556dbf9adc62e3d32309dc4 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sun, 2 Nov 2025 23:21:29 -0500 Subject: [PATCH 7/8] Add and use MPQ filename constants --- .../Models/MoPaQ/Constants.cs | 24 +++++++++++++++++++ .../Wrappers/MoPaQ.Extraction.cs | 3 ++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/SabreTools.Serialization/Models/MoPaQ/Constants.cs b/SabreTools.Serialization/Models/MoPaQ/Constants.cs index 0192e812..02fecae5 100644 --- a/SabreTools.Serialization/Models/MoPaQ/Constants.cs +++ b/SabreTools.Serialization/Models/MoPaQ/Constants.cs @@ -79,5 +79,29 @@ namespace SabreTools.Data.Models.MoPaQ public const uint MPQ_KEY_HASH_TABLE = 0xC3AF3770; #endregion + + #region Special Entry Names + + /// + /// Name of internal listfile + /// + public const string LISTFILE_NAME = "(listfile)"; + + /// + /// Name of internal signature + /// + public const string SIGNATURE_NAME = "(signature)"; + + /// + /// Name of internal attributes file + /// + public const string ATTRIBUTES_NAME = "(attributes)"; + + /// + /// Patch metadata + /// + public const string PATCH_METADATA_NAME = "(patch_metadata)"; + + #endregion } } diff --git a/SabreTools.Serialization/Wrappers/MoPaQ.Extraction.cs b/SabreTools.Serialization/Wrappers/MoPaQ.Extraction.cs index 2f47019b..6b391126 100644 --- a/SabreTools.Serialization/Wrappers/MoPaQ.Extraction.cs +++ b/SabreTools.Serialization/Wrappers/MoPaQ.Extraction.cs @@ -1,6 +1,7 @@ using System; using System.IO; using StormLibSharp; +using static SabreTools.Data.Models.MoPaQ.Constants; namespace SabreTools.Serialization.Wrappers { @@ -24,7 +25,7 @@ namespace SabreTools.Serialization.Wrappers // Try to open the archive and listfile var mpqArchive = new MpqArchive(Filename, FileAccess.Read); string? listfile = null; - MpqFileStream listStream = mpqArchive.OpenFile("(listfile)"); + MpqFileStream listStream = mpqArchive.OpenFile(LISTFILE_NAME); // If we can't read the listfile, we just return if (!listStream.CanRead) From 7bf6e6f344985bcf8d0483b7a3f51d49eadbbbf4 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 3 Nov 2025 08:36:12 -0500 Subject: [PATCH 8/8] Code cleanup and slight modernization --- .../_EXTERNAL/CascLibSharp/CascApi.cs | 19 +++- .../_EXTERNAL/CascLibSharp/CascFileStream.cs | 10 +- .../_EXTERNAL/CascLibSharp/CascFoundFile.cs | 8 +- .../CascLibSharp/CascStorageContext.cs | 107 +++++++++++------- .../CascLibSharp/Native/CoTaskMem.cs | 2 +- .../CascLibSharp/Native/NativeMethods.cs | 14 +-- .../_EXTERNAL/StormLibSharp/MpqArchive.cs | 33 +++--- .../_EXTERNAL/StormLibSharp/MpqFileStream.cs | 22 ++-- .../Native/MpqArchiveSafeHandle.cs | 4 +- .../StormLibSharp/Native/MpqFileSafeHandle.cs | 4 +- .../StormLibSharp/Native/NativeMethods.cs | 34 +++++- 11 files changed, 158 insertions(+), 99 deletions(-) diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascApi.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascApi.cs index 0b390eee..0f6568fd 100644 --- a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascApi.cs +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascApi.cs @@ -10,12 +10,14 @@ namespace CascLibSharp internal sealed class CascApi { #region Imported method signature type definitions + [return: MarshalAs(UnmanagedType.I1)] [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate bool FnCascOpenStorage( [MarshalAs(UnmanagedType.LPTStr)] string szDataPath, uint dwFlags, out CascStorageSafeHandle phStorage); + [return: MarshalAs(UnmanagedType.I1)] [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate bool FnCascGetStorageInfo( @@ -24,6 +26,7 @@ namespace CascLibSharp ref uint pvStorageInfo, IntPtr cbStorageInfo, ref uint pcbLengthNeeded); + [return: MarshalAs(UnmanagedType.I1)] [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate bool FnCascCloseStorage(IntPtr hStorage); @@ -35,6 +38,7 @@ namespace CascLibSharp ref QueryKey pIndexKey, uint dwFlags, out CascStorageFileSafeHandle phFile); + [return: MarshalAs(UnmanagedType.I1)] [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate bool FnCascOpenFileByEncodingKey( @@ -42,6 +46,7 @@ namespace CascLibSharp ref QueryKey pEncodingKey, uint dwFlags, out CascStorageFileSafeHandle phFile); + [return: MarshalAs(UnmanagedType.I1)] [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate bool FnCascOpenFile( @@ -50,6 +55,7 @@ namespace CascLibSharp uint dwLocale, uint dwFlags, out CascStorageFileSafeHandle phFile); + [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate uint FnCascGetFileSize( CascStorageFileSafeHandle hFile, @@ -60,6 +66,7 @@ namespace CascLibSharp uint lFilePos, ref uint plFilePosHigh, SeekOrigin dwMoveMethod); + [return: MarshalAs(UnmanagedType.I1)] [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate bool FnCascReadFile( @@ -67,6 +74,7 @@ namespace CascLibSharp IntPtr lpBuffer, uint dwToRead, out uint dwRead); + [return: MarshalAs(UnmanagedType.I1)] [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate bool FnCascCloseFile(IntPtr hFile); @@ -77,17 +85,21 @@ namespace CascLibSharp [MarshalAs(UnmanagedType.LPStr)] string szMask, ref CascFindData pFindData, [MarshalAs(UnmanagedType.LPTStr)] string? szListFile); + [return: MarshalAs(UnmanagedType.I1)] [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate bool FnCascFindNextFile( CascFileEnumerationSafeHandle hFind, ref CascFindData pFindData); + [return: MarshalAs(UnmanagedType.I1)] [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate bool FnCascFindClose(IntPtr hFind); + #endregion #region Field and method defs + public readonly FnCascOpenStorage? CascOpenStorage; public readonly FnCascGetStorageInfo? CascGetStorageInfo; public readonly FnCascCloseStorage? CascCloseStorage; @@ -99,8 +111,7 @@ namespace CascLibSharp public readonly FnCascGetFileSize? CascGetFileSizeBase; public long CascGetFileSize(CascStorageFileSafeHandle hFile) { - uint high; - uint low = CascGetFileSizeBase!(hFile, out high); + uint low = CascGetFileSizeBase!(hFile, out uint high); long result = (high << 32) | low; return result; @@ -127,6 +138,7 @@ namespace CascLibSharp public readonly FnCascFindFirstFile? CascFindFirstFile; public readonly FnCascFindNextFile? CascFindNextFile; public readonly FnCascFindClose? CascFindClose; + #endregion internal CascApi(IntPtr hModule) @@ -154,6 +166,7 @@ namespace CascLibSharp IntPtr procAddr = NativeMethods.GetProcAddress(hModule, procName); if (procAddr == IntPtr.Zero) throw new Win32Exception(); + target = Marshal.GetDelegateForFunctionPointer(procAddr, typeof(T)) as T; } @@ -194,7 +207,7 @@ namespace CascLibSharp #if NET20 || NET35 private static CascApi? _sharedInstance = null; #else - private static Lazy _sharedInstance = new Lazy(Load); + private static readonly Lazy _sharedInstance = new(Load); #endif public static CascApi Instance { diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascFileStream.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascFileStream.cs index 6f4b46da..3ab5e5f9 100644 --- a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascFileStream.cs +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascFileStream.cs @@ -10,8 +10,8 @@ namespace CascLibSharp /// public class CascFileStream : Stream { - private CascStorageFileSafeHandle _handle; - private CascApi _api; + private readonly CascStorageFileSafeHandle _handle; + private readonly CascApi _api; internal CascFileStream(CascStorageFileSafeHandle? handle, CascApi? api) { @@ -110,11 +110,11 @@ namespace CascLibSharp AssertValidHandle(); long len = Length; if (offset < 0 || offset > len) - throw new ArgumentException("offset"); + throw new ArgumentException(nameof(offset)); if (count < 0) - throw new ArgumentException("count"); + throw new ArgumentException(nameof(count)); if (offset + count > len) - throw new ArgumentException("count"); + throw new ArgumentException(nameof(count)); uint read = 0; fixed (byte* pBuffer = &buffer[0]) diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascFoundFile.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascFoundFile.cs index a7576400..d8c966f0 100644 --- a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascFoundFile.cs +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascFoundFile.cs @@ -9,9 +9,9 @@ namespace CascLibSharp public class CascFoundFile { #if NET20 || NET35 || NET40 - private WeakReference _ownerContext; + private readonly WeakReference _ownerContext; #else - private WeakReference _ownerContext; + private readonly WeakReference _ownerContext; #endif internal CascFoundFile(string? fileName, IntPtr plainName, byte[] encodingKey, CascLocales locales, long fileSize, CascStorageContext ownerContext) @@ -33,18 +33,22 @@ namespace CascLibSharp /// Gets the full path to this file. /// public string? FileName { get; private set; } + /// /// Gets the plain (no directory-qualified) file name of this file. /// public string? PlainFileName { get; private set; } + /// /// Gets the CASC encoding key for this file. /// public byte[] EncodingKey { get; private set; } + /// /// Gets the locales supported by this resource. /// public CascLocales Locales { get; private set; } + /// /// Gets the length of the file in bytes. /// diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascStorageContext.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascStorageContext.cs index 6ef5b531..417cc08f 100644 --- a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascStorageContext.cs +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/CascStorageContext.cs @@ -9,7 +9,7 @@ namespace CascLibSharp /// public class CascStorageContext : IDisposable { - private CascApi _api; + private readonly CascApi _api; private CascStorageSafeHandle? _handle; #if NET20 || NET35 private bool? _hasListfile = null; @@ -17,10 +17,10 @@ namespace CascLibSharp private long? _fileCount = null; private int? _gameBuild = null; #else - private Lazy _hasListfile; - private Lazy _clientType; - private Lazy _fileCount; - private Lazy _gameBuild; + private readonly Lazy _hasListfile; + private readonly Lazy _clientType; + private readonly Lazy _fileCount; + private readonly Lazy _gameBuild; #endif /// @@ -82,20 +82,19 @@ namespace CascLibSharp if (_handle == null || _handle.IsInvalid) throw new ObjectDisposedException("CascStorageContext"); - using (CoTaskMem mem = CoTaskMem.FromBytes(indexKey)) - { - QueryKey qk = new QueryKey(); - qk.cbData = unchecked((uint)indexKey.Length); - qk.pbData = mem.Pointer; + using CoTaskMem mem = CoTaskMem.FromBytes(indexKey); - CascStorageFileSafeHandle hFile; - if (!_api.CascOpenFileByIndexKey!(_handle, ref qk, 0, out hFile)) - throw new CascException(); + var qk = new QueryKey(); + qk.cbData = unchecked((uint)indexKey.Length); + qk.pbData = mem.Pointer; - hFile.Api = _api; + CascStorageFileSafeHandle hFile; + if (!_api.CascOpenFileByIndexKey!(_handle, ref qk, 0, out hFile)) + throw new CascException(); - return new CascFileStream(hFile, _api); - } + hFile.Api = _api; + + return new CascFileStream(hFile, _api); } /// @@ -110,20 +109,19 @@ namespace CascLibSharp if (_handle == null || _handle.IsInvalid) throw new ObjectDisposedException("CascStorageContext"); - using (CoTaskMem mem = CoTaskMem.FromBytes(encodingKey)) - { - QueryKey qk = new QueryKey(); - qk.cbData = unchecked((uint)encodingKey.Length); - qk.pbData = mem.Pointer; + using CoTaskMem mem = CoTaskMem.FromBytes(encodingKey); - CascStorageFileSafeHandle hFile; - if (!_api.CascOpenFileByEncodingKey!(_handle, ref qk, 0, out hFile)) - throw new CascException(); + var qk = new QueryKey(); + qk.cbData = unchecked((uint)encodingKey.Length); + qk.pbData = mem.Pointer; - hFile.Api = _api; + CascStorageFileSafeHandle hFile; + if (!_api.CascOpenFileByEncodingKey!(_handle, ref qk, 0, out hFile)) + throw new CascException(); - return new CascFileStream(hFile, _api); - } + hFile.Api = _api; + + return new CascFileStream(hFile, _api); } /// @@ -138,26 +136,24 @@ namespace CascLibSharp throw new ObjectDisposedException("CascStorageContext"); #if NET20 || NET35 - if (this.GameClient == CascKnownClient.WorldOfWarcraft && string.IsNullOrEmpty(listFilePath)) + if (GameClient == CascKnownClient.WorldOfWarcraft && string.IsNullOrEmpty(listFilePath)) #else - if (this.GameClient == CascKnownClient.WorldOfWarcraft && string.IsNullOrWhiteSpace(listFilePath)) + if (GameClient == CascKnownClient.WorldOfWarcraft && string.IsNullOrWhiteSpace(listFilePath)) #endif throw new ArgumentNullException("listFilePath"); - CascFindData cfd = new CascFindData(); - using (var handle = _api.CascFindFirstFile!(_handle, mask, ref cfd, listFilePath)) + var cfd = new CascFindData(); + using var handle = _api.CascFindFirstFile!(_handle, mask, ref cfd, listFilePath); + if (handle.IsInvalid) + yield break; + + handle.Api = _api; + + yield return cfd.ToFoundFile(this); + + while (_api.CascFindNextFile!(handle, ref cfd)) { - if (handle.IsInvalid) - yield break; - - handle.Api = _api; - yield return cfd.ToFoundFile(this); - - while (_api.CascFindNextFile!(handle, ref cfd)) - { - yield return cfd.ToFoundFile(this); - } } } @@ -280,6 +276,7 @@ namespace CascLibSharp } #region IDisposable implementation + /// /// Finalizes the storage context. /// @@ -303,15 +300,16 @@ namespace CascLibSharp /// True if this is being called via the Dispose() method; false if it's being called by the finalizer. protected virtual void Dispose(bool disposing) { - if (disposing && _handle != null) + if (disposing) { - if (!_handle.IsInvalid) + if (_handle != null && !_handle.IsInvalid) { _handle.Close(); _handle = null; } } } + #endregion } @@ -325,74 +323,92 @@ namespace CascLibSharp /// All available locales. /// All = -1, + /// /// No locales. /// + /// None = 0, /// /// Unknown /// Unknown1 = 1, + /// /// English, United States /// EnUs = 2, + /// /// Korean, South Korea /// KoKr = 4, + /// /// Reserved (unknown) /// Reserved = 8, + /// /// French, France /// FrFr = 0x10, + /// /// German, Germany /// DeDe = 0x20, + /// /// Chinese, China /// ZhCn = 0x40, + /// /// Spanish, Spain /// EsEs = 0x80, + /// /// Chinese, Taiwan /// ZhTw = 0x100, + /// /// English, Great Britain /// EnGb = 0x200, + /// /// English, China /// EnCn = 0x400, + /// /// English, Taiwan /// EnTw = 0x800, + /// /// Spanish, Mexico /// EsMx = 0x1000, + /// /// Russian, Russia /// RuRu = 0x2000, + /// /// Portuguese, Brazil /// PtBr = 0x4000, + /// /// Italian, Italy /// ItIt = 0x8000, + /// /// Portuguese, Portugal /// @@ -408,22 +424,27 @@ namespace CascLibSharp /// The game client was unrecognized. /// Unknown = -1, + /// /// Heroes of the Storm /// HeroesOfTheStorm = 0, + /// /// Diablo 3 /// Diablo3 = 1, + /// /// World of Warcraft /// WorldOfWarcraft = 2, + /// /// Overwatch /// Overwatch = 3, + /// /// Starcraft 2 /// diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CoTaskMem.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CoTaskMem.cs index 361d7654..a65eb891 100644 --- a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CoTaskMem.cs +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/CoTaskMem.cs @@ -19,7 +19,7 @@ namespace CascLibSharp.Native public static CoTaskMem FromBytes(byte[] b) { - CoTaskMem result = new CoTaskMem(b.Length); + var result = new CoTaskMem(b.Length); Marshal.Copy(b, 0, result._mem, b.Length); return result; diff --git a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/NativeMethods.cs b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/NativeMethods.cs index 1096d05f..78069085 100644 --- a/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/NativeMethods.cs +++ b/SabreTools.Serialization/_EXTERNAL/CascLibSharp/Native/NativeMethods.cs @@ -44,7 +44,7 @@ namespace CascLibSharp.Native internal static class GameConverterExtensions { - private static Dictionary GameClientMap = new Dictionary + private static readonly Dictionary GameClientMap = new() { { CascGameId.Hots, CascKnownClient.HeroesOfTheStorm }, { CascGameId.Wow6, CascKnownClient.WorldOfWarcraft }, @@ -53,7 +53,7 @@ namespace CascLibSharp.Native { CascGameId.Starcraft2, CascKnownClient.Starcraft2 }, }; - private static Dictionary ClientGameMap = new Dictionary() + private static readonly Dictionary ClientGameMap = new() { { CascKnownClient.HeroesOfTheStorm, CascGameId.Hots }, { CascKnownClient.WorldOfWarcraft, CascGameId.Wow6 }, @@ -64,8 +64,7 @@ namespace CascLibSharp.Native public static CascKnownClient ToKnownClient(this CascGameId gameId) { - CascKnownClient result; - if (!GameClientMap.TryGetValue(gameId, out result)) + if (!GameClientMap.TryGetValue(gameId, out CascKnownClient result)) result = CascKnownClient.Unknown; return result; @@ -73,15 +72,12 @@ namespace CascLibSharp.Native public static CascGameId ToGameId(this CascKnownClient knownClient) { - CascGameId result; - if (!ClientGameMap.TryGetValue(knownClient, out result)) + if (!ClientGameMap.TryGetValue(knownClient, out CascGameId result)) throw new ArgumentException("Invalid client."); return result; } } - -#pragma warning disable 649 internal struct QueryKey { public IntPtr pbData; @@ -107,6 +103,7 @@ namespace CascLibSharp.Native { fileName = Marshal.PtrToStringAnsi(new IntPtr(pFileName)); } + byte[] encodingKey = new byte[16]; fixed (void* pEncodingKey = EncodingKey) { @@ -116,5 +113,4 @@ namespace CascLibSharp.Native return new CascFoundFile(fileName, szPlainName, encodingKey, (CascLocales)dwLocaleFlags, dwFileSize, ownerContext); } } -#pragma warning restore 649 } diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqArchive.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqArchive.cs index 5c3671ce..9817fef9 100644 --- a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqArchive.cs +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqArchive.cs @@ -9,15 +9,17 @@ namespace StormLibSharp public class MpqArchive : IDisposable { private MpqArchiveSafeHandle? _handle; - private List _openFiles = new List(); - private FileAccess _accessType; - private List _compactCallbacks = new List(); + private readonly List _openFiles = []; + private readonly FileAccess _accessType; + private readonly List _compactCallbacks = []; private SFILE_COMPACT_CALLBACK? _compactCallback; #region Constructors / Factories + public MpqArchive(string filePath, FileAccess accessType) { _accessType = accessType; + SFileOpenArchiveFlags flags = SFileOpenArchiveFlags.TypeIsFile; if (accessType == FileAccess.Read) flags |= SFileOpenArchiveFlags.AccessReadOnly; @@ -63,9 +65,11 @@ namespace StormLibSharp { return new MpqArchive(mpqPath, version, listfileAttributes, attributesFileAttributes, maxFileCount); } + #endregion #region Properties + // TODO: Move to common location. // This is a global setting, not per-archive setting. @@ -91,7 +95,8 @@ namespace StormLibSharp set { if (value < 0 || value > uint.MaxValue) - throw new ArgumentException("value"); + throw new ArgumentException(nameof(value)); + VerifyHandle(); if (!NativeMethods.SFileSetMaxFileCount(_handle, unchecked((uint)value))) @@ -113,6 +118,7 @@ namespace StormLibSharp return NativeMethods.SFileIsPatchedArchive(_handle); } } + #endregion public void Flush() @@ -145,7 +151,7 @@ namespace StormLibSharp private void _OnCompact(IntPtr pvUserData, uint dwWorkType, ulong bytesProcessed, ulong totalBytes) { - MpqArchiveCompactingEventArgs args = new MpqArchiveCompactingEventArgs(dwWorkType, bytesProcessed, totalBytes); + var args = new MpqArchiveCompactingEventArgs(dwWorkType, bytesProcessed, totalBytes); OnCompacting(args); } @@ -220,11 +226,10 @@ namespace StormLibSharp { VerifyHandle(); - MpqFileSafeHandle fileHandle; - if (!NativeMethods.SFileOpenFileEx(_handle, fileName, 0, out fileHandle)) + if (!NativeMethods.SFileOpenFileEx(_handle, fileName, 0, out MpqFileSafeHandle fileHandle)) throw new Win32Exception(); - MpqFileStream fs = new MpqFileStream(fileHandle, _accessType, this); + var fs = new MpqFileStream(fileHandle, _accessType, this); _openFiles.Add(fs); return fs; } @@ -255,6 +260,7 @@ namespace StormLibSharp #region IDisposable implementation + public void Dispose() { Dispose(true); @@ -270,16 +276,13 @@ namespace StormLibSharp if (disposing) { // Release owned files first. - if (_openFiles != null) + foreach (var file in _openFiles) { - foreach (var file in _openFiles) - { - file.Dispose(); - } - - _openFiles.Clear(); + file.Dispose(); } + _openFiles.Clear(); + // Release if (_handle != null && !_handle.IsInvalid) { diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqFileStream.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqFileStream.cs index 97616360..c1bfa1fc 100644 --- a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqFileStream.cs +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/MpqFileStream.cs @@ -9,7 +9,7 @@ namespace StormLibSharp public class MpqFileStream : Stream { private MpqFileSafeHandle? _handle; - private FileAccess _accessType; + private readonly FileAccess _accessType; private MpqArchive? _owner; internal MpqFileStream(MpqFileSafeHandle handle, FileAccess accessType, MpqArchive owner) @@ -78,11 +78,11 @@ namespace StormLibSharp public override unsafe int Read(byte[] buffer, int offset, int count) { if (buffer == null) - throw new ArgumentNullException("buffer"); + throw new ArgumentNullException(nameof(buffer)); if (offset > buffer.Length || (offset + count) > buffer.Length) throw new ArgumentException(); if (count < 0) - throw new ArgumentOutOfRangeException("count"); + throw new ArgumentOutOfRangeException(nameof(count)); VerifyHandle(); @@ -108,9 +108,8 @@ namespace StormLibSharp { VerifyHandle(); - uint low, high; - low = unchecked((uint)(offset & 0xffffffffu)); - high = unchecked((uint)(offset >> 32)); + uint low = unchecked((uint)(offset & 0xffffffffu)); + uint high = unchecked((uint)(offset >> 32)); return NativeMethods.SFileSetFilePointer(_handle, low, ref high, (uint)origin); } @@ -124,11 +123,11 @@ namespace StormLibSharp VerifyHandle(); if (buffer == null) - throw new ArgumentNullException("buffer"); + throw new ArgumentNullException(nameof(buffer)); if (offset > buffer.Length || (offset + count) > buffer.Length) throw new ArgumentException(); if (count < 0) - throw new ArgumentOutOfRangeException("count"); + throw new ArgumentOutOfRangeException(nameof(count)); VerifyHandle(); @@ -154,11 +153,8 @@ namespace StormLibSharp _handle = null; } - if (_owner != null) - { - _owner.RemoveOwnedFile(this); - _owner = null; - } + _owner?.RemoveOwnedFile(this); + _owner = null; } } diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/MpqArchiveSafeHandle.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/MpqArchiveSafeHandle.cs index 6813e927..edd96659 100644 --- a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/MpqArchiveSafeHandle.cs +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/MpqArchiveSafeHandle.cs @@ -8,7 +8,7 @@ namespace StormLibSharp.Native public MpqArchiveSafeHandle(IntPtr handle) : base(true) { - this.SetHandle(handle); + SetHandle(handle); } public MpqArchiveSafeHandle() @@ -16,7 +16,7 @@ namespace StormLibSharp.Native protected override bool ReleaseHandle() { - return NativeMethods.SFileCloseArchive(this.handle); + return NativeMethods.SFileCloseArchive(handle); } } } diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/MpqFileSafeHandle.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/MpqFileSafeHandle.cs index 90d5e3a2..ca9d45c8 100644 --- a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/MpqFileSafeHandle.cs +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/MpqFileSafeHandle.cs @@ -8,7 +8,7 @@ namespace StormLibSharp.Native public MpqFileSafeHandle(IntPtr handle) : base(true) { - this.SetHandle(handle); + SetHandle(handle); } public MpqFileSafeHandle() @@ -18,7 +18,7 @@ namespace StormLibSharp.Native protected override bool ReleaseHandle() { - return NativeMethods.SFileCloseFile(this.handle); + return NativeMethods.SFileCloseFile(handle); } } } diff --git a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/NativeMethods.cs b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/NativeMethods.cs index 2e820e19..85d76c4a 100644 --- a/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/NativeMethods.cs +++ b/SabreTools.Serialization/_EXTERNAL/StormLibSharp/Native/NativeMethods.cs @@ -8,13 +8,17 @@ namespace StormLibSharp.Native private const string STORMLIB = "stormlib.dll"; #region Functions for manipulation with StormLib global flags + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern uint SFileGetLocale(); + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern uint SFileSetLocale(uint lcNewLocale); + #endregion #region Functions for archive manipulation + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileOpenArchive( [MarshalAs(UnmanagedType.LPTStr)] string szMpqName, @@ -48,22 +52,27 @@ namespace StormLibSharp.Native [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileFlushArchive(MpqArchiveSafeHandle? hMpq); + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileCloseArchive(IntPtr hMpq); [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileCloseArchive(MpqArchiveSafeHandle? hMpq); + #endregion #region Adds another listfile into MPQ. + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern int SFileAddListFile( MpqArchiveSafeHandle? hMpq, [MarshalAs(UnmanagedType.LPStr)] string szListFile ); + #endregion #region Archive compacting + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileSetCompactCallback( MpqArchiveSafeHandle? hMpq, @@ -77,17 +86,21 @@ namespace StormLibSharp.Native [MarshalAs(UnmanagedType.LPStr)] string szListFile, bool bReserved ); + #endregion #region Maximum file count + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern uint SFileGetMaxFileCount(MpqArchiveSafeHandle? hMpq); [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileSetMaxFileCount(MpqArchiveSafeHandle? hMpq, uint dwMaxFileCount); + #endregion #region Changing (attributes) file + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern uint SFileGetAttributes(MpqArchiveSafeHandle? hMpq); @@ -99,9 +112,11 @@ namespace StormLibSharp.Native MpqArchiveSafeHandle? hMpq, [MarshalAs(UnmanagedType.LPStr)] string szFileName ); + #endregion #region Functions for manipulation with patch archives + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileOpenPatchArchive( MpqArchiveSafeHandle? hMpq, @@ -112,9 +127,11 @@ namespace StormLibSharp.Native [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileIsPatchedArchive(MpqArchiveSafeHandle? hMpq); + #endregion #region Functions for file manipulation + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileHasFile( MpqArchiveSafeHandle? hMpq, @@ -180,6 +197,7 @@ namespace StormLibSharp.Native public static extern bool SFileCloseFile(MpqFileSafeHandle? hFile); #region Retrieving info about a file in the archive + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileGetFileInfo( IntPtr hMpqOrFile, @@ -218,6 +236,7 @@ namespace StormLibSharp.Native IntPtr pvFileInfo, SFileInfoClass infoClass ); + #endregion [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] @@ -231,6 +250,7 @@ namespace StormLibSharp.Native #endregion #region Functions for file and archive verification + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileGetFileChecksums( MpqArchiveSafeHandle? hMpq, @@ -255,9 +275,11 @@ namespace StormLibSharp.Native [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern uint SFileVerifyArchive(MpqArchiveSafeHandle? hMpq); + #endregion #region Functions for file searching + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern IntPtr SFileFindFirstFile( MpqArchiveSafeHandle? hMpq, @@ -291,9 +313,11 @@ namespace StormLibSharp.Native [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SListFileFindClose(IntPtr hFind); + #endregion #region Locale support + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern int SFileEnumLocales( MpqArchiveSafeHandle? hMpq, @@ -302,9 +326,11 @@ namespace StormLibSharp.Native ref uint pdwMaxLocales, uint dwSearchScope ); + #endregion #region Support for adding files to the MPQ + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern bool SFileCreateFile( MpqArchiveSafeHandle? hMpq, @@ -383,9 +409,11 @@ namespace StormLibSharp.Native SFILE_ADDFILE_CALLBACK AddFileCB, IntPtr pvUserData ); + #endregion #region Compression and decompression + [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] public static extern int SCompImplode( IntPtr pvOutBuffer, @@ -429,11 +457,10 @@ namespace StormLibSharp.Native int cbInBuffer ); - #endregion } -#pragma warning disable 0169,0649 +#pragma warning disable CS0169 internal struct SFILE_CREATE_MPQ { public uint cbSize; @@ -448,6 +475,7 @@ namespace StormLibSharp.Native public uint dwRawChunkSize; public uint dwMaxFileCount; } +#pragma warning restore CS0169 internal unsafe struct _SFILE_FIND_DATA { @@ -489,6 +517,4 @@ namespace StormLibSharp.Native public uint dwFileKey; public uint dwFilePos; } -#pragma warning restore 0169,0649 - }