├── lib ├── x64 │ ├── rel │ │ ├── CascLib.dll │ │ ├── StormLib.dll │ │ └── CascLib_dll.pdb │ └── debug │ │ ├── CascLib.dll │ │ ├── StormLib.dll │ │ └── CascLib_dll.pdb ├── x86 │ ├── rel │ │ ├── CascLib.dll │ │ ├── StormLib.dll │ │ └── CascLib_dll.pdb │ └── debug │ │ ├── CascLib.dll │ │ ├── StormLib.dll │ │ └── CascLib_dll.pdb ├── CascLib.h └── StormLib.h ├── src ├── TestConsole │ ├── StormLib.dll │ ├── StormLib.pdb │ ├── app.config │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── TestConsole.csproj │ └── Program.cs ├── StormLibSharp │ ├── Native │ │ ├── SFileOpenArchiveFlags.cs │ │ ├── MpqFileSafeHandle.cs │ │ ├── MpqArchiveSafeHandle.cs │ │ ├── Callbacks.cs │ │ ├── Win32Methods.cs │ │ ├── SFileInfoClass.cs │ │ └── NativeMethods.cs │ ├── MpqArchiveCompactingEventArgs.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── StormLibSharp.csproj │ ├── MpqFileStream.cs │ └── MpqArchive.cs ├── CascLibSharp │ ├── Native │ │ ├── CascFileEnumerationSafeHandle.cs │ │ ├── CascStorageSafeHandle.cs │ │ ├── CascStorageFileSafeHandle.cs │ │ ├── CoTaskMem.cs │ │ └── NativeMethods.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── CascException.cs │ ├── CascFoundFile.cs │ ├── CascLibSharp.csproj │ ├── CascFileStream.cs │ ├── CascApi.cs │ └── CascStorageContext.cs └── StormLibSharp.sln ├── LICENSE ├── .gitattributes ├── .gitignore └── README.md /lib/x64/rel/CascLib.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robpaveza/stormlibsharp/HEAD/lib/x64/rel/CascLib.dll -------------------------------------------------------------------------------- /lib/x64/rel/StormLib.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robpaveza/stormlibsharp/HEAD/lib/x64/rel/StormLib.dll -------------------------------------------------------------------------------- /lib/x86/rel/CascLib.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robpaveza/stormlibsharp/HEAD/lib/x86/rel/CascLib.dll -------------------------------------------------------------------------------- /lib/x86/rel/StormLib.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robpaveza/stormlibsharp/HEAD/lib/x86/rel/StormLib.dll -------------------------------------------------------------------------------- /lib/x64/debug/CascLib.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robpaveza/stormlibsharp/HEAD/lib/x64/debug/CascLib.dll -------------------------------------------------------------------------------- /lib/x64/debug/StormLib.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robpaveza/stormlibsharp/HEAD/lib/x64/debug/StormLib.dll -------------------------------------------------------------------------------- /lib/x64/rel/CascLib_dll.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robpaveza/stormlibsharp/HEAD/lib/x64/rel/CascLib_dll.pdb -------------------------------------------------------------------------------- /lib/x86/debug/CascLib.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robpaveza/stormlibsharp/HEAD/lib/x86/debug/CascLib.dll -------------------------------------------------------------------------------- /lib/x86/debug/StormLib.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robpaveza/stormlibsharp/HEAD/lib/x86/debug/StormLib.dll -------------------------------------------------------------------------------- /lib/x86/rel/CascLib_dll.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robpaveza/stormlibsharp/HEAD/lib/x86/rel/CascLib_dll.pdb -------------------------------------------------------------------------------- /lib/x64/debug/CascLib_dll.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robpaveza/stormlibsharp/HEAD/lib/x64/debug/CascLib_dll.pdb -------------------------------------------------------------------------------- /lib/x86/debug/CascLib_dll.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robpaveza/stormlibsharp/HEAD/lib/x86/debug/CascLib_dll.pdb -------------------------------------------------------------------------------- /src/TestConsole/StormLib.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robpaveza/stormlibsharp/HEAD/src/TestConsole/StormLib.dll -------------------------------------------------------------------------------- /src/TestConsole/StormLib.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robpaveza/stormlibsharp/HEAD/src/TestConsole/StormLib.pdb -------------------------------------------------------------------------------- /src/TestConsole/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/StormLibSharp/Native/SFileOpenArchiveFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace StormLibSharp.Native 7 | { 8 | [Flags] 9 | internal enum SFileOpenArchiveFlags : uint 10 | { 11 | None = 0, 12 | TypeIsFile = None, 13 | TypeIsMemoryMapped = 1, 14 | TypeIsHttp = 2, 15 | 16 | AccessReadOnly = 0x100, 17 | AccessReadWriteShare = 0x200, 18 | AccessUseBitmap = 0x400, 19 | 20 | DontOpenListfile = 0x10000, 21 | DontOpenAttributes = 0x20000, 22 | DontSearchHeader = 0x40000, 23 | ForceVersion1 = 0x80000, 24 | CheckSectorCRC = 0x100000, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/StormLibSharp/Native/MpqFileSafeHandle.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32.SafeHandles; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace StormLibSharp.Native 8 | { 9 | internal sealed class MpqFileSafeHandle : SafeHandleZeroOrMinusOneIsInvalid 10 | { 11 | public MpqFileSafeHandle(IntPtr handle) 12 | : base(true) 13 | { 14 | this.SetHandle(handle); 15 | } 16 | 17 | public MpqFileSafeHandle() 18 | : base(true) 19 | { 20 | } 21 | 22 | protected override bool ReleaseHandle() 23 | { 24 | return NativeMethods.SFileCloseFile(this.handle); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/StormLibSharp/Native/MpqArchiveSafeHandle.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32.SafeHandles; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | 8 | namespace StormLibSharp.Native 9 | { 10 | internal sealed class MpqArchiveSafeHandle : SafeHandleZeroOrMinusOneIsInvalid 11 | { 12 | public MpqArchiveSafeHandle(IntPtr handle) 13 | : base(true) 14 | { 15 | this.SetHandle(handle); 16 | } 17 | 18 | public MpqArchiveSafeHandle() 19 | : base(true) { } 20 | 21 | protected override bool ReleaseHandle() 22 | { 23 | return NativeMethods.SFileCloseArchive(this.handle); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/StormLibSharp/Native/Callbacks.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | 7 | namespace StormLibSharp.Native 8 | { 9 | [UnmanagedFunctionPointer(CallingConvention.Winapi)] 10 | internal delegate void SFILE_DOWNLOAD_CALLBACK(IntPtr pvUserData, ulong byteOffset, uint dwTotalBytes); 11 | 12 | [UnmanagedFunctionPointer(CallingConvention.Winapi)] 13 | internal delegate void SFILE_COMPACT_CALLBACK(IntPtr pvUserData, uint dwWorkType, ulong bytesProcessed, ulong totalBytes); 14 | 15 | [UnmanagedFunctionPointer(CallingConvention.Winapi)] 16 | internal delegate void SFILE_ADDFILE_CALLBACK(IntPtr pvUserData, uint dwBytesWritte, uint dwTotalBytes, bool bFinalCall); 17 | } 18 | -------------------------------------------------------------------------------- /src/CascLibSharp/Native/CascFileEnumerationSafeHandle.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32.SafeHandles; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace CascLibSharp.Native 9 | { 10 | internal class CascFileEnumerationSafeHandle : SafeHandleZeroOrMinusOneIsInvalid 11 | { 12 | public CascFileEnumerationSafeHandle() 13 | : base(true) { } 14 | public CascFileEnumerationSafeHandle(IntPtr handle) 15 | : this() 16 | { 17 | SetHandle(handle); 18 | } 19 | 20 | protected override bool ReleaseHandle() 21 | { 22 | var api = Api ?? CascApi.Instance; 23 | return api.CascFindClose(DangerousGetHandle()); 24 | } 25 | 26 | internal CascApi Api 27 | { 28 | get; 29 | set; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/CascLibSharp/Native/CascStorageSafeHandle.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32.SafeHandles; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace CascLibSharp.Native 9 | { 10 | internal class CascStorageSafeHandle : SafeHandleZeroOrMinusOneIsInvalid 11 | { 12 | public CascStorageSafeHandle() 13 | : base(true) 14 | { 15 | 16 | } 17 | 18 | public CascStorageSafeHandle(IntPtr handle) 19 | : this() 20 | { 21 | SetHandle(handle); 22 | } 23 | 24 | protected override bool ReleaseHandle() 25 | { 26 | var api = Api ?? CascApi.Instance; 27 | return api.CascCloseStorage(DangerousGetHandle()); 28 | } 29 | 30 | internal CascApi Api 31 | { 32 | get; 33 | set; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/CascLibSharp/Native/CascStorageFileSafeHandle.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32.SafeHandles; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace CascLibSharp.Native 9 | { 10 | internal class CascStorageFileSafeHandle : SafeHandleZeroOrMinusOneIsInvalid 11 | { 12 | public CascStorageFileSafeHandle() 13 | : base(true) 14 | { 15 | 16 | } 17 | 18 | public CascStorageFileSafeHandle(IntPtr handle) 19 | : this() 20 | { 21 | SetHandle(handle); 22 | } 23 | 24 | protected override bool ReleaseHandle() 25 | { 26 | var api = Api ?? CascApi.Instance; 27 | return api.CascCloseFile(DangerousGetHandle()); 28 | } 29 | 30 | internal CascApi Api 31 | { 32 | get; 33 | set; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Rob Paveza 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/StormLibSharp/MpqArchiveCompactingEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace StormLibSharp 7 | { 8 | public delegate void MpqArchiveCompactingEventHandler(MpqArchive sender, MpqArchiveCompactingEventArgs e); 9 | 10 | public class MpqArchiveCompactingEventArgs : EventArgs 11 | { 12 | internal MpqArchiveCompactingEventArgs(uint dwWorkType, ulong processed, ulong total) 13 | { 14 | unchecked 15 | { 16 | WorkType = (MpqCompactingWorkType)dwWorkType; 17 | BytesProcessed = (long)processed; 18 | TotalBytes = (long)total; 19 | } 20 | } 21 | 22 | public MpqCompactingWorkType WorkType 23 | { 24 | get; 25 | private set; 26 | } 27 | 28 | public long BytesProcessed 29 | { 30 | get; 31 | private set; 32 | } 33 | 34 | public long TotalBytes 35 | { 36 | get; 37 | private set; 38 | } 39 | } 40 | 41 | public enum MpqCompactingWorkType 42 | { 43 | CheckingFiles = 1, 44 | CheckingHashTable = 2, 45 | CopyingNonMpqData = 3, 46 | CompactingFiles = 4, 47 | ClosingArchive = 5, 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/TestConsole/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("TestConsole")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("TestConsole")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("36fdeb0f-fb6b-4190-b0f2-30bb922dac0b")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/CascLibSharp/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("CascLibSharp")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("CascLibSharp")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("202757b2-0c2a-452b-a649-fbf9b33a4ece")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/StormLibSharp/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("StormLibSharp")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("StormLibSharp")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("681877df-01f4-4eab-8c85-0627a66c949e")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/CascLibSharp/Native/CoTaskMem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace CascLibSharp.Native 9 | { 10 | internal sealed class CoTaskMem : IDisposable 11 | { 12 | private IntPtr _mem; 13 | private int _size; 14 | 15 | public CoTaskMem(int size) 16 | { 17 | _mem = Marshal.AllocCoTaskMem(size); 18 | if (_mem == IntPtr.Zero) 19 | throw new OutOfMemoryException(); 20 | 21 | _size = size; 22 | } 23 | 24 | public static CoTaskMem FromBytes(byte[] b) 25 | { 26 | CoTaskMem result = new CoTaskMem(b.Length); 27 | Marshal.Copy(b, 0, result._mem, b.Length); 28 | 29 | return result; 30 | } 31 | 32 | public IntPtr Pointer 33 | { 34 | get 35 | { 36 | if (_mem == IntPtr.Zero) 37 | throw new ObjectDisposedException("CoTaskMem"); 38 | 39 | return _mem; 40 | } 41 | } 42 | 43 | public void Dispose() 44 | { 45 | Dispose(true); 46 | GC.SuppressFinalize(this); 47 | } 48 | 49 | private void Dispose(bool disposing) 50 | { 51 | if (_mem != IntPtr.Zero) 52 | { 53 | Marshal.FreeCoTaskMem(_mem); 54 | _mem = IntPtr.Zero; 55 | _size = 0; 56 | } 57 | } 58 | 59 | ~CoTaskMem() 60 | { 61 | Dispose(false); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/CascLibSharp/CascException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace CascLibSharp 10 | { 11 | /// 12 | /// Represents an exception raised as part of a CASC operation. 13 | /// 14 | public class CascException : Win32Exception 15 | { 16 | /// 17 | /// Creates a new CascException based on the last Win32 error. 18 | /// 19 | public CascException() 20 | : base(Marshal.GetLastWin32Error()) 21 | { 22 | 23 | } 24 | 25 | /// 26 | /// Creates a new CascException based on the specified Win32 error code. 27 | /// 28 | /// A Win32 error code. 29 | public CascException(int errorCode) 30 | : base(errorCode) 31 | { 32 | 33 | } 34 | 35 | /// 36 | /// Creates a new CascException based on the specified message. 37 | /// 38 | /// The error message. 39 | public CascException(string message) 40 | : base(message) 41 | { 42 | 43 | } 44 | 45 | /// 46 | /// Creates a new CascException based on the specified Win32 error code and custom error message. 47 | /// 48 | /// A Win32 error code. 49 | /// The error message. 50 | public CascException(int errorCode, string message) 51 | : base(errorCode, message) 52 | { 53 | 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/StormLibSharp/Native/Win32Methods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Diagnostics; 5 | using System.IO.MemoryMappedFiles; 6 | using System.Linq; 7 | using System.Runtime.InteropServices; 8 | using System.Text; 9 | 10 | namespace StormLibSharp.Native 11 | { 12 | internal static class Win32Methods 13 | { 14 | [DllImport("kernel32", ExactSpelling = false, SetLastError = true)] 15 | public static extern uint GetMappedFileName( 16 | IntPtr hProcess, 17 | IntPtr fileHandle, 18 | IntPtr lpFilename, 19 | uint nSize 20 | ); 21 | 22 | [DllImport("kernel32", ExactSpelling = false, SetLastError = true)] 23 | public static extern uint GetFinalPathNameByHandle( 24 | IntPtr hFile, 25 | IntPtr lpszFilePath, 26 | uint cchFilePath, 27 | uint dwFlags 28 | ); 29 | 30 | [DllImport("kernel32", SetLastError = false, ExactSpelling = false)] 31 | public static extern int GetLastError(); 32 | 33 | public static string GetFileNameOfMemoryMappedFile(MemoryMappedFile file) 34 | { 35 | const uint size = 522; 36 | IntPtr path = Marshal.AllocCoTaskMem(unchecked((int)size)); // MAX_PATH + 1 char 37 | 38 | string result = null; 39 | try 40 | { 41 | // constant 0x2 = VOLUME_NAME_NT 42 | uint test = GetFinalPathNameByHandle(file.SafeMemoryMappedFileHandle.DangerousGetHandle(), path, size, 0x2); 43 | if (test != 0) 44 | throw new Win32Exception(); 45 | 46 | result = Marshal.PtrToStringAuto(path); 47 | } 48 | catch 49 | { 50 | uint test = GetMappedFileName(Process.GetCurrentProcess().Handle, file.SafeMemoryMappedFileHandle.DangerousGetHandle(), path, size); 51 | if (test != 0) 52 | throw new Win32Exception(); 53 | 54 | result = Marshal.PtrToStringAuto(path); 55 | } 56 | 57 | return result; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/CascLibSharp/CascFoundFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace CascLibSharp 9 | { 10 | /// 11 | /// Represents a file found in a CASC container search. 12 | /// 13 | public class CascFoundFile 14 | { 15 | private WeakReference _ownerContext; 16 | 17 | internal CascFoundFile(string fileName, IntPtr plainName, byte[] encodingKey, CascLocales locales, long fileSize, CascStorageContext ownerContext) 18 | { 19 | FileName = fileName; 20 | PlainFileName = Marshal.PtrToStringAnsi(plainName); 21 | EncodingKey = encodingKey; 22 | Locales = locales; 23 | FileSize = fileSize; 24 | 25 | _ownerContext = new WeakReference(ownerContext); 26 | } 27 | 28 | /// 29 | /// Gets the full path to this file. 30 | /// 31 | public string FileName { get; private set; } 32 | /// 33 | /// Gets the plain (no directory-qualified) file name of this file. 34 | /// 35 | public string PlainFileName { get; private set; } 36 | /// 37 | /// Gets the CASC encoding key for this file. 38 | /// 39 | public byte[] EncodingKey { get; private set; } 40 | /// 41 | /// Gets the locales supported by this resource. 42 | /// 43 | public CascLocales Locales { get; private set; } 44 | /// 45 | /// Gets the length of the file in bytes. 46 | /// 47 | public long FileSize { get; private set; } 48 | 49 | /// 50 | /// Opens the found file for reading. 51 | /// 52 | /// A CascFileStream, which acts as a Stream for a CASC stored file. 53 | public CascFileStream Open() 54 | { 55 | CascStorageContext context; 56 | if (!_ownerContext.TryGetTarget(out context)) 57 | throw new ObjectDisposedException("The owning context has been closed."); 58 | 59 | return context.OpenFileByEncodingKey(EncodingKey); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | 11 | [Dd]ebug/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | [Bb]in/ 16 | [Oo]bj/ 17 | 18 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 19 | !packages/*/build/ 20 | 21 | # MSTest test Results 22 | [Tt]est[Rr]esult*/ 23 | [Bb]uild[Ll]og.* 24 | 25 | *_i.c 26 | *_p.c 27 | *.ilk 28 | *.meta 29 | *.obj 30 | *.pch 31 | *.pdb 32 | *.pgc 33 | *.pgd 34 | *.rsp 35 | *.sbr 36 | *.tlb 37 | *.tli 38 | *.tlh 39 | *.tmp 40 | *.tmp_proj 41 | *.log 42 | *.vspscc 43 | *.vssscc 44 | .builds 45 | *.pidb 46 | *.log 47 | *.scc 48 | 49 | # Visual C++ cache files 50 | ipch/ 51 | *.aps 52 | *.ncb 53 | *.opensdf 54 | *.sdf 55 | *.cachefile 56 | 57 | # Visual Studio profiler 58 | *.psess 59 | *.vsp 60 | *.vspx 61 | 62 | # Guidance Automation Toolkit 63 | *.gpState 64 | 65 | # ReSharper is a .NET coding add-in 66 | _ReSharper*/ 67 | *.[Rr]e[Ss]harper 68 | 69 | # TeamCity is a build add-in 70 | _TeamCity* 71 | 72 | # DotCover is a Code Coverage Tool 73 | *.dotCover 74 | 75 | # NCrunch 76 | *.ncrunch* 77 | .*crunch*.local.xml 78 | 79 | # Installshield output folder 80 | [Ee]xpress/ 81 | 82 | # DocProject is a documentation generator add-in 83 | DocProject/buildhelp/ 84 | DocProject/Help/*.HxT 85 | DocProject/Help/*.HxC 86 | DocProject/Help/*.hhc 87 | DocProject/Help/*.hhk 88 | DocProject/Help/*.hhp 89 | DocProject/Help/Html2 90 | DocProject/Help/html 91 | 92 | # Click-Once directory 93 | publish/ 94 | 95 | # Publish Web Output 96 | *.Publish.xml 97 | 98 | # NuGet Packages Directory 99 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 100 | #packages/ 101 | 102 | # Windows Azure Build Output 103 | csx 104 | *.build.csdef 105 | 106 | # Windows Store app package directory 107 | AppPackages/ 108 | 109 | # Others 110 | sql/ 111 | *.Cache 112 | ClientBin/ 113 | [Ss]tyle[Cc]op.* 114 | ~$* 115 | *~ 116 | *.dbmdl 117 | *.[Pp]ublish.xml 118 | *.pfx 119 | *.publishsettings 120 | 121 | # RIA/Silverlight projects 122 | Generated_Code/ 123 | 124 | # Backup & report files from converting an old project file to a newer 125 | # Visual Studio version. Backup files are not needed, because we have git ;-) 126 | _UpgradeReport_Files/ 127 | Backup*/ 128 | UpgradeLog*.XML 129 | UpgradeLog*.htm 130 | 131 | # SQL Server files 132 | App_Data/*.mdf 133 | App_Data/*.ldf 134 | 135 | 136 | #LightSwitch generated files 137 | GeneratedArtifacts/ 138 | _Pvt_Extensions/ 139 | ModelManifest.xml 140 | 141 | # ========================= 142 | # Windows detritus 143 | # ========================= 144 | 145 | # Windows image file caches 146 | Thumbs.db 147 | ehthumbs.db 148 | 149 | # Folder config file 150 | Desktop.ini 151 | 152 | # Recycle Bin used on file shares 153 | $RECYCLE.BIN/ 154 | 155 | # Mac desktop service store files 156 | .DS_Store 157 | -------------------------------------------------------------------------------- /src/StormLibSharp/StormLibSharp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {D0139C61-6BA0-4F07-B1E8-551023CD2624} 8 | Library 9 | Properties 10 | StormLibSharp 11 | StormLibSharp 12 | v4.0 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | true 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | true 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 64 | -------------------------------------------------------------------------------- /src/CascLibSharp/CascLibSharp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {89258C6B-C13A-4FC8-B311-2179DE1B8C7F} 8 | Library 9 | Properties 10 | CascLibSharp 11 | CascLibSharp 12 | v4.5 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | true 24 | bin\Debug\CascLibSharp.XML 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | true 34 | bin\Release\CascLibSharp.XML 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | *Please note that while I work for Blizzard Entertainment, this project was developed 2 | on my personal time before I worked for Blizzard. This project does not constitute an 3 | official documentation of the MPQ or CASC storage container formats, nor is it an 4 | official mechanism by which to do data-mining. --@robpaveza, 1 Feb. 2016* 5 | 6 | StormLibSharp and CascLibSharp 7 | ============= 8 | 9 | C# wrappers for Ladislav Zezula's [StormLib](https://github.com/ladislav-zezula/StormLib) and [CascLib](https://github.com/ladislav-zezula/CascLib). 10 | 11 | *Note: Because MPQ is no longer being used, StormLibSharp is no longer being maintained. I will accept pull requests if there is interest or demonstrable fixes. However, my primary focus will be on supporting CascLibSharp and bringing changes online as CascLib evolves.* 12 | 13 | ## What do StormLib and CascLib do? ## 14 | If you don't know, why are you even using this? StormLib is a utility library for reading and writing MPQ files, the archive file format that were used in Blizzard games until World of Warcraft: Warlords of Draenor. In Warlords, Blizzard switched their file storage format to CASC ([Content Addressable Storage Container](http://wow.gamepedia.com/Content_Addressable_Storage_Container)), which has a number of benefits. 15 | 16 | StormLib, and its successor CascLib, are likely most interesting to people who want to data-mine the game files (such as what WoWHead does). 17 | 18 | Both StormLib and CascLib have APIs that are very similar to, if not inspired by, the Win32 API for reading and manipulating files. 19 | 20 | ## Great, so what do the Sharp versions do? ## 21 | StormLibSharp was an effort to expose StormLib's functionality via typical .NET objects. For example, in the Base Class Library, files are exposed primarily via [FileStream](https://msdn.microsoft.com/en-us/library/system.io.filestream(v=vs.110).aspx). In addition to providing a baseline set of functionality, it provided utility methods like "Extract File" to support common use cases. 22 | 23 | CascLibSharp follows in StormLibSharp's tradition, by wrapping the Win32-like CascLib API into a .NET BCL-like API. To open a game's data, you create a `CascStorageContext`, supplying the path to the game's data directory (such as `c:\Program Files (x86)\Heroes of the Storm\HeroesData`). Then, you can enumerate the files, such as via SearchFiles, or if you know a file you want to view, you can just ask to open that file; it will return a `Stream`. 24 | 25 | ## Usage Notes ## 26 | Be absolutely certain to dispose of all of the objects that you create. Remember that you're working with native code under the covers; you have to dispose of the objects in order to free them. 27 | 28 | Previous versions of WoW maintained a special file called `(listfile)` in each MPQ. The listfile contained a list of every file in the MPQ. Warlords of Draenor does not; in order to search CASC, you must provide a path to the listfile. (You can't provide the text as a string; this is a limitation of CascLib). Ladik maintains a Warlords listfile within CascLib [here](https://github.com/ladislav-zezula/CascLib/tree/master/listfile). This limitation does not appear to exist in Starcraft, Heroes, or Overwatch. (By the way, I am not in the Overwatch beta. If anyone wants to fix that for me, I'd be very appreciative). 29 | 30 | ## Example usage ## 31 | For an example, check out the TestConsole project. 32 | -------------------------------------------------------------------------------- /src/TestConsole/TestConsole.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {5A5F9099-49A3-4DB0-8FE5-F88C46590DB3} 8 | Exe 9 | Properties 10 | TestConsole 11 | TestConsole 12 | v4.5 13 | 512 14 | 15 | 16 | 17 | x86 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | false 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | false 36 | true 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | {89258c6b-c13a-4fc8-b311-2179de1b8c7f} 54 | CascLibSharp 55 | 56 | 57 | {d0139c61-6ba0-4f07-b1e8-551023cd2624} 58 | StormLibSharp 59 | 60 | 61 | 62 | 63 | 64 | PreserveNewest 65 | 66 | 67 | PreserveNewest 68 | 69 | 70 | 71 | 72 | rd /s /q $(ProjectDir)$(OutDir)CascLib 73 | md $(ProjectDir)$(OutDir)CascLib\dbg 74 | md $(ProjectDir)$(OutDir)CascLib\dbg\x86 75 | md $(ProjectDir)$(OutDir)CascLib\dbg\x64 76 | md $(ProjectDir)$(OutDir)CascLib\fre 77 | md $(ProjectDir)$(OutDir)CascLib\fre\x86 78 | md $(ProjectDir)$(OutDir)CascLib\fre\x64 79 | copy $(SolutionDir)..\lib\x86\debug\CascLib.dll $(ProjectDir)$(OutDir)CascLib\dbg\x86 80 | copy $(SolutionDir)..\lib\x64\debug\CascLib.dll $(ProjectDir)$(OutDir)CascLib\dbg\x64 81 | copy $(SolutionDir)..\lib\x86\rel\CascLib.dll $(ProjectDir)$(OutDir)CascLib\fre\x86 82 | copy $(SolutionDir)..\lib\x64\rel\CascLib.dll $(ProjectDir)$(OutDir)CascLib\fre\x64 83 | 84 | 91 | -------------------------------------------------------------------------------- /src/CascLibSharp/Native/NativeMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace CascLibSharp.Native 10 | { 11 | internal static class NativeMethods 12 | { 13 | [DllImport("kernel32", SetLastError = true)] 14 | public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); 15 | 16 | [DllImport("kernel32", SetLastError = true)] 17 | public static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags); 18 | } 19 | 20 | internal enum CascStorageInfoClass 21 | { 22 | FileCount, 23 | Features, 24 | GameInfo, 25 | GameBuild, 26 | } 27 | 28 | internal enum CascStorageFeatures 29 | { 30 | None = 0x0, 31 | HasListfile = 0x1, 32 | } 33 | 34 | /* 35 | * #define CASC_GAME_HOTS 0x00010000 // Heroes of the Storm 36 | #define CASC_GAME_WOW6 0x00020000 // World of Warcraft - Warlords of Draenor 37 | #define CASC_GAME_DIABLO3 0x00030000 // Diablo 3 since PTR 2.2.0 38 | #define CASC_GAME_OVERWATCH 0x00040000 // Overwatch since PTR 24919* 39 | */ 40 | internal enum CascGameId 41 | { 42 | Hots = 0x00010000, 43 | Wow6 = 0x00020000, 44 | Diablo3 = 0x00030000, 45 | Overwatch = 0x00040000, 46 | Starcraft2 = 0x00050000, 47 | } 48 | 49 | internal static class GameConverterExtensions 50 | { 51 | private static Dictionary GameClientMap = new Dictionary 52 | { 53 | { CascGameId.Hots, CascKnownClient.HeroesOfTheStorm }, 54 | { CascGameId.Wow6, CascKnownClient.WorldOfWarcraft }, 55 | { CascGameId.Diablo3, CascKnownClient.Diablo3 }, 56 | { CascGameId.Overwatch, CascKnownClient.Overwatch }, 57 | { CascGameId.Starcraft2, CascKnownClient.Starcraft2 }, 58 | }; 59 | 60 | private static Dictionary ClientGameMap = new Dictionary() 61 | { 62 | { CascKnownClient.HeroesOfTheStorm, CascGameId.Hots }, 63 | { CascKnownClient.WorldOfWarcraft, CascGameId.Wow6 }, 64 | { CascKnownClient.Diablo3, CascGameId.Diablo3 }, 65 | { CascKnownClient.Overwatch, CascGameId.Overwatch }, 66 | { CascKnownClient.Starcraft2, CascGameId.Starcraft2 }, 67 | }; 68 | 69 | public static CascKnownClient ToKnownClient(this CascGameId gameId) 70 | { 71 | CascKnownClient result; 72 | if (!GameClientMap.TryGetValue(gameId, out result)) 73 | result = CascKnownClient.Unknown; 74 | 75 | return result; 76 | } 77 | 78 | public static CascGameId ToGameId(this CascKnownClient knownClient) 79 | { 80 | CascGameId result; 81 | if (!ClientGameMap.TryGetValue(knownClient, out result)) 82 | throw new ArgumentException("Invalid client."); 83 | 84 | return result; 85 | } 86 | } 87 | 88 | #pragma warning disable 649 89 | internal struct QueryKey 90 | { 91 | public IntPtr pbData; 92 | public uint cbData; 93 | } 94 | 95 | internal unsafe struct CascFindData 96 | { 97 | const int MAX_PATH = 260; 98 | 99 | public fixed byte szFileName[MAX_PATH]; 100 | public IntPtr szPlainName; 101 | //public ulong FileNameHash; 102 | public fixed byte EncodingKey[16]; 103 | //public uint dwPackageIndex; 104 | public uint dwLocaleFlags; 105 | public uint dwFileSize; 106 | 107 | public unsafe CascFoundFile ToFoundFile(CascStorageContext ownerContext) 108 | { 109 | string fileName = null; 110 | fixed (void* pFileName = szFileName) 111 | { 112 | fileName = Marshal.PtrToStringAnsi(new IntPtr(pFileName)); 113 | } 114 | byte[] encodingKey = new byte[16]; 115 | fixed (void* pEncodingKey = EncodingKey) 116 | { 117 | Marshal.Copy(new IntPtr(pEncodingKey), encodingKey, 0, 16); 118 | } 119 | 120 | return new CascFoundFile(fileName, szPlainName, encodingKey, (CascLocales)dwLocaleFlags, dwFileSize, ownerContext); 121 | } 122 | } 123 | #pragma warning restore 649 124 | } 125 | -------------------------------------------------------------------------------- /src/TestConsole/Program.cs: -------------------------------------------------------------------------------- 1 | using CascLibSharp; 2 | using StormLibSharp; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | 10 | namespace TestConsole 11 | { 12 | class Program 13 | { 14 | const string WOW_DATA_DIRECTORY_X64 = @"C:\Program Files (x86)\World of Warcraft\Data"; 15 | const string HEROES_DATA_DIRECTORY_X64 = @"C:\Program Files (x86)\Heroes of the Storm\HeroesData"; 16 | 17 | const string WOW_LISTFILE_PATH = @"C:\Projects\CascLib2\listfile\World of Warcraft 6x.txt"; 18 | static void Main(string[] args) 19 | { 20 | Console.WriteLine("Attach a native debugger now and press to continue."); 21 | Console.ReadLine(); 22 | 23 | try 24 | { 25 | using (CascStorageContext casc = new CascStorageContext(WOW_DATA_DIRECTORY_X64)) 26 | { 27 | Console.WriteLine("Successfully loaded CASC storage context."); 28 | Console.WriteLine("Game type is {0}, build {1}", casc.GameClient, casc.GameBuild); 29 | Console.WriteLine("{0} total file(s)", casc.FileCount); 30 | Console.WriteLine("Has listfile: {0}", casc.HasListfile); 31 | Console.ReadLine(); 32 | 33 | using (var file = casc.OpenFile(@"Interface\GLUES\LOADINGSCREENS\LoadingScreen_HighMaulRaid.blp")) 34 | { 35 | File.WriteAllBytes("LoadingScreen_HighMaulRaid.blp", file.ReadAllBytes()); 36 | } 37 | Console.WriteLine("Successfully extracted LoadingScreen_HighMaulRaid.blp"); 38 | try 39 | { 40 | using (var file = casc.OpenFileByEncodingKey(Convert.FromBase64String("2Pfre+Ss0jYg7lo3ZRYRtA=="))) 41 | { 42 | File.WriteAllBytes("BloodElfFemaleFaceLower16_02.blp", file.ReadAllBytes()); 43 | } 44 | Console.WriteLine("Successfully extracted BloodElfFemaleFaceLower16_02.blp via encoding key"); 45 | } 46 | catch (Exception ex) 47 | { 48 | Console.WriteLine(ex); 49 | } 50 | Console.ReadLine(); 51 | 52 | foreach (var file in casc.SearchFiles("*", WOW_LISTFILE_PATH)) 53 | { 54 | Console.WriteLine("{0}: {1} [{2}]", file.FileName, file.PlainFileName, Convert.ToBase64String(file.EncodingKey)); 55 | } 56 | Console.ReadLine(); 57 | } 58 | } 59 | catch (Exception ex) 60 | { 61 | Console.WriteLine(ex.ToString()); 62 | } 63 | 64 | /* 65 | string listFile = null; 66 | using (MpqArchive archive = new MpqArchive(@"d:\Projects\base-Win.MPQ", FileAccess.Read)) 67 | { 68 | using (MpqFileStream file = archive.OpenFile("(listfile)")) 69 | using (StreamReader sr = new StreamReader(file)) 70 | { 71 | listFile = sr.ReadToEnd(); 72 | Console.WriteLine(listFile); 73 | } 74 | 75 | archive.ExtractFile("(listfile)", @"d:\projects\base-win-listfile.txt"); 76 | } 77 | 78 | using (MpqArchive archive = MpqArchive.CreateNew(@"d:\projects\mynewmpq.mpq", MpqArchiveVersion.Version4)) 79 | { 80 | archive.AddFileFromDisk(@"D:\projects\base-win-listfile.txt", "base-win-listfile.txt"); 81 | 82 | int retval = archive.AddListFile(@"base-win-listfile.txt"); 83 | archive.Compact("base-win-listfile.txt"); 84 | archive.Flush(); 85 | } */ 86 | 87 | Console.WriteLine(" to exit."); 88 | Console.ReadLine(); 89 | } 90 | } 91 | 92 | internal static class StreamExtensions 93 | { 94 | public static byte[] ReadAllBytes(this Stream fs) 95 | { 96 | byte[] result = new byte[fs.Length]; 97 | fs.Position = 0; 98 | int cur = 0; 99 | while (cur < fs.Length) 100 | { 101 | int read = fs.Read(result, cur, result.Length - cur); 102 | cur += read; 103 | } 104 | 105 | return result; 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/StormLibSharp.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.30501.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StormLib", "StormLib", "{4AE095C9-98B2-4DFC-923D-AE41CB960E5D}" 7 | ProjectSection(SolutionItems) = preProject 8 | ..\lib\StormLib.h = ..\lib\StormLib.h 9 | EndProjectSection 10 | EndProject 11 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "x86-rel", "x86-rel", "{DFD95B66-651C-44B1-B16F-6176EA0A2494}" 12 | ProjectSection(SolutionItems) = preProject 13 | ..\lib\x86\rel\StormLib.dll = ..\lib\x86\rel\StormLib.dll 14 | EndProjectSection 15 | EndProject 16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "x86-dbg", "x86-dbg", "{E5E6C754-C5A2-4DD5-81F8-842317F29E9A}" 17 | ProjectSection(SolutionItems) = preProject 18 | ..\lib\x86\debug\StormLib.dll = ..\lib\x86\debug\StormLib.dll 19 | EndProjectSection 20 | EndProject 21 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "x64-rel", "x64-rel", "{6A7E62DC-0B13-4405-9B66-B36BE056F168}" 22 | ProjectSection(SolutionItems) = preProject 23 | ..\lib\x64\rel\StormLib.dll = ..\lib\x64\rel\StormLib.dll 24 | EndProjectSection 25 | EndProject 26 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "x64-dbg", "x64-dbg", "{BD23D3E5-FFEA-4A81-8174-E9E1FEC4F3AB}" 27 | ProjectSection(SolutionItems) = preProject 28 | ..\lib\x64\debug\StormLib.dll = ..\lib\x64\debug\StormLib.dll 29 | EndProjectSection 30 | EndProject 31 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StormLibSharp", "StormLibSharp\StormLibSharp.csproj", "{D0139C61-6BA0-4F07-B1E8-551023CD2624}" 32 | EndProject 33 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestConsole", "TestConsole\TestConsole.csproj", "{5A5F9099-49A3-4DB0-8FE5-F88C46590DB3}" 34 | EndProject 35 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CascLib", "CascLib", "{DA21C952-512F-4CF9-A788-94E9B8208A94}" 36 | ProjectSection(SolutionItems) = preProject 37 | ..\lib\CascLib.h = ..\lib\CascLib.h 38 | EndProjectSection 39 | EndProject 40 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "x64-dbg", "x64-dbg", "{45931CCF-4CBA-432B-9A8F-F4F3DE3CB125}" 41 | ProjectSection(SolutionItems) = preProject 42 | ..\lib\x64\debug\CascLib.dll = ..\lib\x64\debug\CascLib.dll 43 | EndProjectSection 44 | EndProject 45 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "x64-rel", "x64-rel", "{D54B979C-4217-4BE5-82A9-1E1B63034DD2}" 46 | ProjectSection(SolutionItems) = preProject 47 | ..\lib\x64\rel\CascLib.dll = ..\lib\x64\rel\CascLib.dll 48 | EndProjectSection 49 | EndProject 50 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "x86-dbg", "x86-dbg", "{B20CA97F-8AC7-4E5E-B5BF-D9653B850E15}" 51 | ProjectSection(SolutionItems) = preProject 52 | ..\lib\x86\debug\CascLib.dll = ..\lib\x86\debug\CascLib.dll 53 | EndProjectSection 54 | EndProject 55 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "x86-rel", "x86-rel", "{6FA0FD27-31E0-4D71-B0A5-9D2BEE9A9C66}" 56 | ProjectSection(SolutionItems) = preProject 57 | ..\lib\x86\rel\CascLib.dll = ..\lib\x86\rel\CascLib.dll 58 | EndProjectSection 59 | EndProject 60 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CascLibSharp", "CascLibSharp\CascLibSharp.csproj", "{89258C6B-C13A-4FC8-B311-2179DE1B8C7F}" 61 | EndProject 62 | Global 63 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 64 | Debug|Any CPU = Debug|Any CPU 65 | Release|Any CPU = Release|Any CPU 66 | EndGlobalSection 67 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 68 | {D0139C61-6BA0-4F07-B1E8-551023CD2624}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 69 | {D0139C61-6BA0-4F07-B1E8-551023CD2624}.Debug|Any CPU.Build.0 = Debug|Any CPU 70 | {D0139C61-6BA0-4F07-B1E8-551023CD2624}.Release|Any CPU.ActiveCfg = Release|Any CPU 71 | {D0139C61-6BA0-4F07-B1E8-551023CD2624}.Release|Any CPU.Build.0 = Release|Any CPU 72 | {5A5F9099-49A3-4DB0-8FE5-F88C46590DB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 73 | {5A5F9099-49A3-4DB0-8FE5-F88C46590DB3}.Debug|Any CPU.Build.0 = Debug|Any CPU 74 | {5A5F9099-49A3-4DB0-8FE5-F88C46590DB3}.Release|Any CPU.ActiveCfg = Release|Any CPU 75 | {5A5F9099-49A3-4DB0-8FE5-F88C46590DB3}.Release|Any CPU.Build.0 = Release|Any CPU 76 | {89258C6B-C13A-4FC8-B311-2179DE1B8C7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 77 | {89258C6B-C13A-4FC8-B311-2179DE1B8C7F}.Debug|Any CPU.Build.0 = Debug|Any CPU 78 | {89258C6B-C13A-4FC8-B311-2179DE1B8C7F}.Release|Any CPU.ActiveCfg = Release|Any CPU 79 | {89258C6B-C13A-4FC8-B311-2179DE1B8C7F}.Release|Any CPU.Build.0 = Release|Any CPU 80 | EndGlobalSection 81 | GlobalSection(SolutionProperties) = preSolution 82 | HideSolutionNode = FALSE 83 | EndGlobalSection 84 | GlobalSection(NestedProjects) = preSolution 85 | {DFD95B66-651C-44B1-B16F-6176EA0A2494} = {4AE095C9-98B2-4DFC-923D-AE41CB960E5D} 86 | {E5E6C754-C5A2-4DD5-81F8-842317F29E9A} = {4AE095C9-98B2-4DFC-923D-AE41CB960E5D} 87 | {6A7E62DC-0B13-4405-9B66-B36BE056F168} = {4AE095C9-98B2-4DFC-923D-AE41CB960E5D} 88 | {BD23D3E5-FFEA-4A81-8174-E9E1FEC4F3AB} = {4AE095C9-98B2-4DFC-923D-AE41CB960E5D} 89 | {45931CCF-4CBA-432B-9A8F-F4F3DE3CB125} = {DA21C952-512F-4CF9-A788-94E9B8208A94} 90 | {D54B979C-4217-4BE5-82A9-1E1B63034DD2} = {DA21C952-512F-4CF9-A788-94E9B8208A94} 91 | {B20CA97F-8AC7-4E5E-B5BF-D9653B850E15} = {DA21C952-512F-4CF9-A788-94E9B8208A94} 92 | {6FA0FD27-31E0-4D71-B0A5-9D2BEE9A9C66} = {DA21C952-512F-4CF9-A788-94E9B8208A94} 93 | EndGlobalSection 94 | EndGlobal 95 | -------------------------------------------------------------------------------- /src/StormLibSharp/Native/SFileInfoClass.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace StormLibSharp.Native 7 | { 8 | internal enum SFileInfoClass 9 | { 10 | // Info classes for archives 11 | SFileMpqFileName, // Name of the archive file (TCHAR []) 12 | SFileMpqStreamBitmap, // Array of bits, each bit means availability of one block (BYTE []) 13 | SFileMpqUserDataOffset, // Offset of the user data header (ULONGLONG) 14 | SFileMpqUserDataHeader, // Raw (unfixed) user data header (TMPQUserData) 15 | SFileMpqUserData, // MPQ USer data, without the header (BYTE []) 16 | SFileMpqHeaderOffset, // Offset of the MPQ header (ULONGLONG) 17 | SFileMpqHeaderSize, // Fixed size of the MPQ header 18 | SFileMpqHeader, // Raw (unfixed) archive header (TMPQHeader) 19 | SFileMpqHetTableOffset, // Offset of the HET table, relative to MPQ header (ULONGLONG) 20 | SFileMpqHetTableSize, // Compressed size of the HET table (ULONGLONG) 21 | SFileMpqHetHeader, // HET table header (TMPQHetHeader) 22 | SFileMpqHetTable, // HET table as pointer. Must be freed using SFileFreeFileInfo 23 | SFileMpqBetTableOffset, // Offset of the BET table, relative to MPQ header (ULONGLONG) 24 | SFileMpqBetTableSize, // Compressed size of the BET table (ULONGLONG) 25 | SFileMpqBetHeader, // BET table header, followed by the flags (TMPQBetHeader + DWORD[]) 26 | SFileMpqBetTable, // BET table as pointer. Must be freed using SFileFreeFileInfo 27 | SFileMpqHashTableOffset, // Hash table offset, relative to MPQ header (ULONGLONG) 28 | SFileMpqHashTableSize64, // Compressed size of the hash table (ULONGLONG) 29 | SFileMpqHashTableSize, // Size of the hash table, in entries (DWORD) 30 | SFileMpqHashTable, // Raw (unfixed) hash table (TMPQBlock []) 31 | SFileMpqBlockTableOffset, // Block table offset, relative to MPQ header (ULONGLONG) 32 | SFileMpqBlockTableSize64, // Compressed size of the block table (ULONGLONG) 33 | SFileMpqBlockTableSize, // Size of the block table, in entries (DWORD) 34 | SFileMpqBlockTable, // Raw (unfixed) block table (TMPQBlock []) 35 | SFileMpqHiBlockTableOffset, // Hi-block table offset, relative to MPQ header (ULONGLONG) 36 | SFileMpqHiBlockTableSize64, // Compressed size of the hi-block table (ULONGLONG) 37 | SFileMpqHiBlockTable, // The hi-block table (USHORT []) 38 | SFileMpqSignatures, // Signatures present in the MPQ (DWORD) 39 | SFileMpqStrongSignatureOffset, // Byte offset of the strong signature, relative to begin of the file (ULONGLONG) 40 | SFileMpqStrongSignatureSize, // Size of the strong signature (DWORD) 41 | SFileMpqStrongSignature, // The strong signature (BYTE []) 42 | SFileMpqArchiveSize64, // Archive size from the header (ULONGLONG) 43 | SFileMpqArchiveSize, // Archive size from the header (DWORD) 44 | SFileMpqMaxFileCount, // Max number of files in the archive (DWORD) 45 | SFileMpqFileTableSize, // Number of entries in the file table (DWORD) 46 | SFileMpqSectorSize, // Sector size (DWORD) 47 | SFileMpqNumberOfFiles, // Number of files (DWORD) 48 | SFileMpqRawChunkSize, // Size of the raw data chunk for MD5 49 | SFileMpqStreamFlags, // Stream flags (DWORD) 50 | SFileMpqIsReadOnly, // Nonzero if the MPQ is read only (DWORD) 51 | 52 | // Info classes for files 53 | SFileInfoPatchChain, // Chain of patches where the file is (TCHAR []) 54 | SFileInfoFileEntry, // The file entry for the file (TFileEntry) 55 | SFileInfoHashEntry, // Hash table entry for the file (TMPQHash) 56 | SFileInfoHashIndex, // Index of the hash table entry (DWORD) 57 | SFileInfoNameHash1, // The first name hash in the hash table (DWORD) 58 | SFileInfoNameHash2, // The second name hash in the hash table (DWORD) 59 | SFileInfoNameHash3, // 64-bit file name hash for the HET/BET tables (ULONGLONG) 60 | SFileInfoLocale, // File locale (DWORD) 61 | SFileInfoFileIndex, // Block index (DWORD) 62 | SFileInfoByteOffset, // File position in the archive (ULONGLONG) 63 | SFileInfoFileTime, // File time (ULONGLONG) 64 | SFileInfoFileSize, // Size of the file (DWORD) 65 | SFileInfoCompressedSize, // Compressed file size (DWORD) 66 | SFileInfoFlags, // File flags from (DWORD) 67 | SFileInfoEncryptionKey, // File encryption key 68 | SFileInfoEncryptionKeyRaw, // Unfixed value of the file key 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/StormLibSharp/MpqFileStream.cs: -------------------------------------------------------------------------------- 1 | using StormLibSharp.Native; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading; 9 | 10 | namespace StormLibSharp 11 | { 12 | public class MpqFileStream : Stream 13 | { 14 | private MpqFileSafeHandle _handle; 15 | private FileAccess _accessType; 16 | private MpqArchive _owner; 17 | 18 | internal MpqFileStream(MpqFileSafeHandle handle, FileAccess accessType, MpqArchive owner) 19 | { 20 | _handle = handle; 21 | _accessType = accessType; 22 | _owner = owner; 23 | } 24 | 25 | private void VerifyHandle() 26 | { 27 | if (_handle == null || _handle.IsInvalid || _handle.IsClosed) 28 | throw new ObjectDisposedException("MpqFileStream"); 29 | } 30 | 31 | public override bool CanRead 32 | { 33 | get { VerifyHandle(); return true; } 34 | } 35 | 36 | public override bool CanSeek 37 | { 38 | get { VerifyHandle(); return true; } 39 | } 40 | 41 | public override bool CanWrite 42 | { 43 | get { VerifyHandle(); return _accessType != FileAccess.Read; } 44 | } 45 | 46 | public override void Flush() 47 | { 48 | VerifyHandle(); 49 | 50 | _owner.Flush(); 51 | } 52 | 53 | public override long Length 54 | { 55 | get 56 | { 57 | VerifyHandle(); 58 | 59 | uint high = 0; 60 | uint low = NativeMethods.SFileGetFileSize(_handle, ref high); 61 | 62 | ulong val = (high << 32) | low; 63 | return unchecked((long)val); 64 | } 65 | } 66 | 67 | public override long Position 68 | { 69 | get 70 | { 71 | VerifyHandle(); 72 | 73 | return NativeMethods.SFileGetFilePointer(_handle); 74 | } 75 | set 76 | { 77 | Seek(value, SeekOrigin.Begin); 78 | } 79 | } 80 | 81 | public override unsafe int Read(byte[] buffer, int offset, int count) 82 | { 83 | if (buffer == null) 84 | throw new ArgumentNullException("buffer"); 85 | if (offset > buffer.Length || (offset + count) > buffer.Length) 86 | throw new ArgumentException(); 87 | if (count < 0) 88 | throw new ArgumentOutOfRangeException("count"); 89 | 90 | VerifyHandle(); 91 | 92 | bool success; 93 | uint read; 94 | fixed (byte* pb = &buffer[offset]) 95 | { 96 | NativeOverlapped overlapped = default(NativeOverlapped); 97 | success = NativeMethods.SFileReadFile(_handle, new IntPtr(pb), unchecked((uint)count), out read, ref overlapped); 98 | } 99 | 100 | if (!success) 101 | { 102 | int lastError = Win32Methods.GetLastError(); 103 | if (lastError != 38) // EOF 104 | throw new Win32Exception(lastError); 105 | } 106 | 107 | return unchecked((int)read); 108 | } 109 | 110 | public override long Seek(long offset, SeekOrigin origin) 111 | { 112 | VerifyHandle(); 113 | 114 | uint low, high; 115 | low = unchecked((uint)(offset & 0xffffffffu)); 116 | high = unchecked((uint)(offset >> 32)); 117 | return NativeMethods.SFileSetFilePointer(_handle, low, ref high, (uint)origin); 118 | } 119 | 120 | public override void SetLength(long value) 121 | { 122 | throw new NotSupportedException(); 123 | } 124 | 125 | public override unsafe void Write(byte[] buffer, int offset, int count) 126 | { 127 | VerifyHandle(); 128 | 129 | if (buffer == null) 130 | throw new ArgumentNullException("buffer"); 131 | if (offset > buffer.Length || (offset + count) > buffer.Length) 132 | throw new ArgumentException(); 133 | if (count < 0) 134 | throw new ArgumentOutOfRangeException("count"); 135 | 136 | VerifyHandle(); 137 | 138 | bool success; 139 | fixed (byte* pb = &buffer[offset]) 140 | { 141 | success = NativeMethods.SFileWriteFile(_handle, new IntPtr(pb), unchecked((uint)count), 0u); 142 | } 143 | 144 | if (!success) 145 | throw new Win32Exception(); 146 | } 147 | 148 | protected override void Dispose(bool disposing) 149 | { 150 | base.Dispose(disposing); 151 | 152 | if (disposing) 153 | { 154 | if (_handle != null && !_handle.IsInvalid) 155 | { 156 | _handle.Close(); 157 | _handle = null; 158 | } 159 | 160 | if (_owner != null) 161 | { 162 | _owner.RemoveOwnedFile(this); 163 | _owner = null; 164 | } 165 | } 166 | } 167 | 168 | // TODO: Seems like the right place for SFileGetFileInfo, but will need to determine 169 | // what value add these features have except for sophisticated debugging purposes 170 | // (like in Ladis' MPQ Editor app). 171 | 172 | public int ChecksumCrc32 173 | { 174 | get 175 | { 176 | throw new NotImplementedException(); 177 | } 178 | } 179 | 180 | public byte[] GetMd5Hash() 181 | { 182 | throw new NotImplementedException(); 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/CascLibSharp/CascFileStream.cs: -------------------------------------------------------------------------------- 1 | using CascLibSharp.Native; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace CascLibSharp 11 | { 12 | /// 13 | /// Provides a view of the data in a file contained within CASC storage. 14 | /// 15 | public class CascFileStream : Stream 16 | { 17 | private CascStorageFileSafeHandle _handle; 18 | private CascApi _api; 19 | 20 | internal CascFileStream(CascStorageFileSafeHandle handle, CascApi api) 21 | { 22 | Debug.Assert(handle != null); 23 | Debug.Assert(!handle.IsInvalid); 24 | Debug.Assert(api != null); 25 | 26 | _api = api; 27 | _handle = handle; 28 | } 29 | 30 | private void AssertValidHandle() 31 | { 32 | if (_handle == null || _handle.IsInvalid) 33 | throw new ObjectDisposedException("CascFileStream"); 34 | } 35 | 36 | /// 37 | /// Gets whether this Stream may be read. Always returns true as long as the Stream has not been disposed. 38 | /// 39 | public override bool CanRead 40 | { 41 | get { return _handle != null && !_handle.IsInvalid; } 42 | } 43 | 44 | /// 45 | /// Gets whether this Stream may seek. Always returns true as long as the Stream has not been disposed. 46 | /// 47 | public override bool CanSeek 48 | { 49 | get { return _handle != null && !_handle.IsInvalid; } 50 | } 51 | 52 | /// 53 | /// Gets whether this Stream may write. Always returns false. 54 | /// 55 | public override bool CanWrite 56 | { 57 | get { return false; } 58 | } 59 | 60 | /// 61 | /// Flushes writes to the backing store. This method always throws because writing is not supported. 62 | /// 63 | /// Always thrown. 64 | public override void Flush() 65 | { 66 | throw new NotSupportedException(); 67 | } 68 | 69 | /// 70 | /// Gets the length of the Stream. 71 | /// 72 | /// Thrown if the Stream has been disposed. 73 | public override long Length 74 | { 75 | get 76 | { 77 | AssertValidHandle(); 78 | return _api.CascGetFileSize(_handle); 79 | } 80 | } 81 | 82 | /// 83 | /// Gets or sets the current position within the Stream. 84 | /// 85 | /// Thrown if the Stream has been disposed. 86 | /// Thrown if the attempt to seek fails. 87 | public override long Position 88 | { 89 | get 90 | { 91 | AssertValidHandle(); 92 | return _api.CascSetFilePointer(_handle, 0, SeekOrigin.Current); 93 | } 94 | set 95 | { 96 | AssertValidHandle(); 97 | long result = _api.CascSetFilePointer(_handle, value, SeekOrigin.Begin); 98 | if (result != value) 99 | throw new CascException(); 100 | } 101 | } 102 | 103 | /// 104 | /// Reads data into the buffer. 105 | /// 106 | /// The destination into which the data should be read. 107 | /// The offset into the buffer to read from the current position of the Stream. 108 | /// The number of bytes to read. 109 | /// The number of bytes actually read. 110 | /// Thrown if the Stream has been disposed. 111 | /// Thrown if the CASC provider fails to read. 112 | public override unsafe int Read(byte[] buffer, int offset, int count) 113 | { 114 | AssertValidHandle(); 115 | long len = Length; 116 | if (offset < 0 || offset > len) 117 | throw new ArgumentException("offset"); 118 | if (count < 0) 119 | throw new ArgumentException("count"); 120 | if (offset + count > len) 121 | throw new ArgumentException("count"); 122 | 123 | uint read = 0; 124 | fixed (byte* pBuffer = &buffer[0]) 125 | { 126 | if (!_api.CascReadFile(_handle, new IntPtr((void*)pBuffer), unchecked((uint)count), out read)) 127 | throw new CascException(); 128 | } 129 | 130 | return unchecked((int)read); 131 | } 132 | 133 | /// 134 | /// Seeks to a specific position within the Stream. 135 | /// 136 | /// The offset from the specified origin to seek. 137 | /// The relative location (beginning, current, or end) from which to seek. 138 | /// The new position in the stream. 139 | /// Thrown if the Stream has been disposed. 140 | public override long Seek(long offset, SeekOrigin origin) 141 | { 142 | AssertValidHandle(); 143 | return _api.CascSetFilePointer(_handle, offset, origin); 144 | } 145 | 146 | /// 147 | /// Sets the length of the Stream. Always throws because writing is not supported. 148 | /// 149 | /// The new length of the Stream. 150 | /// Always thrown. 151 | public override void SetLength(long value) 152 | { 153 | throw new NotSupportedException(); 154 | } 155 | 156 | /// 157 | /// Writes the specified data to the Stream. Always throws because writing is not supported. 158 | /// 159 | /// The data to write. 160 | /// The starting position in the buffer. 161 | /// The number of bytes. 162 | /// Always thrown. 163 | public override void Write(byte[] buffer, int offset, int count) 164 | { 165 | throw new NotSupportedException(); 166 | } 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/CascLibSharp/CascApi.cs: -------------------------------------------------------------------------------- 1 | using CascLibSharp.Native; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Runtime.InteropServices; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace CascLibSharp 13 | { 14 | internal sealed class CascApi 15 | { 16 | #region Imported method signature type definitions 17 | [return: MarshalAs(UnmanagedType.I1)] 18 | [UnmanagedFunctionPointer(CallingConvention.Winapi)] 19 | internal delegate bool FnCascOpenStorage( 20 | [MarshalAs(UnmanagedType.LPTStr)] string szDataPath, 21 | uint dwFlags, 22 | out CascStorageSafeHandle phStorage); 23 | [return: MarshalAs(UnmanagedType.I1)] 24 | [UnmanagedFunctionPointer(CallingConvention.Winapi)] 25 | internal delegate bool FnCascGetStorageInfo( 26 | CascStorageSafeHandle hStorage, 27 | CascStorageInfoClass infoClass, 28 | ref uint pvStorageInfo, 29 | IntPtr cbStorageInfo, 30 | ref uint pcbLengthNeeded); 31 | [return: MarshalAs(UnmanagedType.I1)] 32 | [UnmanagedFunctionPointer(CallingConvention.Winapi)] 33 | internal delegate bool FnCascCloseStorage( IntPtr hStorage); 34 | 35 | [return: MarshalAs(UnmanagedType.I1)] 36 | [UnmanagedFunctionPointer(CallingConvention.Winapi)] 37 | internal delegate bool FnCascOpenFileByIndexKey( 38 | CascStorageSafeHandle hStorage, 39 | ref QueryKey pIndexKey, 40 | uint dwFlags, 41 | out CascStorageFileSafeHandle phFile); 42 | [return: MarshalAs(UnmanagedType.I1)] 43 | [UnmanagedFunctionPointer(CallingConvention.Winapi)] 44 | internal delegate bool FnCascOpenFileByEncodingKey( 45 | CascStorageSafeHandle hStorage, 46 | ref QueryKey pEncodingKey, 47 | uint dwFlags, 48 | out CascStorageFileSafeHandle phFile); 49 | [return: MarshalAs(UnmanagedType.I1)] 50 | [UnmanagedFunctionPointer(CallingConvention.Winapi)] 51 | internal delegate bool FnCascOpenFile( 52 | CascStorageSafeHandle hStorage, 53 | [MarshalAs(UnmanagedType.LPStr)] string szFileName, 54 | uint dwLocale, 55 | uint dwFlags, 56 | out CascStorageFileSafeHandle phFile); 57 | [UnmanagedFunctionPointer(CallingConvention.Winapi)] 58 | internal delegate uint FnCascGetFileSize( 59 | CascStorageFileSafeHandle hFile, 60 | out uint pdwFileSizeHigh); 61 | [UnmanagedFunctionPointer(CallingConvention.Winapi)] 62 | internal delegate uint FnCascSetFilePointer( 63 | CascStorageFileSafeHandle hFile, 64 | uint lFilePos, 65 | ref uint plFilePosHigh, 66 | SeekOrigin dwMoveMethod); 67 | [return: MarshalAs(UnmanagedType.I1)] 68 | [UnmanagedFunctionPointer(CallingConvention.Winapi)] 69 | internal delegate bool FnCascReadFile( 70 | CascStorageFileSafeHandle hFile, 71 | IntPtr lpBuffer, 72 | uint dwToRead, 73 | out uint dwRead); 74 | [return: MarshalAs(UnmanagedType.I1)] 75 | [UnmanagedFunctionPointer(CallingConvention.Winapi)] 76 | internal delegate bool FnCascCloseFile( IntPtr hFile); 77 | 78 | [UnmanagedFunctionPointer(CallingConvention.Winapi)] 79 | internal delegate CascFileEnumerationSafeHandle FnCascFindFirstFile( 80 | CascStorageSafeHandle hStorage, 81 | [MarshalAs(UnmanagedType.LPStr)] string szMask, 82 | ref CascFindData pFindData, 83 | [MarshalAs(UnmanagedType.LPTStr)] string szListFile); 84 | [return: MarshalAs(UnmanagedType.I1)] 85 | [UnmanagedFunctionPointer(CallingConvention.Winapi)] 86 | internal delegate bool FnCascFindNextFile( 87 | CascFileEnumerationSafeHandle hFind, 88 | ref CascFindData pFindData); 89 | [return: MarshalAs(UnmanagedType.I1)] 90 | [UnmanagedFunctionPointer(CallingConvention.Winapi)] 91 | internal delegate bool FnCascFindClose( IntPtr hFind); 92 | #endregion 93 | 94 | #region Field and method defs 95 | public readonly FnCascOpenStorage CascOpenStorage; 96 | public readonly FnCascGetStorageInfo CascGetStorageInfo; 97 | public readonly FnCascCloseStorage CascCloseStorage; 98 | 99 | public readonly FnCascOpenFileByIndexKey CascOpenFileByIndexKey; 100 | public readonly FnCascOpenFileByEncodingKey CascOpenFileByEncodingKey; 101 | public readonly FnCascOpenFile CascOpenFile; 102 | 103 | public readonly FnCascGetFileSize CascGetFileSizeBase; 104 | public long CascGetFileSize(CascStorageFileSafeHandle hFile) 105 | { 106 | uint high; 107 | uint low = CascGetFileSizeBase(hFile, out high); 108 | long result = (high << 32) | low; 109 | 110 | return result; 111 | } 112 | public readonly FnCascSetFilePointer CascSetFilePointerBase; 113 | public long CascSetFilePointer(CascStorageFileSafeHandle hFile, long filePos, SeekOrigin moveMethod) 114 | { 115 | uint low, high; 116 | unchecked 117 | { 118 | low = (uint)(filePos & 0xffffffff); 119 | high = (uint)(((ulong)filePos & 0xffffffff00000000) >> 32); 120 | } 121 | 122 | low = CascSetFilePointerBase(hFile, low, ref high, moveMethod); 123 | 124 | long result = (high << 32) | low; 125 | 126 | return result; 127 | } 128 | public readonly FnCascReadFile CascReadFile; 129 | public readonly FnCascCloseFile CascCloseFile; 130 | 131 | public readonly FnCascFindFirstFile CascFindFirstFile; 132 | public readonly FnCascFindNextFile CascFindNextFile; 133 | public readonly FnCascFindClose CascFindClose; 134 | #endregion 135 | 136 | internal CascApi(IntPtr hModule) 137 | { 138 | SetFn(ref CascOpenStorage, hModule, "CascOpenStorage"); 139 | SetFn(ref CascGetStorageInfo, hModule, "CascGetStorageInfo"); 140 | SetFn(ref CascCloseStorage, hModule, "CascCloseStorage"); 141 | 142 | SetFn(ref CascOpenFileByIndexKey, hModule, "CascOpenFileByIndexKey"); 143 | SetFn(ref CascOpenFileByEncodingKey, hModule, "CascOpenFileByEncodingKey"); 144 | SetFn(ref CascOpenFile, hModule, "CascOpenFile"); 145 | SetFn(ref CascGetFileSizeBase, hModule, "CascGetFileSize"); 146 | SetFn(ref CascSetFilePointerBase, hModule, "CascSetFilePointer"); 147 | SetFn(ref CascReadFile, hModule, "CascReadFile"); 148 | SetFn(ref CascCloseFile, hModule, "CascCloseFile"); 149 | 150 | SetFn(ref CascFindFirstFile, hModule, "CascFindFirstFile"); 151 | SetFn(ref CascFindNextFile, hModule, "CascFindNextFile"); 152 | SetFn(ref CascFindClose, hModule, "CascFindClose"); 153 | } 154 | 155 | private static void SetFn(ref T target, IntPtr hModule, string procName) 156 | where T : class 157 | { 158 | IntPtr procAddr = NativeMethods.GetProcAddress(hModule, procName); 159 | if (procAddr == IntPtr.Zero) 160 | throw new Win32Exception(); 161 | target = Marshal.GetDelegateForFunctionPointer(procAddr, typeof(T)) as T; 162 | } 163 | 164 | private static CascApi Load() 165 | { 166 | string myPath = Assembly.GetExecutingAssembly().Location; 167 | string directory = Path.GetDirectoryName(myPath); 168 | string arch = IntPtr.Size == 8 ? "x64" : "x86"; 169 | #if DEBUG 170 | string build = "dbg"; 171 | #else 172 | string build = "fre"; 173 | #endif 174 | 175 | string mainPath = Path.Combine(directory, "CascLib", build, arch, "CascLib.dll"); 176 | if (File.Exists(mainPath)) 177 | return FromFile(mainPath); 178 | 179 | string alternatePath = Path.Combine(directory, "CascLib", arch, "CascLib.dll"); 180 | if (File.Exists(mainPath)) 181 | return FromFile(alternatePath); 182 | 183 | string localPath = Path.Combine(directory, "CascLib.dll"); 184 | if (File.Exists(localPath)) 185 | return FromFile(localPath); 186 | 187 | 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)); 188 | } 189 | 190 | private static Lazy _sharedInstance = new Lazy(Load); 191 | public static CascApi Instance 192 | { 193 | get 194 | { 195 | return _sharedInstance.Value; 196 | } 197 | } 198 | 199 | public static CascApi FromFile(string filePath) 200 | { 201 | if (!File.Exists(filePath)) 202 | throw new FileNotFoundException("The CascLib.dll library could not be loaded at the specified path.", filePath); 203 | 204 | IntPtr hMod = NativeMethods.LoadLibraryEx(filePath, IntPtr.Zero, 0); 205 | if (hMod == IntPtr.Zero) 206 | throw new Win32Exception(); 207 | 208 | return new CascApi(hMod); 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /lib/CascLib.h: -------------------------------------------------------------------------------- 1 | /*****************************************************************************/ 2 | /* CascLib.h Copyright (c) Ladislav Zezula 2014 */ 3 | /*---------------------------------------------------------------------------*/ 4 | /* CascLib library v 1.00 */ 5 | /* */ 6 | /* Author : Ladislav Zezula */ 7 | /* E-mail : ladik@zezula.net */ 8 | /* WWW : http://www.zezula.net */ 9 | /*---------------------------------------------------------------------------*/ 10 | /* Date Ver Who Comment */ 11 | /* -------- ---- --- ------- */ 12 | /* 29.04.14 1.00 Lad Created */ 13 | /*****************************************************************************/ 14 | 15 | #ifndef __CASCLIB_H__ 16 | #define __CASCLIB_H__ 17 | 18 | #ifdef _MSC_VER 19 | #pragma warning(disable:4668) // 'XXX' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' 20 | #pragma warning(disable:4820) // 'XXX' : '2' bytes padding added after data member 'XXX::yyy' 21 | #endif 22 | 23 | #include "CascPort.h" 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | //----------------------------------------------------------------------------- 30 | // Use the apropriate library 31 | // 32 | // The library type is encoded in the library name as the following 33 | // CascLibXYZ.lib 34 | // 35 | // X - D for Debug version, R for Release version 36 | // Y - A for ANSI version, U for Unicode version 37 | // Z - S for static-linked CRT library, D for multithreaded DLL CRT library 38 | // 39 | 40 | #if defined(_MSC_VER) && !defined(__CASCLIB_SELF__) 41 | 42 | #ifdef _DEBUG // DEBUG VERSIONS 43 | #ifndef _UNICODE 44 | #ifdef _DLL 45 | #pragma comment(lib, "CascLibDAD.lib") // Debug Ansi CRT-DLL version 46 | #else 47 | #pragma comment(lib, "CascLibDAS.lib") // Debug Ansi CRT-LIB version 48 | #endif 49 | #else 50 | #ifdef _DLL 51 | #pragma comment(lib, "CascLibDUD.lib") // Debug Unicode CRT-DLL version 52 | #else 53 | #pragma comment(lib, "CascLibDUS.lib") // Debug Unicode CRT-LIB version 54 | #endif 55 | #endif 56 | #else // RELEASE VERSIONS 57 | #ifndef _UNICODE 58 | #ifdef _DLL 59 | #pragma comment(lib, "CascLibRAD.lib") // Release Ansi CRT-DLL version 60 | #else 61 | #pragma comment(lib, "CascLibRAS.lib") // Release Ansi CRT-LIB version 62 | #endif 63 | #else 64 | #ifdef _DLL 65 | #pragma comment(lib, "CascLibRUD.lib") // Release Unicode CRT-DLL version 66 | #else 67 | #pragma comment(lib, "CascLibRUS.lib") // Release Unicode CRT-LIB version 68 | #endif 69 | #endif 70 | #endif 71 | 72 | #endif 73 | 74 | //----------------------------------------------------------------------------- 75 | // Defines 76 | 77 | #define CASCLIB_VERSION 0x0100 // Current version of CascLib (1.0) 78 | #define CASCLIB_VERSION_STRING "1.00" // String version of CascLib version 79 | 80 | #define ERROR_UNKNOWN_FILE_KEY 10001 // Returned by encrypted stream when can't find file key 81 | #define ERROR_FILE_INCOMPLETE 10006 // The required file part is missing 82 | 83 | // Values for CascOpenStorage 84 | #define CASC_STOR_XXXXX 0x00000001 // Not used 85 | 86 | // Values for CascOpenFile 87 | #define CASC_OPEN_BY_ENCODING_KEY 0x00000001 // The name is just the encoding key; skip ROOT file processing 88 | 89 | // Flags for file stream 90 | #define BASE_PROVIDER_FILE 0x00000000 // Base data source is a file 91 | #define BASE_PROVIDER_MAP 0x00000001 // Base data source is memory-mapped file 92 | #define BASE_PROVIDER_HTTP 0x00000002 // Base data source is a file on web server 93 | #define BASE_PROVIDER_MASK 0x0000000F // Mask for base provider value 94 | 95 | #define STREAM_PROVIDER_FLAT 0x00000000 // Stream is linear with no offset mapping 96 | #define STREAM_PROVIDER_PARTIAL 0x00000010 // Stream is partial file (.part) 97 | #define STREAM_PROVIDER_ENCRYPTED 0x00000020 // Stream is an encrypted archive 98 | #define STREAM_PROVIDER_BLOCK4 0x00000030 // 0x4000 per block, text MD5 after each block, max 0x2000 blocks per file 99 | #define STREAM_PROVIDER_MASK 0x000000F0 // Mask for stream provider value 100 | 101 | #define STREAM_FLAG_READ_ONLY 0x00000100 // Stream is read only 102 | #define STREAM_FLAG_WRITE_SHARE 0x00000200 // Allow write sharing when open for write 103 | #define STREAM_FLAG_USE_BITMAP 0x00000400 // If the file has a file bitmap, load it and use it 104 | #define STREAM_OPTIONS_MASK 0x0000FF00 // Mask for stream options 105 | 106 | #define STREAM_PROVIDERS_MASK 0x000000FF // Mask to get stream providers 107 | #define STREAM_FLAGS_MASK 0x0000FFFF // Mask for all stream flags (providers+options) 108 | 109 | #define CASC_LOCALE_ALL 0xFFFFFFFF 110 | #define CASC_LOCALE_NONE 0x00000000 111 | #define CASC_LOCALE_UNKNOWN1 0x00000001 112 | #define CASC_LOCALE_ENUS 0x00000002 113 | #define CASC_LOCALE_KOKR 0x00000004 114 | #define CASC_LOCALE_RESERVED 0x00000008 115 | #define CASC_LOCALE_FRFR 0x00000010 116 | #define CASC_LOCALE_DEDE 0x00000020 117 | #define CASC_LOCALE_ZHCN 0x00000040 118 | #define CASC_LOCALE_ESES 0x00000080 119 | #define CASC_LOCALE_ZHTW 0x00000100 120 | #define CASC_LOCALE_ENGB 0x00000200 121 | #define CASC_LOCALE_ENCN 0x00000400 122 | #define CASC_LOCALE_ENTW 0x00000800 123 | #define CASC_LOCALE_ESMX 0x00001000 124 | #define CASC_LOCALE_RURU 0x00002000 125 | #define CASC_LOCALE_PTBR 0x00004000 126 | #define CASC_LOCALE_ITIT 0x00008000 127 | #define CASC_LOCALE_PTPT 0x00010000 128 | 129 | #define CASC_LOCALE_BIT_ENUS 0x01 130 | #define CASC_LOCALE_BIT_KOKR 0x02 131 | #define CASC_LOCALE_DUAL_LANG 0x03 132 | #define CASC_LOCALE_BIT_FRFR 0x04 133 | #define CASC_LOCALE_BIT_DEDE 0x05 134 | #define CASC_LOCALE_BIT_ZHCN 0x06 135 | #define CASC_LOCALE_BIT_ESES 0x07 136 | #define CASC_LOCALE_BIT_ZHTW 0x08 137 | #define CASC_LOCALE_BIT_ENGB 0x09 138 | #define CASC_LOCALE_BIT_ENCN 0x0A 139 | #define CASC_LOCALE_BIT_ENTW 0x0B 140 | #define CASC_LOCALE_BIT_ESMX 0x0C 141 | #define CASC_LOCALE_BIT_RURU 0x0D 142 | #define CASC_LOCALE_BIT_PTBR 0x0E 143 | #define CASC_LOCALE_BIT_ITIT 0x0F 144 | #define CASC_LOCALE_BIT_PTPT 0x10 145 | 146 | 147 | #define MAX_CASC_KEY_LENGTH 0x10 // Maximum length of the key (equal to MD5 hash) 148 | 149 | #ifndef MD5_HASH_SIZE 150 | #define MD5_HASH_SIZE 0x10 151 | #define MD5_STRING_SIZE 0x20 152 | #endif 153 | 154 | #ifndef SHA1_DIGEST_SIZE 155 | #define SHA1_DIGEST_SIZE 0x14 // 160 bits 156 | #endif 157 | 158 | #ifndef LANG_NEUTRAL 159 | #define LANG_NEUTRAL 0x00 // Neutral locale 160 | #endif 161 | 162 | // Return value for CascGetFileSize and CascSetFilePointer 163 | #define CASC_INVALID_SIZE 0xFFFFFFFF 164 | #define CASC_INVALID_POS 0xFFFFFFFF 165 | 166 | // Flags for CascGetStorageInfo 167 | #define CASC_FEATURE_LISTFILE 0x00000001 // The storage supports listfile 168 | 169 | //----------------------------------------------------------------------------- 170 | // Structures 171 | 172 | typedef enum _CASC_STORAGE_INFO_CLASS 173 | { 174 | CascStorageFileCount, 175 | CascStorageFeatures, 176 | CascStorageGameInfo, 177 | CascStorageGameBuild, 178 | CascStorageInfoClassMax 179 | 180 | } CASC_STORAGE_INFO_CLASS, *PCASC_STORAGE_INFO_CLASS; 181 | 182 | 183 | typedef struct _QUERY_KEY 184 | { 185 | LPBYTE pbData; 186 | DWORD cbData; 187 | } QUERY_KEY, *PQUERY_KEY; 188 | 189 | // Structure for SFileFindFirstFile and SFileFindNextFile 190 | typedef struct _CASC_FIND_DATA 191 | { 192 | char szFileName[MAX_PATH]; // Full name of the found file 193 | char * szPlainName; // Plain name of the found file 194 | BYTE EncodingKey[MD5_HASH_SIZE]; // Encoding key 195 | DWORD dwLocaleFlags; // Locale flags (WoW only) 196 | DWORD dwFileSize; // Size of the file 197 | 198 | } CASC_FIND_DATA, *PCASC_FIND_DATA; 199 | 200 | //----------------------------------------------------------------------------- 201 | // Callback functions 202 | 203 | typedef struct TFileStream TFileStream; 204 | typedef void (WINAPI * STREAM_DOWNLOAD_CALLBACK)(void * pvUserData, ULONGLONG ByteOffset, DWORD dwTotalBytes); 205 | 206 | //----------------------------------------------------------------------------- 207 | // We have our own qsort implementation, optimized for sorting array of pointers 208 | 209 | void qsort_pointer_array(void ** base, size_t num, int (*compare)(const void *, const void *, const void *), const void * context); 210 | 211 | //----------------------------------------------------------------------------- 212 | // Functions for storage manipulation 213 | 214 | bool WINAPI CascOpenStorage(const TCHAR * szDataPath, DWORD dwLocaleMask, HANDLE * phStorage); 215 | bool WINAPI CascGetStorageInfo(HANDLE hStorage, CASC_STORAGE_INFO_CLASS InfoClass, void * pvStorageInfo, size_t cbStorageInfo, size_t * pcbLengthNeeded); 216 | bool WINAPI CascCloseStorage(HANDLE hStorage); 217 | 218 | bool WINAPI CascOpenFileByIndexKey(HANDLE hStorage, PQUERY_KEY pIndexKey, DWORD dwFlags, HANDLE * phFile); 219 | bool WINAPI CascOpenFileByEncodingKey(HANDLE hStorage, PQUERY_KEY pEncodingKey, DWORD dwFlags, HANDLE * phFile); 220 | bool WINAPI CascOpenFile(HANDLE hStorage, const char * szFileName, DWORD dwLocale, DWORD dwFlags, HANDLE * phFile); 221 | DWORD WINAPI CascGetFileSize(HANDLE hFile, PDWORD pdwFileSizeHigh); 222 | DWORD WINAPI CascSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod); 223 | bool WINAPI CascReadFile(HANDLE hFile, void * lpBuffer, DWORD dwToRead, PDWORD pdwRead); 224 | bool WINAPI CascCloseFile(HANDLE hFile); 225 | 226 | HANDLE WINAPI CascFindFirstFile(HANDLE hStorage, const char * szMask, PCASC_FIND_DATA pFindData, const TCHAR * szListFile); 227 | bool WINAPI CascFindNextFile(HANDLE hFind, PCASC_FIND_DATA pFindData); 228 | bool WINAPI CascFindClose(HANDLE hFind); 229 | 230 | //----------------------------------------------------------------------------- 231 | // GetLastError/SetLastError support for non-Windows platform 232 | 233 | #ifndef PLATFORM_WINDOWS 234 | 235 | int GetLastError(); 236 | void SetLastError(int nError); 237 | 238 | #endif // PLATFORM_WINDOWS 239 | 240 | #ifdef __cplusplus 241 | } // extern "C" 242 | #endif 243 | 244 | #endif // __CASCLIB_H__ 245 | -------------------------------------------------------------------------------- /src/StormLibSharp/MpqArchive.cs: -------------------------------------------------------------------------------- 1 | using StormLibSharp.Native; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Globalization; 6 | using System.IO; 7 | using System.IO.MemoryMappedFiles; 8 | using System.Linq; 9 | using System.Runtime.InteropServices; 10 | using System.Text; 11 | 12 | namespace StormLibSharp 13 | { 14 | public class MpqArchive : IDisposable 15 | { 16 | private MpqArchiveSafeHandle _handle; 17 | private List _openFiles = new List(); 18 | private FileAccess _accessType; 19 | private List _compactCallbacks = new List(); 20 | private SFILE_COMPACT_CALLBACK _compactCallback; 21 | 22 | #region Constructors / Factories 23 | public MpqArchive(string filePath, FileAccess accessType) 24 | { 25 | _accessType = accessType; 26 | SFileOpenArchiveFlags flags = SFileOpenArchiveFlags.TypeIsFile; 27 | if (accessType == FileAccess.Read) 28 | flags |= SFileOpenArchiveFlags.AccessReadOnly; 29 | else 30 | flags |= SFileOpenArchiveFlags.AccessReadWriteShare; 31 | 32 | // constant 2 = SFILE_OPEN_HARD_DISK_FILE 33 | if (!NativeMethods.SFileOpenArchive(filePath, 2, flags, out _handle)) 34 | throw new Win32Exception(); // Implicitly calls GetLastError 35 | } 36 | 37 | public MpqArchive(MemoryMappedFile file, FileAccess accessType) 38 | { 39 | _accessType = accessType; 40 | string fileName = Win32Methods.GetFileNameOfMemoryMappedFile(file); 41 | if (fileName == null) 42 | throw new ArgumentException("Could not retrieve the name of the file to initialize."); 43 | 44 | SFileOpenArchiveFlags flags = SFileOpenArchiveFlags.TypeIsMemoryMapped; 45 | if (accessType == FileAccess.Read) 46 | flags |= SFileOpenArchiveFlags.AccessReadOnly; 47 | else 48 | flags |= SFileOpenArchiveFlags.AccessReadWriteShare; 49 | 50 | // constant 2 = SFILE_OPEN_HARD_DISK_FILE 51 | if (!NativeMethods.SFileOpenArchive(fileName, 2, flags, out _handle)) 52 | throw new Win32Exception(); // Implicitly calls GetLastError 53 | } 54 | 55 | private MpqArchive(string filePath, MpqArchiveVersion version, MpqFileStreamAttributes listfileAttributes, MpqFileStreamAttributes attributesFileAttributes, int maxFileCount) 56 | { 57 | if (maxFileCount < 0) 58 | throw new ArgumentException("maxFileCount"); 59 | 60 | SFileOpenArchiveFlags flags = SFileOpenArchiveFlags.TypeIsFile | SFileOpenArchiveFlags.AccessReadWriteShare; 61 | flags |= (SFileOpenArchiveFlags)version; 62 | 63 | //SFILE_CREATE_MPQ create = new SFILE_CREATE_MPQ() 64 | //{ 65 | // cbSize = unchecked((uint)Marshal.SizeOf(typeof(SFILE_CREATE_MPQ))), 66 | // dwMaxFileCount = unchecked((uint)maxFileCount), 67 | // dwMpqVersion = (uint)version, 68 | // dwFileFlags1 = (uint)listfileAttributes, 69 | // dwFileFlags2 = (uint)attributesFileAttributes, 70 | // dwStreamFlags = (uint)flags, 71 | //}; 72 | 73 | //if (!NativeMethods.SFileCreateArchive2(filePath, ref create, out _handle)) 74 | // throw new Win32Exception(); 75 | if (!NativeMethods.SFileCreateArchive(filePath, (uint)flags, int.MaxValue, out _handle)) 76 | throw new Win32Exception(); 77 | } 78 | 79 | public static MpqArchive CreateNew(string mpqPath, MpqArchiveVersion version) 80 | { 81 | return CreateNew(mpqPath, version, MpqFileStreamAttributes.None, MpqFileStreamAttributes.None, int.MaxValue); 82 | } 83 | 84 | public static MpqArchive CreateNew(string mpqPath, MpqArchiveVersion version, MpqFileStreamAttributes listfileAttributes, 85 | MpqFileStreamAttributes attributesFileAttributes, int maxFileCount) 86 | { 87 | return new MpqArchive(mpqPath, version, listfileAttributes, attributesFileAttributes, maxFileCount); 88 | } 89 | #endregion 90 | 91 | #region Properties 92 | // TODO: Move to common location. 93 | // This is a global setting, not per-archive setting. 94 | 95 | //public int Locale 96 | //{ 97 | // get 98 | // { 99 | // throw new NotImplementedException(); 100 | // } 101 | // set 102 | // { 103 | // throw new NotImplementedException(); 104 | // } 105 | //} 106 | 107 | public long MaxFileCount 108 | { 109 | get 110 | { 111 | VerifyHandle(); 112 | return NativeMethods.SFileGetMaxFileCount(_handle); 113 | } 114 | set 115 | { 116 | if (value < 0 || value > uint.MaxValue) 117 | throw new ArgumentException("value"); 118 | VerifyHandle(); 119 | 120 | if (!NativeMethods.SFileSetMaxFileCount(_handle, unchecked((uint)value))) 121 | throw new Win32Exception(); 122 | } 123 | } 124 | 125 | private void VerifyHandle() 126 | { 127 | if (_handle == null || _handle.IsInvalid) 128 | throw new ObjectDisposedException("MpqArchive"); 129 | } 130 | 131 | public bool IsPatchedArchive 132 | { 133 | get 134 | { 135 | VerifyHandle(); 136 | return NativeMethods.SFileIsPatchedArchive(_handle); 137 | } 138 | } 139 | #endregion 140 | 141 | public void Flush() 142 | { 143 | VerifyHandle(); 144 | if (!NativeMethods.SFileFlushArchive(_handle)) 145 | throw new Win32Exception(); 146 | } 147 | 148 | public int AddListFile(string listfileContents) 149 | { 150 | VerifyHandle(); 151 | return NativeMethods.SFileAddListFile(_handle, listfileContents); 152 | } 153 | 154 | public void AddFileFromDisk(string filePath, string archiveName) 155 | { 156 | VerifyHandle(); 157 | 158 | if (!NativeMethods.SFileAddFile(_handle, filePath, archiveName, 0)) 159 | throw new Win32Exception(); 160 | } 161 | 162 | public void Compact(string listfile) 163 | { 164 | VerifyHandle(); 165 | if (!NativeMethods.SFileCompactArchive(_handle, listfile, false)) 166 | throw new Win32Exception(); 167 | } 168 | 169 | private void _OnCompact(IntPtr pvUserData, uint dwWorkType, ulong bytesProcessed, ulong totalBytes) 170 | { 171 | MpqArchiveCompactingEventArgs args = new MpqArchiveCompactingEventArgs(dwWorkType, bytesProcessed, totalBytes); 172 | OnCompacting(args); 173 | } 174 | 175 | protected virtual void OnCompacting(MpqArchiveCompactingEventArgs e) 176 | { 177 | foreach (var cb in _compactCallbacks) 178 | { 179 | cb(this, e); 180 | } 181 | } 182 | 183 | public event MpqArchiveCompactingEventHandler Compacting 184 | { 185 | add 186 | { 187 | VerifyHandle(); 188 | _compactCallback = _OnCompact; 189 | if (!NativeMethods.SFileSetCompactCallback(_handle, _compactCallback, IntPtr.Zero)) 190 | throw new Win32Exception(); 191 | 192 | _compactCallbacks.Add(value); 193 | } 194 | remove 195 | { 196 | _compactCallbacks.Remove(value); 197 | 198 | VerifyHandle(); 199 | if (_compactCallbacks.Count == 0) 200 | { 201 | if (!NativeMethods.SFileSetCompactCallback(_handle, null, IntPtr.Zero)) 202 | { 203 | // Don't do anything here. Remove shouldn't fail hard. 204 | } 205 | } 206 | } 207 | } 208 | 209 | // TODO: Determine if SFileGetAttributes/SFileSetAttributes/SFileUpdateFileAttributes deserves a projection. 210 | // It's unclear - these seem to affect the (attributes) file but I can't figure out exactly what that means. 211 | 212 | public void AddPatchArchive(string patchPath) 213 | { 214 | VerifyHandle(); 215 | 216 | if (!NativeMethods.SFileOpenPatchArchive(_handle, patchPath, null, 0)) 217 | throw new Win32Exception(); 218 | } 219 | 220 | public void AddPatchArchives(IEnumerable patchPaths) 221 | { 222 | if (patchPaths == null) 223 | throw new ArgumentNullException("patchPaths"); 224 | 225 | VerifyHandle(); 226 | 227 | foreach (string path in patchPaths) 228 | { 229 | // Don't sublet to AddPatchArchive to avoid having to repeatedly call VerifyHandle() 230 | if (!NativeMethods.SFileOpenPatchArchive(_handle, path, null, 0)) 231 | throw new Win32Exception(); 232 | } 233 | } 234 | 235 | public bool HasFile(string fileToFind) 236 | { 237 | VerifyHandle(); 238 | 239 | return NativeMethods.SFileHasFile(_handle, fileToFind); 240 | } 241 | 242 | public MpqFileStream OpenFile(string fileName) 243 | { 244 | VerifyHandle(); 245 | 246 | MpqFileSafeHandle fileHandle; 247 | if (!NativeMethods.SFileOpenFileEx(_handle, fileName, 0, out fileHandle)) 248 | throw new Win32Exception(); 249 | 250 | MpqFileStream fs = new MpqFileStream(fileHandle, _accessType, this); 251 | _openFiles.Add(fs); 252 | return fs; 253 | } 254 | 255 | public void ExtractFile(string fileToExtract, string destinationPath) 256 | { 257 | VerifyHandle(); 258 | 259 | if (!NativeMethods.SFileExtractFile(_handle, fileToExtract, destinationPath, 0)) 260 | throw new Win32Exception(); 261 | } 262 | 263 | public MpqFileVerificationResults VerifyFile(string fileToVerify) 264 | { 265 | VerifyHandle(); 266 | 267 | return (MpqFileVerificationResults)NativeMethods.SFileVerifyFile(_handle, fileToVerify, 0); 268 | } 269 | 270 | // TODO: Consider SFileVerifyRawData 271 | 272 | public MpqArchiveVerificationResult VerifyArchive() 273 | { 274 | VerifyHandle(); 275 | 276 | return (MpqArchiveVerificationResult)NativeMethods.SFileVerifyArchive(_handle); 277 | } 278 | 279 | 280 | #region IDisposable implementation 281 | public void Dispose() 282 | { 283 | Dispose(true); 284 | } 285 | 286 | ~MpqArchive() 287 | { 288 | Dispose(false); 289 | } 290 | 291 | protected virtual void Dispose(bool disposing) 292 | { 293 | if (disposing) 294 | { 295 | // Release owned files first. 296 | if (_openFiles != null) 297 | { 298 | foreach (var file in _openFiles) 299 | { 300 | file.Dispose(); 301 | } 302 | 303 | _openFiles.Clear(); 304 | _openFiles = null; 305 | } 306 | 307 | // Release 308 | if (_handle != null && !_handle.IsInvalid) 309 | { 310 | _handle.Close(); 311 | _handle = null; 312 | } 313 | } 314 | } 315 | 316 | internal void RemoveOwnedFile(MpqFileStream file) 317 | { 318 | _openFiles.Remove(file); 319 | } 320 | 321 | #endregion 322 | } 323 | 324 | public enum MpqArchiveVersion 325 | { 326 | Version1 = 0, 327 | Version2 = 0x01000000, 328 | Version3 = 0x02000000, 329 | Version4 = 0x03000000, 330 | } 331 | 332 | [Flags] 333 | public enum MpqFileStreamAttributes 334 | { 335 | None = 0x0, 336 | } 337 | 338 | [Flags] 339 | public enum MpqFileVerificationResults 340 | { 341 | /// 342 | /// There were no errors with the file. 343 | /// 344 | Verified = 0, 345 | /// 346 | /// Failed to open the file 347 | /// 348 | Error = 0x1, 349 | /// 350 | /// Failed to read all data from the file 351 | /// 352 | ReadError = 0x2, 353 | /// 354 | /// File has sector CRC 355 | /// 356 | HasSectorCrc = 0x4, 357 | /// 358 | /// Sector CRC check failed 359 | /// 360 | SectorCrcError = 0x8, 361 | /// 362 | /// File has CRC32 363 | /// 364 | HasChecksum = 0x10, 365 | /// 366 | /// CRC32 check failed 367 | /// 368 | ChecksumError = 0x20, 369 | /// 370 | /// File has data MD5 371 | /// 372 | HasMd5 = 0x40, 373 | /// 374 | /// MD5 check failed 375 | /// 376 | Md5Error = 0x80, 377 | /// 378 | /// File has raw data MD5 379 | /// 380 | HasRawMd5 = 0x100, 381 | /// 382 | /// Raw MD5 check failed 383 | /// 384 | RawMd5Error = 0x200, 385 | } 386 | 387 | public enum MpqArchiveVerificationResult 388 | { 389 | /// 390 | /// There is no signature in the MPQ 391 | /// 392 | NoSignature = 0, 393 | /// 394 | /// There was an error during verifying signature (like no memory) 395 | /// 396 | VerificationFailed = 1, 397 | /// 398 | /// There is a weak signature and sign check passed 399 | /// 400 | WeakSignatureVerified = 2, 401 | /// 402 | /// There is a weak signature but sign check failed 403 | /// 404 | WeakSignatureFailed = 3, 405 | /// 406 | /// There is a strong signature and sign check passed 407 | /// 408 | StrongSignatureVerified = 4, 409 | /// 410 | /// There is a strong signature but sign check failed 411 | /// 412 | StrongSignatureFailed = 5, 413 | } 414 | } 415 | -------------------------------------------------------------------------------- /src/CascLibSharp/CascStorageContext.cs: -------------------------------------------------------------------------------- 1 | using CascLibSharp.Native; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace CascLibSharp 10 | { 11 | /// 12 | /// Represents a CASC storage directory. 13 | /// 14 | public class CascStorageContext : IDisposable 15 | { 16 | private CascApi _api; 17 | private CascStorageSafeHandle _handle; 18 | private Lazy _hasListfile; 19 | private Lazy _clientType; 20 | private Lazy _fileCount; 21 | private Lazy _gameBuild; 22 | 23 | /// 24 | /// Creates a new CascStorageContext for the specified path. 25 | /// 26 | /// The path to a game's data directory. 27 | /// An example directory is c:\Program Files (x86)\Heroes of the Storm\HeroesData. 28 | public CascStorageContext(string dataPath) 29 | { 30 | _api = CascApi.Instance; 31 | 32 | if (!_api.CascOpenStorage(dataPath, 0, out _handle) || _handle.IsInvalid) 33 | throw new CascException(); 34 | _handle.Api = _api; 35 | 36 | _hasListfile = new Lazy(CheckHasListfile); 37 | _clientType = new Lazy(GetClient); 38 | _fileCount = new Lazy(GetFileCount); 39 | _gameBuild = new Lazy(GetGameBuild); 40 | } 41 | 42 | /// 43 | /// Opens a file by its fully-qualified name. 44 | /// 45 | /// The name of the file. 46 | /// The file's locale (defaults to English-United States). 47 | /// A CascFileStream, which implements a Stream. 48 | /// Thrown if the CascStorageContext has been disposed. 49 | /// Thrown if the file does not exist within the CASC storage container. 50 | public CascFileStream OpenFile(string fileName, CascLocales locale = CascLocales.EnUs) 51 | { 52 | if (_handle == null || _handle.IsInvalid) 53 | throw new ObjectDisposedException("CascStorageContext"); 54 | 55 | CascStorageFileSafeHandle hFile; 56 | if (!_api.CascOpenFile(_handle, fileName, (uint)locale, 0, out hFile)) 57 | throw new CascException(); 58 | 59 | hFile.Api = _api; 60 | 61 | return new CascFileStream(hFile, _api); 62 | } 63 | 64 | /// 65 | /// Opens a file by its index key. 66 | /// 67 | /// 68 | /// 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, 69 | /// but I don't know how someone obtains the index key. They are not produced in the public API of CascLib. 70 | /// 71 | /// The index key to search. 72 | /// A CascFileStream, which implements a Stream. 73 | /// Thrown if the CascStorageContext has been disposed. 74 | /// Thrown if the file does not exist within the CASC storage container. 75 | public CascFileStream OpenFileByIndexKey(byte[] indexKey) 76 | { 77 | if (_handle == null || _handle.IsInvalid) 78 | throw new ObjectDisposedException("CascStorageContext"); 79 | 80 | using (CoTaskMem mem = CoTaskMem.FromBytes(indexKey)) 81 | { 82 | QueryKey qk = new QueryKey(); 83 | qk.cbData = unchecked((uint)indexKey.Length); 84 | qk.pbData = mem.Pointer; 85 | 86 | CascStorageFileSafeHandle hFile; 87 | if (!_api.CascOpenFileByIndexKey(_handle, ref qk, 0, out hFile)) 88 | throw new CascException(); 89 | 90 | hFile.Api = _api; 91 | 92 | return new CascFileStream(hFile, _api); 93 | } 94 | } 95 | 96 | /// 97 | /// Opens a file by its encoding key. 98 | /// 99 | /// A 16-byte key representing the file. Encoding keys may be obtained via . 100 | /// A CascFileStream, which implements a Stream. 101 | /// Thrown if the CascStorageContext has been disposed. 102 | /// Thrown if the file does not exist within the CASC storage container. 103 | public CascFileStream OpenFileByEncodingKey(byte[] encodingKey) 104 | { 105 | if (_handle == null || _handle.IsInvalid) 106 | throw new ObjectDisposedException("CascStorageContext"); 107 | 108 | using (CoTaskMem mem = CoTaskMem.FromBytes(encodingKey)) 109 | { 110 | QueryKey qk = new QueryKey(); 111 | qk.cbData = unchecked((uint)encodingKey.Length); 112 | qk.pbData = mem.Pointer; 113 | 114 | CascStorageFileSafeHandle hFile; 115 | if (!_api.CascOpenFileByEncodingKey(_handle, ref qk, 0, out hFile)) 116 | throw new CascException(); 117 | 118 | hFile.Api = _api; 119 | 120 | return new CascFileStream(hFile, _api); 121 | } 122 | } 123 | 124 | /// 125 | /// Searches the files in the CASC container for files that match the specified pattern. 126 | /// 127 | /// The mask to search. * and ? are valid tokens for substitution. 128 | /// A path to a listfile. Required if the CASC container is for World of Warcraft. 129 | /// An enumeration of matching file references in the CASC container. 130 | public IEnumerable SearchFiles(string mask, string listFilePath = null) 131 | { 132 | if (_handle == null || _handle.IsInvalid) 133 | throw new ObjectDisposedException("CascStorageContext"); 134 | 135 | if (this.GameClient == CascKnownClient.WorldOfWarcraft && string.IsNullOrWhiteSpace(listFilePath)) 136 | throw new ArgumentNullException("listFilePath"); 137 | 138 | CascFindData cfd = new CascFindData(); 139 | using (var handle = _api.CascFindFirstFile(_handle, mask, ref cfd, listFilePath)) 140 | { 141 | if (handle.IsInvalid) 142 | yield break; 143 | 144 | handle.Api = _api; 145 | 146 | yield return cfd.ToFoundFile(this); 147 | 148 | while (_api.CascFindNextFile(handle, ref cfd)) 149 | { 150 | yield return cfd.ToFoundFile(this); 151 | } 152 | } 153 | } 154 | 155 | private bool CheckHasListfile() 156 | { 157 | if (_handle == null || _handle.IsInvalid) 158 | throw new ObjectDisposedException("CascStorageContext"); 159 | 160 | uint storageInfo = 0, lengthNeeded = 4; 161 | if (!_api.CascGetStorageInfo(_handle, CascStorageInfoClass.Features, ref storageInfo, new IntPtr(4), ref lengthNeeded)) 162 | throw new CascException(); 163 | 164 | CascStorageFeatures features = (CascStorageFeatures)storageInfo; 165 | if (features.HasFlag(CascStorageFeatures.HasListfile)) 166 | return true; 167 | 168 | return false; 169 | } 170 | 171 | private CascKnownClient GetClient() 172 | { 173 | if (_handle == null || _handle.IsInvalid) 174 | throw new ObjectDisposedException("CascStorageContext"); 175 | 176 | uint storageInfo = 0, lengthNeeded = 4; 177 | if (!_api.CascGetStorageInfo(_handle, CascStorageInfoClass.GameInfo, ref storageInfo, new IntPtr(4), ref lengthNeeded)) 178 | throw new CascException(); 179 | 180 | CascGameId gameId = (CascGameId)storageInfo; 181 | 182 | return gameId.ToKnownClient(); 183 | } 184 | 185 | private long GetFileCount() 186 | { 187 | if (_handle == null || _handle.IsInvalid) 188 | throw new ObjectDisposedException("CascStorageContext"); 189 | 190 | uint storageInfo = 0, lengthNeeded = 4; 191 | if (!_api.CascGetStorageInfo(_handle, CascStorageInfoClass.Features, ref storageInfo, new IntPtr(4), ref lengthNeeded)) 192 | throw new CascException(); 193 | 194 | return storageInfo; 195 | } 196 | 197 | private int GetGameBuild() 198 | { 199 | if (_handle == null || _handle.IsInvalid) 200 | throw new ObjectDisposedException("CascStorageContext"); 201 | 202 | uint storageInfo = 0, lengthNeeded = 4; 203 | if (!_api.CascGetStorageInfo(_handle, CascStorageInfoClass.Features, ref storageInfo, new IntPtr(4), ref lengthNeeded)) 204 | throw new CascException(); 205 | 206 | return unchecked((int)storageInfo); 207 | } 208 | 209 | /// 210 | /// Gets whether the CASC container has a listfile or if one must be supplied while searching. 211 | /// 212 | public bool HasListfile 213 | { 214 | get 215 | { 216 | return _hasListfile.Value; 217 | } 218 | } 219 | 220 | /// 221 | /// Gets the number of files in the CASC container. 222 | /// 223 | public long FileCount 224 | { 225 | get 226 | { 227 | return _fileCount.Value; 228 | } 229 | } 230 | 231 | /// 232 | /// Gets the build number of the game. 233 | /// 234 | public int GameBuild 235 | { 236 | get 237 | { 238 | return _gameBuild.Value; 239 | } 240 | } 241 | 242 | /// 243 | /// Gets the game client of the container, if it can be determined. 244 | /// 245 | public CascKnownClient GameClient 246 | { 247 | get 248 | { 249 | return _clientType.Value; 250 | } 251 | } 252 | 253 | #region IDisposable implementation 254 | /// 255 | /// Finalizes the storage context. 256 | /// 257 | ~CascStorageContext() 258 | { 259 | Dispose(false); 260 | } 261 | 262 | /// 263 | /// Disposes the object. 264 | /// 265 | public void Dispose() 266 | { 267 | Dispose(true); 268 | GC.SuppressFinalize(this); 269 | } 270 | 271 | /// 272 | /// Disposes the object, cleaning up the unmanaged objects. 273 | /// 274 | /// True if this is being called via the Dispose() method; false if it's being called by the finalizer. 275 | protected virtual void Dispose(bool disposing) 276 | { 277 | if (disposing) 278 | { 279 | if (!_handle.IsInvalid) 280 | { 281 | _handle.Close(); 282 | _handle = null; 283 | } 284 | } 285 | } 286 | #endregion 287 | } 288 | 289 | /// 290 | /// Known locales supported by CASC. 291 | /// 292 | [Flags] 293 | public enum CascLocales 294 | { 295 | /// 296 | /// All available locales. 297 | /// 298 | All = -1, 299 | /// 300 | /// No locales. 301 | /// 302 | None = 0, 303 | /// 304 | /// Unknown 305 | /// 306 | Unknown1 = 1, 307 | /// 308 | /// English, United States 309 | /// 310 | EnUs = 2, 311 | /// 312 | /// Korean, South Korea 313 | /// 314 | KoKr = 4, 315 | /// 316 | /// Reserved (unknown) 317 | /// 318 | Reserved = 8, 319 | /// 320 | /// French, France 321 | /// 322 | FrFr = 0x10, 323 | /// 324 | /// German, Germany 325 | /// 326 | DeDe = 0x20, 327 | /// 328 | /// Chinese, China 329 | /// 330 | ZhCn = 0x40, 331 | /// 332 | /// Spanish, Spain 333 | /// 334 | EsEs = 0x80, 335 | /// 336 | /// Chinese, Taiwan 337 | /// 338 | ZhTw = 0x100, 339 | /// 340 | /// English, Great Britain 341 | /// 342 | EnGb = 0x200, 343 | /// 344 | /// English, China 345 | /// 346 | EnCn = 0x400, 347 | /// 348 | /// English, Taiwan 349 | /// 350 | EnTw = 0x800, 351 | /// 352 | /// Spanish, Mexico 353 | /// 354 | EsMx = 0x1000, 355 | /// 356 | /// Russian, Russia 357 | /// 358 | RuRu = 0x2000, 359 | /// 360 | /// Portuguese, Brazil 361 | /// 362 | PtBr = 0x4000, 363 | /// 364 | /// Italian, Italy 365 | /// 366 | ItIt = 0x8000, 367 | /// 368 | /// Portuguese, Portugal 369 | /// 370 | PtPt = 0x10000, 371 | } 372 | 373 | /// 374 | /// Known clients supporting CASC 375 | /// 376 | public enum CascKnownClient 377 | { 378 | /// 379 | /// The game client was unrecognized. 380 | /// 381 | Unknown = -1, 382 | /// 383 | /// Heroes of the Storm 384 | /// 385 | HeroesOfTheStorm = 0, 386 | /// 387 | /// Diablo 3 388 | /// 389 | Diablo3 = 1, 390 | /// 391 | /// World of Warcraft 392 | /// 393 | WorldOfWarcraft = 2, 394 | /// 395 | /// Overwatch 396 | /// 397 | Overwatch = 3, 398 | /// 399 | /// Starcraft 2 400 | /// 401 | Starcraft2 = 4, 402 | } 403 | } 404 | -------------------------------------------------------------------------------- /src/StormLibSharp/Native/NativeMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | 7 | namespace StormLibSharp.Native 8 | { 9 | internal static class NativeMethods 10 | { 11 | private const string STORMLIB = "stormlib.dll"; 12 | 13 | #region Functions for manipulation with StormLib global flags 14 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 15 | public static extern uint SFileGetLocale(); 16 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 17 | public static extern uint SFileSetLocale(uint lcNewLocale); 18 | #endregion 19 | 20 | #region Functions for archive manipulation 21 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 22 | public static extern bool SFileOpenArchive( 23 | [MarshalAs(UnmanagedType.LPTStr)] string szMpqName, 24 | uint dwPriority, 25 | SFileOpenArchiveFlags dwFlags, 26 | out MpqArchiveSafeHandle phMpq 27 | ); 28 | 29 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 30 | public static extern bool SFileCreateArchive( 31 | [MarshalAs(UnmanagedType.LPTStr)] string szMpqName, 32 | uint dwCreateFlags, 33 | uint dwMaxFileCount, 34 | out MpqArchiveSafeHandle phMpq 35 | ); 36 | 37 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 38 | public static extern bool SFileCreateArchive2( 39 | [MarshalAs(UnmanagedType.LPTStr)] string szMpqName, 40 | ref SFILE_CREATE_MPQ pCreateInfo, 41 | out MpqArchiveSafeHandle phMpq 42 | ); 43 | 44 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 45 | public static extern bool SFileSetDownloadCallback( 46 | MpqArchiveSafeHandle hMpq, 47 | [MarshalAs(UnmanagedType.FunctionPtr)] SFILE_DOWNLOAD_CALLBACK pfnCallback, 48 | IntPtr pvUserData 49 | ); 50 | 51 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 52 | public static extern bool SFileFlushArchive(MpqArchiveSafeHandle hMpq); 53 | 54 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 55 | public static extern bool SFileCloseArchive(IntPtr hMpq); 56 | 57 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 58 | public static extern bool SFileCloseArchive(MpqArchiveSafeHandle hMpq); 59 | #endregion 60 | 61 | #region Adds another listfile into MPQ. 62 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 63 | public static extern int SFileAddListFile( 64 | MpqArchiveSafeHandle hMpq, 65 | [MarshalAs(UnmanagedType.LPStr)] string szListFile 66 | ); 67 | #endregion 68 | 69 | #region Archive compacting 70 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 71 | public static extern bool SFileSetCompactCallback( 72 | MpqArchiveSafeHandle hMpq, 73 | SFILE_COMPACT_CALLBACK compactCB, 74 | IntPtr pvUserData 75 | ); 76 | 77 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 78 | public static extern bool SFileCompactArchive( 79 | MpqArchiveSafeHandle hMpq, 80 | [MarshalAs(UnmanagedType.LPStr)] string szListFile, 81 | bool bReserved 82 | ); 83 | #endregion 84 | 85 | #region Maximum file count 86 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 87 | public static extern uint SFileGetMaxFileCount(MpqArchiveSafeHandle hMpq); 88 | 89 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 90 | public static extern bool SFileSetMaxFileCount(MpqArchiveSafeHandle hMpq, uint dwMaxFileCount); 91 | #endregion 92 | 93 | #region Changing (attributes) file 94 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 95 | public static extern uint SFileGetAttributes(MpqArchiveSafeHandle hMpq); 96 | 97 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 98 | public static extern bool SFileSetAttributes(MpqArchiveSafeHandle hMpq, uint dwFlags); 99 | 100 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 101 | public static extern bool SFileUpdateFileAttributes( 102 | MpqArchiveSafeHandle hMpq, 103 | [MarshalAs(UnmanagedType.LPStr)] string szFileName 104 | ); 105 | #endregion 106 | 107 | #region Functions for manipulation with patch archives 108 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 109 | public static extern bool SFileOpenPatchArchive( 110 | MpqArchiveSafeHandle hMpq, 111 | [MarshalAs(UnmanagedType.LPTStr)] string szPatchMpqName, 112 | [MarshalAs(UnmanagedType.LPStr)] string szPatchPathPrefix, 113 | uint dwFlags 114 | ); 115 | 116 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 117 | public static extern bool SFileIsPatchedArchive(MpqArchiveSafeHandle hMpq); 118 | #endregion 119 | 120 | #region Functions for file manipulation 121 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 122 | public static extern bool SFileHasFile( 123 | MpqArchiveSafeHandle hMpq, 124 | [MarshalAs(UnmanagedType.LPStr)] string szFileName 125 | ); 126 | 127 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 128 | public static extern bool SFileOpenFileEx( 129 | MpqArchiveSafeHandle hMpq, 130 | [MarshalAs(UnmanagedType.LPStr)] string szFileName, 131 | uint dwSearchScope, 132 | out MpqFileSafeHandle phFile 133 | ); 134 | 135 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 136 | public static extern uint SFileGetFileSize(MpqFileSafeHandle hFile, ref uint pdwFileSizeHigh); 137 | 138 | public static unsafe uint SFileGetFilePointer( 139 | MpqFileSafeHandle hFile 140 | ) 141 | { 142 | if (hFile.IsInvalid || hFile.IsClosed) 143 | throw new InvalidOperationException(); 144 | 145 | IntPtr handle = hFile.DangerousGetHandle(); 146 | _TMPQFileHeader* header = (_TMPQFileHeader*)handle.ToPointer(); 147 | return header->dwFilePos; 148 | } 149 | 150 | //public static unsafe uint SFileGetFileSize( 151 | // MpqFileSafeHandle hFile 152 | // ) 153 | //{ 154 | // if (hFile.IsInvalid || hFile.IsClosed) 155 | // throw new InvalidOperationException(); 156 | 157 | // IntPtr handle = hFile.DangerousGetHandle(); 158 | // _TMPQFileHeader* header = (_TMPQFileHeader*)handle.ToPointer(); 159 | // return header->pFileEntry->dwFileSize; 160 | //} 161 | 162 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 163 | public static extern uint SFileSetFilePointer( 164 | MpqFileSafeHandle hFile, 165 | uint lFilePos, 166 | ref uint plFilePosHigh, 167 | uint dwMoveMethod 168 | ); 169 | 170 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 171 | public static extern bool SFileReadFile( 172 | MpqFileSafeHandle hFile, 173 | IntPtr lpBuffer, 174 | uint dwToRead, 175 | out uint pdwRead, 176 | ref System.Threading.NativeOverlapped lpOverlapped 177 | ); 178 | 179 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 180 | public static extern bool SFileCloseFile(IntPtr hFile); 181 | 182 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 183 | public static extern bool SFileCloseFile(MpqFileSafeHandle hFile); 184 | 185 | #region Retrieving info about a file in the archive 186 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 187 | public static extern bool SFileGetFileInfo( 188 | IntPtr hMpqOrFile, 189 | SFileInfoClass InfoClass, 190 | IntPtr pvFileInfo, 191 | uint cbFileInfoSize, 192 | out uint pcbLengthNeeded 193 | ); 194 | 195 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 196 | public static extern bool SFileGetFileInfo( 197 | MpqArchiveSafeHandle hMpqOrFile, 198 | SFileInfoClass InfoClass, 199 | IntPtr pvFileInfo, 200 | uint cbFileInfoSize, 201 | out uint pcbLengthNeeded 202 | ); 203 | 204 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 205 | public static extern bool SFileGetFileInfo( 206 | MpqFileSafeHandle hMpqOrFile, 207 | SFileInfoClass InfoClass, 208 | IntPtr pvFileInfo, 209 | uint cbFileInfoSize, 210 | out uint pcbLengthNeeded 211 | ); 212 | 213 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 214 | public static extern bool SFileGetFileName( 215 | MpqFileSafeHandle hFile, 216 | [MarshalAs(UnmanagedType.LPStr)] out string szFileName 217 | ); 218 | 219 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 220 | public static extern bool SFileFreeFileInfo( 221 | IntPtr pvFileInfo, 222 | SFileInfoClass infoClass 223 | ); 224 | #endregion 225 | 226 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 227 | public static extern bool SFileExtractFile( 228 | MpqArchiveSafeHandle hMpq, 229 | [MarshalAs(UnmanagedType.LPStr)] string szToExtract, 230 | [MarshalAs(UnmanagedType.LPTStr)] string szExtracted, 231 | uint dwSearchScope 232 | ); 233 | 234 | #endregion 235 | 236 | #region Functions for file and archive verification 237 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 238 | public static extern bool SFileGetFileChecksums( 239 | MpqArchiveSafeHandle hMpq, 240 | [MarshalAs(UnmanagedType.LPStr)] string szFileName, 241 | out uint pdwCrc32, 242 | IntPtr pMD5 243 | ); 244 | 245 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 246 | public static extern uint SFileVerifyFile( 247 | MpqArchiveSafeHandle hMpq, 248 | [MarshalAs(UnmanagedType.LPStr)] string szFileName, 249 | uint dwFlags 250 | ); 251 | 252 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 253 | public static extern int SFileVerifyRawData( 254 | MpqArchiveSafeHandle hMpq, 255 | uint dwWhatToVerify, 256 | [MarshalAs(UnmanagedType.LPStr)] string szFileName 257 | ); 258 | 259 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 260 | public static extern uint SFileVerifyArchive(MpqArchiveSafeHandle hMpq); 261 | #endregion 262 | 263 | #region Functions for file searching 264 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 265 | public static extern IntPtr SFileFindFirstFile( 266 | MpqArchiveSafeHandle hMpq, 267 | [MarshalAs(UnmanagedType.LPStr)] string szMask, 268 | out _SFILE_FIND_DATA lpFindFileData, 269 | [MarshalAs(UnmanagedType.LPStr)] string szListFile 270 | ); 271 | 272 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 273 | public static extern bool SFileFindNextFile( 274 | IntPtr hFind, 275 | [In, Out] ref _SFILE_FIND_DATA lpFindFileData 276 | ); 277 | 278 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 279 | public static extern bool SFileFindClose(IntPtr hFind); 280 | 281 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 282 | public static extern IntPtr SListFileFindFirstFile( 283 | MpqArchiveSafeHandle hMpq, 284 | [MarshalAs(UnmanagedType.LPStr)] string szListFile, 285 | [MarshalAs(UnmanagedType.LPStr)] string szMask, 286 | [In, Out] ref _SFILE_FIND_DATA lpFindFileData 287 | ); 288 | 289 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 290 | public static extern bool SListFileFindNextFile( 291 | IntPtr hFind, 292 | [In, Out] ref _SFILE_FIND_DATA lpFindFileData 293 | ); 294 | 295 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 296 | public static extern bool SListFileFindClose(IntPtr hFind); 297 | #endregion 298 | 299 | #region Locale support 300 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 301 | public static extern int SFileEnumLocales( 302 | MpqArchiveSafeHandle hMpq, 303 | [MarshalAs(UnmanagedType.LPStr)] string szFileName, 304 | IntPtr plcLocales, 305 | ref uint pdwMaxLocales, 306 | uint dwSearchScope 307 | ); 308 | #endregion 309 | 310 | #region Support for adding files to the MPQ 311 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 312 | public static extern bool SFileCreateFile( 313 | MpqArchiveSafeHandle hMpq, 314 | [MarshalAs(UnmanagedType.LPStr)] string szArchiveName, 315 | ulong fileTime, 316 | uint dwFileSize, 317 | uint lcLocale, 318 | uint dwFlags, 319 | out IntPtr phFile 320 | ); 321 | 322 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 323 | public static extern bool SFileWriteFile( 324 | MpqFileSafeHandle hFile, 325 | IntPtr pvData, 326 | uint dwSize, 327 | uint dwCompression 328 | ); 329 | 330 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 331 | public static extern bool SFileFinishFile(MpqFileSafeHandle hFile); 332 | 333 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 334 | public static extern bool SFileAddFileEx( 335 | MpqArchiveSafeHandle hMpq, 336 | [MarshalAs(UnmanagedType.LPTStr)] string szFileName, 337 | [MarshalAs(UnmanagedType.LPStr)] string szArchivedName, 338 | uint dwFlags, 339 | uint dwCompression, 340 | uint dwCompressionNext 341 | ); 342 | 343 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 344 | public static extern bool SFileAddFile( 345 | MpqArchiveSafeHandle hMpq, 346 | [MarshalAs(UnmanagedType.LPTStr)] string szFileName, 347 | [MarshalAs(UnmanagedType.LPStr)] string szArchivedName, 348 | uint dwFlags 349 | ); 350 | 351 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 352 | public static extern bool SFileAddWave( 353 | MpqArchiveSafeHandle hMpq, 354 | [MarshalAs(UnmanagedType.LPTStr)] string szFileName, 355 | [MarshalAs(UnmanagedType.LPStr)] string szArchivedName, 356 | uint dwFlags, 357 | uint dwQuality 358 | ); 359 | 360 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 361 | public static extern bool SFileRemoveFile( 362 | MpqArchiveSafeHandle hMpq, 363 | [MarshalAs(UnmanagedType.LPStr)] string szFileName, 364 | uint dwSearchScope 365 | ); 366 | 367 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 368 | public static extern bool SFileRenameFile( 369 | MpqArchiveSafeHandle hMpq, 370 | [MarshalAs(UnmanagedType.LPStr)] string szOldFileName, 371 | [MarshalAs(UnmanagedType.LPStr)] string szNewFileName 372 | ); 373 | 374 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 375 | public static extern bool SFileSetFileLocale( 376 | MpqFileSafeHandle hFile, 377 | uint lcNewLocale 378 | ); 379 | 380 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 381 | public static extern bool SFileSetDataCompression(uint DataCompression); 382 | 383 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 384 | public static extern bool SFileSetAddFileCallback( 385 | MpqArchiveSafeHandle hMpq, 386 | SFILE_ADDFILE_CALLBACK AddFileCB, 387 | IntPtr pvUserData 388 | ); 389 | #endregion 390 | 391 | #region Compression and decompression 392 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 393 | public static extern int SCompImplode( 394 | IntPtr pvOutBuffer, 395 | ref int pcbOutBuffer, 396 | IntPtr pvInBuffer, 397 | int cbInBuffer 398 | ); 399 | 400 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 401 | public static extern int SCompExplode( 402 | IntPtr pvOutBuffer, 403 | ref int pcbOutBuffer, 404 | IntPtr pvInBuffer, 405 | int cbInBuffer 406 | ); 407 | 408 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 409 | public static extern int SCompCompress( 410 | IntPtr pvOutBuffer, 411 | ref int pcbOutBuffer, 412 | IntPtr pvInBuffer, 413 | int cbInBuffer, 414 | uint uCompressionMask, 415 | int nCmpType, 416 | int nCmpLevel 417 | ); 418 | 419 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 420 | public static extern int SCompDecompress( 421 | IntPtr pvOutBuffer, 422 | ref int pcbOutBuffer, 423 | IntPtr pvInBuffer, 424 | int cbInBuffer 425 | ); 426 | 427 | [DllImport(STORMLIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, PreserveSig = true, SetLastError = true, ThrowOnUnmappableChar = false)] 428 | public static extern int SCompDecompress2( 429 | IntPtr pvOutBuffer, 430 | ref int pcbOutBuffer, 431 | IntPtr pvInBuffer, 432 | int cbInBuffer 433 | ); 434 | 435 | 436 | #endregion 437 | } 438 | 439 | #pragma warning disable 0169,0649 440 | internal struct SFILE_CREATE_MPQ 441 | { 442 | public uint cbSize; 443 | public uint dwMpqVersion; 444 | private IntPtr pvUserData; 445 | private uint cbUserData; 446 | public uint dwStreamFlags; 447 | public uint dwFileFlags1; 448 | public uint dwFileFlags2; 449 | public uint dwAttrFlags; 450 | public uint dwSectorSize; 451 | public uint dwRawChunkSize; 452 | public uint dwMaxFileCount; 453 | } 454 | 455 | internal unsafe struct _SFILE_FIND_DATA 456 | { 457 | public fixed char cFileName[260]; // Full name of the found file 458 | 459 | public IntPtr szPlainName; // Plain name of the found file 460 | public uint dwHashIndex; // Hash table index for the file 461 | public uint dwBlockIndex; // Block table index for the file 462 | public uint dwFileSize; // File size in bytes 463 | public uint dwFileFlags; // MPQ file flags 464 | public uint dwCompSize; // Compressed file size 465 | public uint dwFileTimeLo; // Low 32-bits of the file time (0 if not present) 466 | public uint dwFileTimeHi; // High 32-bits of the file time (0 if not present) 467 | public uint lcLocale; // Locale version 468 | } 469 | 470 | internal unsafe struct _TFileEntry 471 | { 472 | public ulong FileNameHash; 473 | public ulong ByteOffset; 474 | public ulong FileTime; 475 | public uint dwHashIndex; 476 | public uint dwFileSize; 477 | public uint dwCmpSize; 478 | public uint dwFlags; 479 | public ushort lcLocale; 480 | public ushort wPlatform; 481 | public uint dwCrc32; 482 | public fixed byte md5[16]; 483 | public IntPtr szFileName; 484 | } 485 | 486 | // Provides enough of _TMPQFile to get to the file size and current position. 487 | internal unsafe struct _TMPQFileHeader 488 | { 489 | public IntPtr pStream; 490 | public IntPtr ha; 491 | public _TFileEntry* pFileEntry; 492 | public uint dwFileKey; 493 | public uint dwFilePos; 494 | } 495 | #pragma warning restore 0169,0649 496 | 497 | } 498 | -------------------------------------------------------------------------------- /lib/StormLib.h: -------------------------------------------------------------------------------- 1 | /*****************************************************************************/ 2 | /* StormLib.h Copyright (c) Ladislav Zezula 1999-2010 */ 3 | /*---------------------------------------------------------------------------*/ 4 | /* StormLib library v 7.02 */ 5 | /* */ 6 | /* Author : Ladislav Zezula */ 7 | /* E-mail : ladik@zezula.net */ 8 | /* WWW : http://www.zezula.net */ 9 | /*---------------------------------------------------------------------------*/ 10 | /* Date Ver Who Comment */ 11 | /* -------- ---- --- ------- */ 12 | /* xx.xx.99 1.00 Lad Created */ 13 | /* 24.03.03 2.50 Lad Version 2.50 */ 14 | /* 02.04.03 3.00 Lad Version 3.00 with compression */ 15 | /* 11.04.03 3.01 Lad Renamed to StormLib.h for compatibility with */ 16 | /* original headers for Storm.dll */ 17 | /* 10.05.03 3.02 Lad Added Pkware DCL compression */ 18 | /* 26.05.03 4.00 Lad Completed all compressions */ 19 | /* 18.06.03 4.01 Lad Added SFileSetFileLocale */ 20 | /* Added SFileExtractFile */ 21 | /* 26.07.03 4.02 Lad Implemented nameless rename and delete */ 22 | /* 26.07.03 4.03 Lad Added support for protected MPQs */ 23 | /* 28.08.03 4.10 Lad Fixed bugs that caused StormLib incorrectly work */ 24 | /* with Diablo I savegames and with files having full */ 25 | /* hash table */ 26 | /* 08.12.03 4.11 DCH Fixed bug in reading file sector larger than 0x1000 */ 27 | /* on certain files. */ 28 | /* Fixed bug in AddFile with MPQ_FILE_REPLACE_EXISTING */ 29 | /* (Thanx Daniel Chiamarello, dchiamarello@madvawes.com)*/ 30 | /* 21.12.03 4.50 Lad Completed port for Mac */ 31 | /* Fixed bug in compacting (if fsize is mul of 0x1000) */ 32 | /* Fixed bug in SCompCompress */ 33 | /* 27.05.04 4.51 Lad Changed memory management from new/delete to our */ 34 | /* own macros */ 35 | /* 22.06.04 4.60 Lad Optimized search. Support for multiple listfiles. */ 36 | /* 30.09.04 4.61 Lad Fixed some bugs (Aaargh !!!) */ 37 | /* Correctly works if HashTableSize > BlockTableSize */ 38 | /* 29.12.04 4.70 Lad Fixed compatibility problem with MPQs from WoW */ 39 | /* 14.07.05 5.00 Lad Added the BZLIB compression support */ 40 | /* Added suport of files stored as single unit */ 41 | /* 17.04.06 5.01 Lad Converted to MS Visual Studio 8.0 */ 42 | /* Fixed issue with protected Warcraft 3 protected maps */ 43 | /* 15.05.06 5.02 Lad Fixed issue with WoW 1.10+ */ 44 | /* 07.09.06 5.10 Lad Fixed processing files longer than 2GB */ 45 | /* 22.11.06 6.00 Lad Support for MPQ archives V2 */ 46 | /* 12.06.07 6.10 Lad Support for (attributes) file */ 47 | /* 10.09.07 6.12 Lad Support for MPQs protected by corrupting hash table */ 48 | /* 03.12.07 6.13 Lad Support for MPQs with hash tbl size > block tbl size */ 49 | /* 07.04.08 6.20 Lad Added SFileFlushArchive */ 50 | /* 09.04.08 Lad Removed FilePointer variable from MPQ handle */ 51 | /* structure, as it caused more problems than benefits */ 52 | /* 12.05.08 6.22 Lad Support for w3xMaster map protector */ 53 | /* 05.10.08 6.23 Lad Support for protectors who set negative values in */ 54 | /* the table of file blocks */ 55 | /* 26.05.09 6.24 Lad Fixed search for multiple lang files with deleted */ 56 | /* entries */ 57 | /* 03.09.09 6.25 Lad Fixed decompression bug in huffmann decompression */ 58 | /* 22.03.10 6.50 Lad New compressions in Starcraft II (LZMA, sparse) */ 59 | /* Fixed compacting MPQs that contain single unit files */ 60 | /* 26.04.10 7.00 Lad Major rewrite */ 61 | /* 08.06.10 7.10 Lad Support for partial MPQs */ 62 | /* 08.07.10 7.11 Lad Support for MPQs v 3.0 */ 63 | /* 20.08.10 7.20 Lad Support for opening multiple MPQs in patch mode */ 64 | /* 20.09.10 8.00 Lad MPQs v 4, HET and BET tables */ 65 | /* 07.01.11 8.01 Lad Write support for MPQs v 3 and 4 */ 66 | /* 15.09.11 8.04 Lad Bug fixes, testing for Diablo III MPQs */ 67 | /* 26.04.12 8.10 Lad Support for data map, added SFileGetArchiveBitmap */ 68 | /* 29.05.12 8.20 Lad C-only interface */ 69 | /* 14.01.13 8.21 Lad ADPCM and Huffmann (de)compression refactored */ 70 | /* 04.12.13 9.00 Lad Unit tests, bug fixes */ 71 | /*****************************************************************************/ 72 | 73 | #ifndef __STORMLIB_H__ 74 | #define __STORMLIB_H__ 75 | 76 | #ifdef _MSC_VER 77 | #pragma warning(disable:4668) // 'XXX' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' 78 | #pragma warning(disable:4820) // 'XXX' : '2' bytes padding added after data member 'XXX::yyy' 79 | #endif 80 | 81 | #include "StormPort.h" 82 | 83 | #ifdef __cplusplus 84 | extern "C" { 85 | #endif 86 | 87 | //----------------------------------------------------------------------------- 88 | // Use the apropriate library 89 | // 90 | // The library type is encoded in the library name as the following 91 | // StormLibXYZ.lib 92 | // 93 | // X - D for Debug version, R for Release version 94 | // Y - A for ANSI version, U for Unicode version 95 | // Z - S for static-linked CRT library, D for multithreaded DLL CRT library 96 | // 97 | 98 | #if defined(_MSC_VER) && !defined(__STORMLIB_SELF__) 99 | 100 | #ifdef _DEBUG // DEBUG VERSIONS 101 | #ifndef _UNICODE 102 | #ifdef _DLL 103 | #pragma comment(lib, "StormLibDAD.lib") // Debug Ansi CRT-DLL version 104 | #else 105 | #pragma comment(lib, "StormLibDAS.lib") // Debug Ansi CRT-LIB version 106 | #endif 107 | #else 108 | #ifdef _DLL 109 | #pragma comment(lib, "StormLibDUD.lib") // Debug Unicode CRT-DLL version 110 | #else 111 | #pragma comment(lib, "StormLibDUS.lib") // Debug Unicode CRT-LIB version 112 | #endif 113 | #endif 114 | #else // RELEASE VERSIONS 115 | #ifndef _UNICODE 116 | #ifdef _DLL 117 | #pragma comment(lib, "StormLibRAD.lib") // Release Ansi CRT-DLL version 118 | #else 119 | #pragma comment(lib, "StormLibRAS.lib") // Release Ansi CRT-LIB version 120 | #endif 121 | #else 122 | #ifdef _DLL 123 | #pragma comment(lib, "StormLibRUD.lib") // Release Unicode CRT-DLL version 124 | #else 125 | #pragma comment(lib, "StormLibRUS.lib") // Release Unicode CRT-LIB version 126 | #endif 127 | #endif 128 | #endif 129 | 130 | #endif 131 | 132 | //----------------------------------------------------------------------------- 133 | // Defines 134 | 135 | #define STORMLIB_VERSION 0x0900 // Current version of StormLib (9.0) 136 | #define STORMLIB_VERSION_STRING "9.00" // String version of StormLib version 137 | 138 | #define ID_MPQ 0x1A51504D // MPQ archive header ID ('MPQ\x1A') 139 | #define ID_MPQ_USERDATA 0x1B51504D // MPQ userdata entry ('MPQ\x1B') 140 | #define ID_MPK 0x1A4B504D // MPK archive header ID ('MPK\x1A') 141 | 142 | #define ERROR_AVI_FILE 10000 // Not a MPQ file, but an AVI file. 143 | #define ERROR_UNKNOWN_FILE_KEY 10001 // Returned by SFileReadFile when can't find file key 144 | #define ERROR_CHECKSUM_ERROR 10002 // Returned by SFileReadFile when sector CRC doesn't match 145 | #define ERROR_INTERNAL_FILE 10003 // The given operation is not allowed on internal file 146 | #define ERROR_BASE_FILE_MISSING 10004 // The file is present as incremental patch file, but base file is missing 147 | #define ERROR_MARKED_FOR_DELETE 10005 // The file was marked as "deleted" in the MPQ 148 | #define ERROR_FILE_INCOMPLETE 10006 // The required file part is missing 149 | #define ERROR_UNKNOWN_FILE_NAMES 10007 // A name of at least one file is unknown 150 | 151 | // Values for SFileCreateArchive 152 | #define HASH_TABLE_SIZE_MIN 0x00000004 // Verified: If there is 1 file, hash table size is 4 153 | #define HASH_TABLE_SIZE_DEFAULT 0x00001000 // Default hash table size for empty MPQs 154 | #define HASH_TABLE_SIZE_MAX 0x00080000 // Maximum acceptable hash table size 155 | 156 | #define HASH_ENTRY_DELETED 0xFFFFFFFE // Block index for deleted entry in the hash table 157 | #define HASH_ENTRY_FREE 0xFFFFFFFF // Block index for free entry in the hash table 158 | 159 | #define HET_ENTRY_DELETED 0x80 // NameHash1 value for a deleted entry 160 | #define HET_ENTRY_FREE 0x00 // NameHash1 value for free entry 161 | 162 | #define HASH_STATE_SIZE 0x60 // Size of LibTomCrypt's hash_state structure 163 | 164 | #define MPQ_PATCH_PREFIX_LEN 0x20 // Maximum length of the patch prefix 165 | 166 | // Values for SFileOpenArchive 167 | #define SFILE_OPEN_HARD_DISK_FILE 2 // Open the archive on HDD 168 | #define SFILE_OPEN_CDROM_FILE 3 // Open the archive only if it is on CDROM 169 | 170 | // Values for SFileOpenFile 171 | #define SFILE_OPEN_FROM_MPQ 0x00000000 // Open the file from the MPQ archive 172 | #define SFILE_OPEN_BASE_FILE 0xFFFFFFFD // Reserved for StormLib internal use 173 | #define SFILE_OPEN_ANY_LOCALE 0xFFFFFFFE // Reserved for StormLib internal use 174 | #define SFILE_OPEN_LOCAL_FILE 0xFFFFFFFF // Open a local file 175 | 176 | // Flags for TMPQArchive::dwFlags 177 | #define MPQ_FLAG_READ_ONLY 0x00000001 // If set, the MPQ has been open for read-only access 178 | #define MPQ_FLAG_CHANGED 0x00000002 // If set, the MPQ tables have been changed 179 | #define MPQ_FLAG_MALFORMED 0x00000004 // Malformed data structure detected (W3M map protectors) 180 | #define MPQ_FLAG_CHECK_SECTOR_CRC 0x00000008 // Checking sector CRC when reading files 181 | #define MPQ_FLAG_LISTFILE_INVALID 0x00000020 // If set, it means that the (listfile) has been invalidated 182 | #define MPQ_FLAG_ATTRIBUTES_INVALID 0x00000040 // If set, it means that the (attributes) has been invalidated 183 | #define MPQ_FLAG_SAVING_TABLES 0x00000080 // If set, we are saving MPQ internal files and MPQ tables 184 | 185 | // Values for TMPQArchive::dwSubType 186 | #define MPQ_SUBTYPE_MPQ 0x00000000 // The file is a MPQ file (Blizzard games) 187 | #define MPQ_SUBTYPE_SQP 0x00000001 // The file is a SQP file (War of the Immortals) 188 | #define MPQ_SUBTYPE_MPK 0x00000002 // The file is a MPK file (Longwu Online) 189 | 190 | // Return value for SFileGetFileSize and SFileSetFilePointer 191 | #define SFILE_INVALID_SIZE 0xFFFFFFFF 192 | #define SFILE_INVALID_POS 0xFFFFFFFF 193 | #define SFILE_INVALID_ATTRIBUTES 0xFFFFFFFF 194 | 195 | // Flags for SFileAddFile 196 | #define MPQ_FILE_IMPLODE 0x00000100 // Implode method (By PKWARE Data Compression Library) 197 | #define MPQ_FILE_COMPRESS 0x00000200 // Compress methods (By multiple methods) 198 | #define MPQ_FILE_ENCRYPTED 0x00010000 // Indicates whether file is encrypted 199 | #define MPQ_FILE_FIX_KEY 0x00020000 // File decryption key has to be fixed 200 | #define MPQ_FILE_PATCH_FILE 0x00100000 // The file is a patch file. Raw file data begin with TPatchInfo structure 201 | #define MPQ_FILE_SINGLE_UNIT 0x01000000 // File is stored as a single unit, rather than split into sectors (Thx, Quantam) 202 | #define MPQ_FILE_DELETE_MARKER 0x02000000 // File is a deletion marker. Used in MPQ patches, indicating that the file no longer exists. 203 | #define MPQ_FILE_SECTOR_CRC 0x04000000 // File has checksums for each sector. 204 | // Ignored if file is not compressed or imploded. 205 | 206 | #define MPQ_FILE_COMPRESS_MASK 0x0000FF00 // Mask for a file being compressed 207 | #define MPQ_FILE_EXISTS 0x80000000 // Set if file exists, reset when the file was deleted 208 | #define MPQ_FILE_REPLACEEXISTING 0x80000000 // Replace when the file exist (SFileAddFile) 209 | 210 | #define MPQ_FILE_VALID_FLAGS (MPQ_FILE_IMPLODE | \ 211 | MPQ_FILE_COMPRESS | \ 212 | MPQ_FILE_ENCRYPTED | \ 213 | MPQ_FILE_FIX_KEY | \ 214 | MPQ_FILE_PATCH_FILE | \ 215 | MPQ_FILE_SINGLE_UNIT | \ 216 | MPQ_FILE_DELETE_MARKER | \ 217 | MPQ_FILE_SECTOR_CRC | \ 218 | MPQ_FILE_EXISTS) 219 | 220 | // Compression types for multiple compressions 221 | #define MPQ_COMPRESSION_HUFFMANN 0x01 // Huffmann compression (used on WAVE files only) 222 | #define MPQ_COMPRESSION_ZLIB 0x02 // ZLIB compression 223 | #define MPQ_COMPRESSION_PKWARE 0x08 // PKWARE DCL compression 224 | #define MPQ_COMPRESSION_BZIP2 0x10 // BZIP2 compression (added in Warcraft III) 225 | #define MPQ_COMPRESSION_SPARSE 0x20 // Sparse compression (added in Starcraft 2) 226 | #define MPQ_COMPRESSION_ADPCM_MONO 0x40 // IMA ADPCM compression (mono) 227 | #define MPQ_COMPRESSION_ADPCM_STEREO 0x80 // IMA ADPCM compression (stereo) 228 | #define MPQ_COMPRESSION_LZMA 0x12 // LZMA compression. Added in Starcraft 2. This value is NOT a combination of flags. 229 | #define MPQ_COMPRESSION_NEXT_SAME 0xFFFFFFFF // Same compression 230 | 231 | // Constants for SFileAddWave 232 | #define MPQ_WAVE_QUALITY_HIGH 0 // Best quality, the worst compression 233 | #define MPQ_WAVE_QUALITY_MEDIUM 1 // Medium quality, medium compression 234 | #define MPQ_WAVE_QUALITY_LOW 2 // Low quality, the best compression 235 | 236 | // Signatures for HET and BET table 237 | #define HET_TABLE_SIGNATURE 0x1A544548 // 'HET\x1a' 238 | #define BET_TABLE_SIGNATURE 0x1A544542 // 'BET\x1a' 239 | 240 | // Decryption keys for MPQ tables 241 | #define MPQ_KEY_HASH_TABLE 0xC3AF3770 // Obtained by HashString("(hash table)", MPQ_HASH_FILE_KEY) 242 | #define MPQ_KEY_BLOCK_TABLE 0xEC83B3A3 // Obtained by HashString("(block table)", MPQ_HASH_FILE_KEY) 243 | 244 | #define LISTFILE_NAME "(listfile)" // Name of internal listfile 245 | #define SIGNATURE_NAME "(signature)" // Name of internal signature 246 | #define ATTRIBUTES_NAME "(attributes)" // Name of internal attributes file 247 | #define PATCH_METADATA_NAME "(patch_metadata)" 248 | 249 | #define MPQ_FORMAT_VERSION_1 0 // Up to The Burning Crusade 250 | #define MPQ_FORMAT_VERSION_2 1 // The Burning Crusade and newer 251 | #define MPQ_FORMAT_VERSION_3 2 // WoW Cataclysm Beta 252 | #define MPQ_FORMAT_VERSION_4 3 // WoW Cataclysm and newer 253 | 254 | // Flags for MPQ attributes 255 | #define MPQ_ATTRIBUTE_CRC32 0x00000001 // The "(attributes)" contains CRC32 for each file 256 | #define MPQ_ATTRIBUTE_FILETIME 0x00000002 // The "(attributes)" contains file time for each file 257 | #define MPQ_ATTRIBUTE_MD5 0x00000004 // The "(attributes)" contains MD5 for each file 258 | #define MPQ_ATTRIBUTE_PATCH_BIT 0x00000008 // The "(attributes)" contains a patch bit for each file 259 | #define MPQ_ATTRIBUTE_ALL 0x0000000F // Summary mask 260 | 261 | #define MPQ_ATTRIBUTES_V1 100 // (attributes) format version 1.00 262 | 263 | // Flags for SFileOpenArchive 264 | #define BASE_PROVIDER_FILE 0x00000000 // Base data source is a file 265 | #define BASE_PROVIDER_MAP 0x00000001 // Base data source is memory-mapped file 266 | #define BASE_PROVIDER_HTTP 0x00000002 // Base data source is a file on web server 267 | #define BASE_PROVIDER_MASK 0x0000000F // Mask for base provider value 268 | 269 | #define STREAM_PROVIDER_FLAT 0x00000000 // Stream is linear with no offset mapping 270 | #define STREAM_PROVIDER_PARTIAL 0x00000010 // Stream is partial file (.part) 271 | #define STREAM_PROVIDER_MPQE 0x00000020 // Stream is an encrypted MPQ 272 | #define STREAM_PROVIDER_BLOCK4 0x00000030 // 0x4000 per block, text MD5 after each block, max 0x2000 blocks per file 273 | #define STREAM_PROVIDER_MASK 0x000000F0 // Mask for stream provider value 274 | 275 | #define STREAM_FLAG_READ_ONLY 0x00000100 // Stream is read only 276 | #define STREAM_FLAG_WRITE_SHARE 0x00000200 // Allow write sharing when open for write 277 | #define STREAM_FLAG_USE_BITMAP 0x00000400 // If the file has a file bitmap, load it and use it 278 | #define STREAM_OPTIONS_MASK 0x0000FF00 // Mask for stream options 279 | 280 | #define STREAM_PROVIDERS_MASK 0x000000FF // Mask to get stream providers 281 | #define STREAM_FLAGS_MASK 0x0000FFFF // Mask for all stream flags (providers+options) 282 | 283 | #define MPQ_OPEN_NO_LISTFILE 0x00010000 // Don't load the internal listfile 284 | #define MPQ_OPEN_NO_ATTRIBUTES 0x00020000 // Don't open the attributes 285 | #define MPQ_OPEN_NO_HEADER_SEARCH 0x00040000 // Don't search for the MPQ header past the begin of the file 286 | #define MPQ_OPEN_FORCE_MPQ_V1 0x00080000 // Always open the archive as MPQ v 1.00, ignore the "wFormatVersion" variable in the header 287 | #define MPQ_OPEN_CHECK_SECTOR_CRC 0x00100000 // On files with MPQ_FILE_SECTOR_CRC, the CRC will be checked when reading file 288 | #define MPQ_OPEN_READ_ONLY STREAM_FLAG_READ_ONLY 289 | 290 | // Flags for SFileCreateArchive 291 | #define MPQ_CREATE_LISTFILE 0x00100000 // Also add the (listfile) file 292 | #define MPQ_CREATE_ATTRIBUTES 0x00200000 // Also add the (attributes) file 293 | #define MPQ_CREATE_ARCHIVE_V1 0x00000000 // Creates archive of version 1 (size up to 4GB) 294 | #define MPQ_CREATE_ARCHIVE_V2 0x01000000 // Creates archive of version 2 (larger than 4 GB) 295 | #define MPQ_CREATE_ARCHIVE_V3 0x02000000 // Creates archive of version 3 296 | #define MPQ_CREATE_ARCHIVE_V4 0x03000000 // Creates archive of version 4 297 | #define MPQ_CREATE_ARCHIVE_VMASK 0x0F000000 // Mask for archive version 298 | 299 | #define FLAGS_TO_FORMAT_SHIFT 24 // (MPQ_CREATE_ARCHIVE_V4 >> FLAGS_TO_FORMAT_SHIFT) => MPQ_FORMAT_VERSION_4 300 | 301 | // Flags for SFileVerifyFile 302 | #define SFILE_VERIFY_SECTOR_CRC 0x00000001 // Verify sector checksum for the file, if available 303 | #define SFILE_VERIFY_FILE_CRC 0x00000002 // Verify file CRC, if available 304 | #define SFILE_VERIFY_FILE_MD5 0x00000004 // Verify file MD5, if available 305 | #define SFILE_VERIFY_RAW_MD5 0x00000008 // Verify raw file MD5, if available 306 | #define SFILE_VERIFY_ALL 0x0000000F // Verify every checksum possible 307 | 308 | // Return values for SFileVerifyFile 309 | #define VERIFY_OPEN_ERROR 0x0001 // Failed to open the file 310 | #define VERIFY_READ_ERROR 0x0002 // Failed to read all data from the file 311 | #define VERIFY_FILE_HAS_SECTOR_CRC 0x0004 // File has sector CRC 312 | #define VERIFY_FILE_SECTOR_CRC_ERROR 0x0008 // Sector CRC check failed 313 | #define VERIFY_FILE_HAS_CHECKSUM 0x0010 // File has CRC32 314 | #define VERIFY_FILE_CHECKSUM_ERROR 0x0020 // CRC32 check failed 315 | #define VERIFY_FILE_HAS_MD5 0x0040 // File has data MD5 316 | #define VERIFY_FILE_MD5_ERROR 0x0080 // MD5 check failed 317 | #define VERIFY_FILE_HAS_RAW_MD5 0x0100 // File has raw data MD5 318 | #define VERIFY_FILE_RAW_MD5_ERROR 0x0200 // Raw MD5 check failed 319 | #define VERIFY_FILE_ERROR_MASK (VERIFY_OPEN_ERROR | VERIFY_READ_ERROR | VERIFY_FILE_SECTOR_CRC_ERROR | VERIFY_FILE_CHECKSUM_ERROR | VERIFY_FILE_MD5_ERROR | VERIFY_FILE_RAW_MD5_ERROR) 320 | 321 | // Flags for SFileVerifyRawData (for MPQs version 4.0 or higher) 322 | #define SFILE_VERIFY_MPQ_HEADER 0x0001 // Verify raw MPQ header 323 | #define SFILE_VERIFY_HET_TABLE 0x0002 // Verify raw data of the HET table 324 | #define SFILE_VERIFY_BET_TABLE 0x0003 // Verify raw data of the BET table 325 | #define SFILE_VERIFY_HASH_TABLE 0x0004 // Verify raw data of the hash table 326 | #define SFILE_VERIFY_BLOCK_TABLE 0x0005 // Verify raw data of the block table 327 | #define SFILE_VERIFY_HIBLOCK_TABLE 0x0006 // Verify raw data of the hi-block table 328 | #define SFILE_VERIFY_FILE 0x0007 // Verify raw data of a file 329 | 330 | // Signature types 331 | #define SIGNATURE_TYPE_NONE 0x0000 // The archive has no signature in it 332 | #define SIGNATURE_TYPE_WEAK 0x0001 // The archive has weak signature 333 | #define SIGNATURE_TYPE_STRONG 0x0002 // The archive has strong signature 334 | 335 | // Return values for SFileVerifyArchive 336 | #define ERROR_NO_SIGNATURE 0 // There is no signature in the MPQ 337 | #define ERROR_VERIFY_FAILED 1 // There was an error during verifying signature (like no memory) 338 | #define ERROR_WEAK_SIGNATURE_OK 2 // There is a weak signature and sign check passed 339 | #define ERROR_WEAK_SIGNATURE_ERROR 3 // There is a weak signature but sign check failed 340 | #define ERROR_STRONG_SIGNATURE_OK 4 // There is a strong signature and sign check passed 341 | #define ERROR_STRONG_SIGNATURE_ERROR 5 // There is a strong signature but sign check failed 342 | 343 | #ifndef MD5_DIGEST_SIZE 344 | #define MD5_DIGEST_SIZE 0x10 345 | #endif 346 | 347 | #ifndef SHA1_DIGEST_SIZE 348 | #define SHA1_DIGEST_SIZE 0x14 // 160 bits 349 | #endif 350 | 351 | #ifndef LANG_NEUTRAL 352 | #define LANG_NEUTRAL 0x00 // Neutral locale 353 | #endif 354 | 355 | // Pointer to hashing function 356 | typedef DWORD (*HASH_STRING)(const char * szFileName, DWORD dwHashType); 357 | 358 | //----------------------------------------------------------------------------- 359 | // File information classes for SFileGetFileInfo and SFileFreeFileInfo 360 | 361 | typedef enum _SFileInfoClass 362 | { 363 | // Info classes for archives 364 | SFileMpqFileName, // Name of the archive file (TCHAR []) 365 | SFileMpqStreamBitmap, // Array of bits, each bit means availability of one block (BYTE []) 366 | SFileMpqUserDataOffset, // Offset of the user data header (ULONGLONG) 367 | SFileMpqUserDataHeader, // Raw (unfixed) user data header (TMPQUserData) 368 | SFileMpqUserData, // MPQ USer data, without the header (BYTE []) 369 | SFileMpqHeaderOffset, // Offset of the MPQ header (ULONGLONG) 370 | SFileMpqHeaderSize, // Fixed size of the MPQ header 371 | SFileMpqHeader, // Raw (unfixed) archive header (TMPQHeader) 372 | SFileMpqHetTableOffset, // Offset of the HET table, relative to MPQ header (ULONGLONG) 373 | SFileMpqHetTableSize, // Compressed size of the HET table (ULONGLONG) 374 | SFileMpqHetHeader, // HET table header (TMPQHetHeader) 375 | SFileMpqHetTable, // HET table as pointer. Must be freed using SFileFreeFileInfo 376 | SFileMpqBetTableOffset, // Offset of the BET table, relative to MPQ header (ULONGLONG) 377 | SFileMpqBetTableSize, // Compressed size of the BET table (ULONGLONG) 378 | SFileMpqBetHeader, // BET table header, followed by the flags (TMPQBetHeader + DWORD[]) 379 | SFileMpqBetTable, // BET table as pointer. Must be freed using SFileFreeFileInfo 380 | SFileMpqHashTableOffset, // Hash table offset, relative to MPQ header (ULONGLONG) 381 | SFileMpqHashTableSize64, // Compressed size of the hash table (ULONGLONG) 382 | SFileMpqHashTableSize, // Size of the hash table, in entries (DWORD) 383 | SFileMpqHashTable, // Raw (unfixed) hash table (TMPQBlock []) 384 | SFileMpqBlockTableOffset, // Block table offset, relative to MPQ header (ULONGLONG) 385 | SFileMpqBlockTableSize64, // Compressed size of the block table (ULONGLONG) 386 | SFileMpqBlockTableSize, // Size of the block table, in entries (DWORD) 387 | SFileMpqBlockTable, // Raw (unfixed) block table (TMPQBlock []) 388 | SFileMpqHiBlockTableOffset, // Hi-block table offset, relative to MPQ header (ULONGLONG) 389 | SFileMpqHiBlockTableSize64, // Compressed size of the hi-block table (ULONGLONG) 390 | SFileMpqHiBlockTable, // The hi-block table (USHORT []) 391 | SFileMpqSignatures, // Signatures present in the MPQ (DWORD) 392 | SFileMpqStrongSignatureOffset, // Byte offset of the strong signature, relative to begin of the file (ULONGLONG) 393 | SFileMpqStrongSignatureSize, // Size of the strong signature (DWORD) 394 | SFileMpqStrongSignature, // The strong signature (BYTE []) 395 | SFileMpqArchiveSize64, // Archive size from the header (ULONGLONG) 396 | SFileMpqArchiveSize, // Archive size from the header (DWORD) 397 | SFileMpqMaxFileCount, // Max number of files in the archive (DWORD) 398 | SFileMpqFileTableSize, // Number of entries in the file table (DWORD) 399 | SFileMpqSectorSize, // Sector size (DWORD) 400 | SFileMpqNumberOfFiles, // Number of files (DWORD) 401 | SFileMpqRawChunkSize, // Size of the raw data chunk for MD5 402 | SFileMpqStreamFlags, // Stream flags (DWORD) 403 | SFileMpqIsReadOnly, // Nonzero if the MPQ is read only (DWORD) 404 | 405 | // Info classes for files 406 | SFileInfoPatchChain, // Chain of patches where the file is (TCHAR []) 407 | SFileInfoFileEntry, // The file entry for the file (TFileEntry) 408 | SFileInfoHashEntry, // Hash table entry for the file (TMPQHash) 409 | SFileInfoHashIndex, // Index of the hash table entry (DWORD) 410 | SFileInfoNameHash1, // The first name hash in the hash table (DWORD) 411 | SFileInfoNameHash2, // The second name hash in the hash table (DWORD) 412 | SFileInfoNameHash3, // 64-bit file name hash for the HET/BET tables (ULONGLONG) 413 | SFileInfoLocale, // File locale (DWORD) 414 | SFileInfoFileIndex, // Block index (DWORD) 415 | SFileInfoByteOffset, // File position in the archive (ULONGLONG) 416 | SFileInfoFileTime, // File time (ULONGLONG) 417 | SFileInfoFileSize, // Size of the file (DWORD) 418 | SFileInfoCompressedSize, // Compressed file size (DWORD) 419 | SFileInfoFlags, // File flags from (DWORD) 420 | SFileInfoEncryptionKey, // File encryption key 421 | SFileInfoEncryptionKeyRaw, // Unfixed value of the file key 422 | } SFileInfoClass; 423 | 424 | //----------------------------------------------------------------------------- 425 | // Deprecated flags. These are going to be removed in next releases. 426 | 427 | /* 428 | 429 | STORMLIB_DEPRECATED_FLAG(DWORD, STREAM_PROVIDER_LINEAR, STREAM_PROVIDER_FLAT); 430 | STORMLIB_DEPRECATED_FLAG(DWORD, STREAM_PROVIDER_ENCRYPTED, STREAM_PROVIDER_MPQE); 431 | STORMLIB_DEPRECATED_FLAG(DWORD, MPQ_OPEN_ENCRYPTED, STREAM_PROVIDER_MPQE); 432 | STORMLIB_DEPRECATED_FLAG(DWORD, MPQ_OPEN_PARTIAL, STREAM_PROVIDER_PARTIAL); 433 | 434 | // MPQ_FILE_COMPRESSED is deprecated. Do not use. 435 | STORMLIB_DEPRECATED_FLAG(DWORD, MPQ_FILE_COMPRESSED, MPQ_FILE_COMPRESS_MASK); 436 | 437 | // Legacy values for file info classes. Included for backward compatibility, do not use. 438 | STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_ARCHIVE_NAME, SFileMpqFileName); 439 | STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_ARCHIVE_SIZE, SFileMpqArchiveSize); 440 | STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_MAX_FILE_COUNT, SFileMpqMaxFileCount); 441 | STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_HASH_TABLE_SIZE, SFileMpqHashTableSize); 442 | STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_BLOCK_TABLE_SIZE, SFileMpqBlockTableSize); 443 | STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_SECTOR_SIZE, SFileMpqSectorSize); 444 | STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_HASH_TABLE, SFileMpqHashTable); 445 | STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_BLOCK_TABLE, SFileMpqBlockTable); 446 | STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_NUM_FILES, SFileMpqNumberOfFiles); 447 | STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_STREAM_FLAGS, SFileMpqStreamFlags); 448 | STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_IS_READ_ONLY, SFileMpqIsReadOnly); 449 | STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_HASH_INDEX, SFileInfoHashIndex); 450 | STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_CODENAME1, SFileInfoNameHash1); 451 | STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_CODENAME2, SFileInfoNameHash2); 452 | STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_LOCALEID, SFileInfoLocale); 453 | STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_BLOCKINDEX, SFileInfoFileIndex); 454 | STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_FILE_SIZE, SFileInfoFileSize); 455 | STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_COMPRESSED_SIZE, SFileInfoCompressedSize); 456 | STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_FLAGS, SFileInfoFlags); 457 | STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_POSITION, SFileInfoByteOffset); 458 | STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_KEY, SFileInfoEncryptionKey); 459 | STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_KEY_UNFIXED, SFileInfoEncryptionKeyRaw); 460 | STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_FILETIME, SFileInfoFileTime); 461 | STORMLIB_DEPRECATED_FLAG(SFileInfoClass, SFILE_INFO_PATCH_CHAIN, SFileInfoPatchChain); 462 | */ 463 | 464 | //----------------------------------------------------------------------------- 465 | // Callback functions 466 | 467 | // Values for compact callback 468 | #define CCB_CHECKING_FILES 1 // Checking archive (dwParam1 = current, dwParam2 = total) 469 | #define CCB_CHECKING_HASH_TABLE 2 // Checking hash table (dwParam1 = current, dwParam2 = total) 470 | #define CCB_COPYING_NON_MPQ_DATA 3 // Copying non-MPQ data: No params used 471 | #define CCB_COMPACTING_FILES 4 // Compacting archive (dwParam1 = current, dwParam2 = total) 472 | #define CCB_CLOSING_ARCHIVE 5 // Closing archive: No params used 473 | 474 | typedef void (WINAPI * SFILE_DOWNLOAD_CALLBACK)(void * pvUserData, ULONGLONG ByteOffset, DWORD dwTotalBytes); 475 | typedef void (WINAPI * SFILE_ADDFILE_CALLBACK)(void * pvUserData, DWORD dwBytesWritten, DWORD dwTotalBytes, bool bFinalCall); 476 | typedef void (WINAPI * SFILE_COMPACT_CALLBACK)(void * pvUserData, DWORD dwWorkType, ULONGLONG BytesProcessed, ULONGLONG TotalBytes); 477 | 478 | typedef struct TFileStream TFileStream; 479 | 480 | //----------------------------------------------------------------------------- 481 | // Structure for bit arrays used for HET and BET tables 482 | 483 | typedef struct _TBitArray 484 | { 485 | DWORD NumberOfBytes; // Total number of bytes in "Elements" 486 | DWORD NumberOfBits; // Total number of bits that are available 487 | BYTE Elements[1]; // Array of elements (variable length) 488 | } TBitArray; 489 | 490 | void GetBits(TBitArray * array, unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, int nResultSize); 491 | void SetBits(TBitArray * array, unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, int nResultSize); 492 | 493 | //----------------------------------------------------------------------------- 494 | // Structures related to MPQ format 495 | // 496 | // Note: All structures in this header file are supposed to remain private 497 | // to StormLib. The structures may (and will) change over time, as the MPQ 498 | // file format evolves. Programmers directly using these structures need to 499 | // be aware of this. And the last, but not least, NEVER do any modifications 500 | // to those structures directly, always use SFile* functions. 501 | // 502 | 503 | #define MPQ_HEADER_SIZE_V1 0x20 504 | #define MPQ_HEADER_SIZE_V2 0x2C 505 | #define MPQ_HEADER_SIZE_V3 0x44 506 | #define MPQ_HEADER_SIZE_V4 0xD0 507 | #define MPQ_HEADER_DWORDS (MPQ_HEADER_SIZE_V4 / 0x04) 508 | 509 | typedef struct _TMPQUserData 510 | { 511 | // The ID_MPQ_USERDATA ('MPQ\x1B') signature 512 | DWORD dwID; 513 | 514 | // Maximum size of the user data 515 | DWORD cbUserDataSize; 516 | 517 | // Offset of the MPQ header, relative to the begin of this header 518 | DWORD dwHeaderOffs; 519 | 520 | // Appears to be size of user data header (Starcraft II maps) 521 | DWORD cbUserDataHeader; 522 | } TMPQUserData; 523 | 524 | // MPQ file header 525 | // 526 | // We have to make sure that the header is packed OK. 527 | // Reason: A 64-bit integer at the beginning of 3.0 part, 528 | // which is offset 0x2C 529 | #pragma pack(push, 1) 530 | typedef struct _TMPQHeader 531 | { 532 | // The ID_MPQ ('MPQ\x1A') signature 533 | DWORD dwID; 534 | 535 | // Size of the archive header 536 | DWORD dwHeaderSize; 537 | 538 | // 32-bit size of MPQ archive 539 | // This field is deprecated in the Burning Crusade MoPaQ format, and the size of the archive 540 | // is calculated as the size from the beginning of the archive to the end of the hash table, 541 | // block table, or hi-block table (whichever is largest). 542 | DWORD dwArchiveSize; 543 | 544 | // 0 = Format 1 (up to The Burning Crusade) 545 | // 1 = Format 2 (The Burning Crusade and newer) 546 | // 2 = Format 3 (WoW - Cataclysm beta or newer) 547 | // 3 = Format 4 (WoW - Cataclysm beta or newer) 548 | USHORT wFormatVersion; 549 | 550 | // Power of two exponent specifying the number of 512-byte disk sectors in each file sector 551 | // in the archive. The size of each file sector in the archive is 512 * 2 ^ wSectorSize. 552 | USHORT wSectorSize; 553 | 554 | // Offset to the beginning of the hash table, relative to the beginning of the archive. 555 | DWORD dwHashTablePos; 556 | 557 | // Offset to the beginning of the block table, relative to the beginning of the archive. 558 | DWORD dwBlockTablePos; 559 | 560 | // Number of entries in the hash table. Must be a power of two, and must be less than 2^16 for 561 | // the original MoPaQ format, or less than 2^20 for the Burning Crusade format. 562 | DWORD dwHashTableSize; 563 | 564 | // Number of entries in the block table 565 | DWORD dwBlockTableSize; 566 | 567 | //-- MPQ HEADER v 2 ------------------------------------------- 568 | 569 | // Offset to the beginning of array of 16-bit high parts of file offsets. 570 | ULONGLONG HiBlockTablePos64; 571 | 572 | // High 16 bits of the hash table offset for large archives. 573 | USHORT wHashTablePosHi; 574 | 575 | // High 16 bits of the block table offset for large archives. 576 | USHORT wBlockTablePosHi; 577 | 578 | //-- MPQ HEADER v 3 ------------------------------------------- 579 | 580 | // 64-bit version of the archive size 581 | ULONGLONG ArchiveSize64; 582 | 583 | // 64-bit position of the BET table 584 | ULONGLONG BetTablePos64; 585 | 586 | // 64-bit position of the HET table 587 | ULONGLONG HetTablePos64; 588 | 589 | //-- MPQ HEADER v 4 ------------------------------------------- 590 | 591 | // Compressed size of the hash table 592 | ULONGLONG HashTableSize64; 593 | 594 | // Compressed size of the block table 595 | ULONGLONG BlockTableSize64; 596 | 597 | // Compressed size of the hi-block table 598 | ULONGLONG HiBlockTableSize64; 599 | 600 | // Compressed size of the HET block 601 | ULONGLONG HetTableSize64; 602 | 603 | // Compressed size of the BET block 604 | ULONGLONG BetTableSize64; 605 | 606 | // Size of raw data chunk to calculate MD5. 607 | // MD5 of each data chunk follows the raw file data. 608 | DWORD dwRawChunkSize; 609 | 610 | // MD5 of MPQ tables 611 | unsigned char MD5_BlockTable[MD5_DIGEST_SIZE]; // MD5 of the block table before decryption 612 | unsigned char MD5_HashTable[MD5_DIGEST_SIZE]; // MD5 of the hash table before decryption 613 | unsigned char MD5_HiBlockTable[MD5_DIGEST_SIZE]; // MD5 of the hi-block table 614 | unsigned char MD5_BetTable[MD5_DIGEST_SIZE]; // MD5 of the BET table before decryption 615 | unsigned char MD5_HetTable[MD5_DIGEST_SIZE]; // MD5 of the HET table before decryption 616 | unsigned char MD5_MpqHeader[MD5_DIGEST_SIZE]; // MD5 of the MPQ header from signature to (including) MD5_HetTable 617 | } TMPQHeader; 618 | #pragma pack(pop) 619 | 620 | // Hash table entry. All files in the archive are searched by their hashes. 621 | typedef struct _TMPQHash 622 | { 623 | // The hash of the file path, using method A. 624 | DWORD dwName1; 625 | 626 | // The hash of the file path, using method B. 627 | DWORD dwName2; 628 | 629 | #ifdef PLATFORM_LITTLE_ENDIAN 630 | 631 | // The language of the file. This is a Windows LANGID data type, and uses the same values. 632 | // 0 indicates the default language (American English), or that the file is language-neutral. 633 | USHORT lcLocale; 634 | 635 | // The platform the file is used for. 0 indicates the default platform. 636 | // No other values have been observed. 637 | // Note: wPlatform is actually just BYTE, but since it has never been used, we don't care. 638 | USHORT wPlatform; 639 | 640 | #else 641 | 642 | USHORT wPlatform; 643 | USHORT lcLocale; 644 | 645 | #endif 646 | 647 | // If the hash table entry is valid, this is the index into the block table of the file. 648 | // Otherwise, one of the following two values: 649 | // - FFFFFFFFh: Hash table entry is empty, and has always been empty. 650 | // Terminates searches for a given file. 651 | // - FFFFFFFEh: Hash table entry is empty, but was valid at some point (a deleted file). 652 | // Does not terminate searches for a given file. 653 | DWORD dwBlockIndex; 654 | } TMPQHash; 655 | 656 | // File description block contains informations about the file 657 | typedef struct _TMPQBlock 658 | { 659 | // Offset of the beginning of the file, relative to the beginning of the archive. 660 | DWORD dwFilePos; 661 | 662 | // Compressed file size 663 | DWORD dwCSize; 664 | 665 | // Only valid if the block is a file; otherwise meaningless, and should be 0. 666 | // If the file is compressed, this is the size of the uncompressed file data. 667 | DWORD dwFSize; 668 | 669 | // Flags for the file. See MPQ_FILE_XXXX constants 670 | DWORD dwFlags; 671 | } TMPQBlock; 672 | 673 | // Patch file information, preceding the sector offset table 674 | typedef struct _TPatchInfo 675 | { 676 | DWORD dwLength; // Length of patch info header, in bytes 677 | DWORD dwFlags; // Flags. 0x80000000 = MD5 (?) 678 | DWORD dwDataSize; // Uncompressed size of the patch file 679 | BYTE md5[0x10]; // MD5 of the entire patch file after decompression 680 | 681 | // Followed by the sector table (variable length) 682 | } TPatchInfo; 683 | 684 | // Header for PTCH files 685 | typedef struct _TPatchHeader 686 | { 687 | //-- PATCH header ----------------------------------- 688 | DWORD dwSignature; // 'PTCH' 689 | DWORD dwSizeOfPatchData; // Size of the entire patch (decompressed) 690 | DWORD dwSizeBeforePatch; // Size of the file before patch 691 | DWORD dwSizeAfterPatch; // Size of file after patch 692 | 693 | //-- MD5 block -------------------------------------- 694 | DWORD dwMD5; // 'MD5_' 695 | DWORD dwMd5BlockSize; // Size of the MD5 block, including the signature and size itself 696 | BYTE md5_before_patch[0x10]; // MD5 of the original (unpached) file 697 | BYTE md5_after_patch[0x10]; // MD5 of the patched file 698 | 699 | //-- XFRM block ------------------------------------- 700 | DWORD dwXFRM; // 'XFRM' 701 | DWORD dwXfrmBlockSize; // Size of the XFRM block, includes XFRM header and patch data 702 | DWORD dwPatchType; // Type of patch ('BSD0' or 'COPY') 703 | 704 | // Followed by the patch data 705 | } TPatchHeader; 706 | 707 | #define SIZE_OF_XFRM_HEADER 0x0C 708 | 709 | // This is the combined file entry for maintaining file list in the MPQ. 710 | // This structure is combined from block table, hi-block table, 711 | // (attributes) file and from (listfile). 712 | typedef struct _TFileEntry 713 | { 714 | ULONGLONG FileNameHash; // Jenkins hash of the file name. Only used when the MPQ has BET table. 715 | ULONGLONG ByteOffset; // Position of the file content in the MPQ, relative to the MPQ header 716 | ULONGLONG FileTime; // FileTime from the (attributes) file. 0 if not present. 717 | DWORD dwHashIndex; // Index to the hash table. Only used when the MPQ has classic hash table 718 | DWORD dwFileSize; // Decompressed size of the file 719 | DWORD dwCmpSize; // Compressed size of the file (i.e., size of the file data in the MPQ) 720 | DWORD dwFlags; // File flags (from block table) 721 | USHORT lcLocale; // Locale ID for the file 722 | USHORT wPlatform; // Platform ID for the file 723 | DWORD dwCrc32; // CRC32 from (attributes) file. 0 if not present. 724 | unsigned char md5[MD5_DIGEST_SIZE]; // File MD5 from the (attributes) file. 0 if not present. 725 | char * szFileName; // File name. NULL if not known. 726 | } TFileEntry; 727 | 728 | // Common header for HET and BET tables 729 | typedef struct _TMPQExtHeader 730 | { 731 | DWORD dwSignature; // 'HET\x1A' or 'BET\x1A' 732 | DWORD dwVersion; // Version. Seems to be always 1 733 | DWORD dwDataSize; // Size of the contained table 734 | 735 | // Followed by the table header 736 | // Followed by the table data 737 | 738 | } TMPQExtHeader; 739 | 740 | // Structure for HET table header 741 | typedef struct _TMPQHetHeader 742 | { 743 | TMPQExtHeader ExtHdr; 744 | 745 | DWORD dwTableSize; // Size of the entire HET table, including HET_TABLE_HEADER (in bytes) 746 | DWORD dwEntryCount; // Number of occupied entries in the HET table 747 | DWORD dwTotalCount; // Total number of entries in the HET table 748 | DWORD dwNameHashBitSize; // Size of the name hash entry (in bits) 749 | DWORD dwIndexSizeTotal; // Total size of file index (in bits) 750 | DWORD dwIndexSizeExtra; // Extra bits in the file index 751 | DWORD dwIndexSize; // Effective size of the file index (in bits) 752 | DWORD dwIndexTableSize; // Size of the block index subtable (in bytes) 753 | 754 | } TMPQHetHeader; 755 | 756 | // Structure for BET table header 757 | typedef struct _TMPQBetHeader 758 | { 759 | TMPQExtHeader ExtHdr; 760 | 761 | DWORD dwTableSize; // Size of the entire BET table, including the header (in bytes) 762 | DWORD dwEntryCount; // Number of entries in the BET table. Must match HET_TABLE_HEADER::dwEntryCount 763 | DWORD dwUnknown08; 764 | DWORD dwTableEntrySize; // Size of one table entry (in bits) 765 | DWORD dwBitIndex_FilePos; // Bit index of the file position (within the entry record) 766 | DWORD dwBitIndex_FileSize; // Bit index of the file size (within the entry record) 767 | DWORD dwBitIndex_CmpSize; // Bit index of the compressed size (within the entry record) 768 | DWORD dwBitIndex_FlagIndex; // Bit index of the flag index (within the entry record) 769 | DWORD dwBitIndex_Unknown; // Bit index of the ??? (within the entry record) 770 | DWORD dwBitCount_FilePos; // Bit size of file position (in the entry record) 771 | DWORD dwBitCount_FileSize; // Bit size of file size (in the entry record) 772 | DWORD dwBitCount_CmpSize; // Bit size of compressed file size (in the entry record) 773 | DWORD dwBitCount_FlagIndex; // Bit size of flags index (in the entry record) 774 | DWORD dwBitCount_Unknown; // Bit size of ??? (in the entry record) 775 | DWORD dwBitTotal_NameHash2; // Total bit size of the NameHash2 776 | DWORD dwBitExtra_NameHash2; // Extra bits in the NameHash2 777 | DWORD dwBitCount_NameHash2; // Effective size of NameHash2 (in bits) 778 | DWORD dwNameHashArraySize; // Size of NameHash2 table, in bytes 779 | DWORD dwFlagCount; // Number of flags in the following array 780 | 781 | } TMPQBetHeader; 782 | 783 | // Structure for parsed HET table 784 | typedef struct _TMPQHetTable 785 | { 786 | TBitArray * pBetIndexes; // Bit array of FileIndex values 787 | LPBYTE pNameHashes; // Array of NameHash1 values (NameHash1 = upper 8 bits of FileName hashe) 788 | ULONGLONG AndMask64; // AND mask used for calculating file name hash 789 | ULONGLONG OrMask64; // OR mask used for setting the highest bit of the file name hash 790 | 791 | DWORD dwEntryCount; // Number of occupied entries in the HET table 792 | DWORD dwTotalCount; // Number of entries in both NameHash and FileIndex table 793 | DWORD dwNameHashBitSize; // Size of the name hash entry (in bits) 794 | DWORD dwIndexSizeTotal; // Total size of one entry in pBetIndexes (in bits) 795 | DWORD dwIndexSizeExtra; // Extra bits in the entry in pBetIndexes 796 | DWORD dwIndexSize; // Effective size of one entry in pBetIndexes (in bits) 797 | } TMPQHetTable; 798 | 799 | // Structure for parsed BET table 800 | typedef struct _TMPQBetTable 801 | { 802 | TBitArray * pNameHashes; // Array of NameHash2 entries (lower 24 bits of FileName hash) 803 | TBitArray * pFileTable; // Bit-based file table 804 | LPDWORD pFileFlags; // Array of file flags 805 | 806 | DWORD dwTableEntrySize; // Size of one table entry, in bits 807 | DWORD dwBitIndex_FilePos; // Bit index of the file position in the table entry 808 | DWORD dwBitIndex_FileSize; // Bit index of the file size in the table entry 809 | DWORD dwBitIndex_CmpSize; // Bit index of the compressed size in the table entry 810 | DWORD dwBitIndex_FlagIndex; // Bit index of the flag index in the table entry 811 | DWORD dwBitIndex_Unknown; // Bit index of ??? in the table entry 812 | DWORD dwBitCount_FilePos; // Size of file offset (in bits) within table entry 813 | DWORD dwBitCount_FileSize; // Size of file size (in bits) within table entry 814 | DWORD dwBitCount_CmpSize; // Size of compressed file size (in bits) within table entry 815 | DWORD dwBitCount_FlagIndex; // Size of flag index (in bits) within table entry 816 | DWORD dwBitCount_Unknown; // Size of ??? (in bits) within table entry 817 | DWORD dwBitTotal_NameHash2; // Total size of the NameHash2 818 | DWORD dwBitExtra_NameHash2; // Extra bits in the NameHash2 819 | DWORD dwBitCount_NameHash2; // Effective size of the NameHash2 820 | DWORD dwEntryCount; // Number of entries 821 | DWORD dwFlagCount; // Number of fil flags in pFileFlags 822 | } TMPQBetTable; 823 | 824 | // Archive handle structure 825 | typedef struct _TMPQArchive 826 | { 827 | TFileStream * pStream; // Open stream for the MPQ 828 | 829 | ULONGLONG UserDataPos; // Position of user data (relative to the begin of the file) 830 | ULONGLONG MpqPos; // MPQ header offset (relative to the begin of the file) 831 | 832 | struct _TMPQArchive * haPatch; // Pointer to patch archive, if any 833 | struct _TMPQArchive * haBase; // Pointer to base ("previous version") archive, if any 834 | char szPatchPrefix[MPQ_PATCH_PREFIX_LEN]; // Prefix for file names in patch MPQs 835 | size_t cchPatchPrefix; // Length of the patch prefix, in characters 836 | 837 | TMPQUserData * pUserData; // MPQ user data (NULL if not present in the file) 838 | TMPQHeader * pHeader; // MPQ file header 839 | TMPQHash * pHashTable; // Hash table 840 | TMPQHetTable * pHetTable; // HET table 841 | TFileEntry * pFileTable; // File table 842 | HASH_STRING pfnHashString; // Hashing function that will convert the file name into hash 843 | 844 | TMPQUserData UserData; // MPQ user data. Valid only when ID_MPQ_USERDATA has been found 845 | DWORD HeaderData[MPQ_HEADER_DWORDS]; // Storage for MPQ header 846 | 847 | DWORD dwHETBlockSize; 848 | DWORD dwBETBlockSize; 849 | DWORD dwMaxFileCount; // Maximum number of files in the MPQ. Also total size of the file table. 850 | DWORD dwFileTableSize; // Current size of the file table, e.g. index of the entry past the last occupied one 851 | DWORD dwReservedFiles; // Number of entries reserved for internal MPQ files (listfile, attributes) 852 | DWORD dwSectorSize; // Default size of one file sector 853 | DWORD dwFileFlags1; // Flags for (listfile) 854 | DWORD dwFileFlags2; // Flags for (attributes) 855 | DWORD dwAttrFlags; // Flags for the (attributes) file, see MPQ_ATTRIBUTE_XXX 856 | DWORD dwFlags; // See MPQ_FLAG_XXXXX 857 | DWORD dwSubType; // See MPQ_SUBTYPE_XXX 858 | 859 | SFILE_ADDFILE_CALLBACK pfnAddFileCB; // Callback function for adding files 860 | void * pvAddFileUserData; // User data thats passed to the callback 861 | 862 | SFILE_COMPACT_CALLBACK pfnCompactCB; // Callback function for compacting the archive 863 | ULONGLONG CompactBytesProcessed; // Amount of bytes that have been processed during a particular compact call 864 | ULONGLONG CompactTotalBytes; // Total amount of bytes to be compacted 865 | void * pvCompactUserData; // User data thats passed to the callback 866 | } TMPQArchive; 867 | 868 | // File handle structure 869 | typedef struct _TMPQFile 870 | { 871 | TFileStream * pStream; // File stream. Only used on local files 872 | TMPQArchive * ha; // Archive handle 873 | TFileEntry * pFileEntry; // File entry for the file 874 | DWORD dwFileKey; // Decryption key 875 | DWORD dwFilePos; // Current file position 876 | ULONGLONG RawFilePos; // Offset in MPQ archive (relative to file begin) 877 | ULONGLONG MpqFilePos; // Offset in MPQ archive (relative to MPQ header) 878 | DWORD dwMagic; // 'FILE' 879 | 880 | struct _TMPQFile * hfPatch; // Pointer to opened patch file 881 | TPatchHeader * pPatchHeader; // Patch header. Only used if the file is a patch file 882 | LPBYTE pbFileData; // Loaded and patched file data. Only used if the file is a patch file 883 | DWORD cbFileData; // Size of loaded patched data 884 | 885 | TPatchInfo * pPatchInfo; // Patch info block, preceding the sector table 886 | DWORD * SectorOffsets; // Position of each file sector, relative to the begin of the file. Only for compressed files. 887 | DWORD * SectorChksums; // Array of sector checksums (either ADLER32 or MD5) values for each file sector 888 | DWORD dwCompression0; // Compression that will be used on the first file sector 889 | DWORD dwSectorCount; // Number of sectors in the file 890 | DWORD dwPatchedFileSize; // Size of patched file. Used when saving patch file to the MPQ 891 | DWORD dwDataSize; // Size of data in the file (on patch files, this differs from file size in block table entry) 892 | 893 | LPBYTE pbFileSector; // Last loaded file sector. For single unit files, entire file content 894 | DWORD dwSectorOffs; // File position of currently loaded file sector 895 | DWORD dwSectorSize; // Size of the file sector. For single unit files, this is equal to the file size 896 | 897 | unsigned char hctx[HASH_STATE_SIZE]; // Hash state for MD5. Used when saving file to MPQ 898 | DWORD dwCrc32; // CRC32 value, used when saving file to MPQ 899 | 900 | int nAddFileError; // Result of the "Add File" operations 901 | 902 | bool bLoadedSectorCRCs; // If true, we already tried to load sector CRCs 903 | bool bCheckSectorCRCs; // If true, then SFileReadFile will check sector CRCs when reading the file 904 | bool bIsWriteHandle; // If true, this handle has been created by SFileCreateFile 905 | } TMPQFile; 906 | 907 | // Structure for SFileFindFirstFile and SFileFindNextFile 908 | typedef struct _SFILE_FIND_DATA 909 | { 910 | char cFileName[MAX_PATH]; // Full name of the found file 911 | char * szPlainName; // Plain name of the found file 912 | DWORD dwHashIndex; // Hash table index for the file 913 | DWORD dwBlockIndex; // Block table index for the file 914 | DWORD dwFileSize; // File size in bytes 915 | DWORD dwFileFlags; // MPQ file flags 916 | DWORD dwCompSize; // Compressed file size 917 | DWORD dwFileTimeLo; // Low 32-bits of the file time (0 if not present) 918 | DWORD dwFileTimeHi; // High 32-bits of the file time (0 if not present) 919 | LCID lcLocale; // Locale version 920 | 921 | } SFILE_FIND_DATA, *PSFILE_FIND_DATA; 922 | 923 | typedef struct _SFILE_CREATE_MPQ 924 | { 925 | DWORD cbSize; // Size of this structure, in bytes 926 | DWORD dwMpqVersion; // Version of the MPQ to be created 927 | void *pvUserData; // Reserved, must be NULL 928 | DWORD cbUserData; // Reserved, must be 0 929 | DWORD dwStreamFlags; // Stream flags for creating the MPQ 930 | DWORD dwFileFlags1; // File flags for (listfile). 0 = default 931 | DWORD dwFileFlags2; // File flags for (attributes). 0 = default 932 | DWORD dwAttrFlags; // Flags for the (attributes) file. If 0, no attributes will be created 933 | DWORD dwSectorSize; // Sector size for compressed files 934 | DWORD dwRawChunkSize; // Size of raw data chunk 935 | DWORD dwMaxFileCount; // File limit for the MPQ 936 | 937 | } SFILE_CREATE_MPQ, *PSFILE_CREATE_MPQ; 938 | 939 | //----------------------------------------------------------------------------- 940 | // Stream support - functions 941 | 942 | // Structure used by FileStream_GetBitmap 943 | typedef struct _TStreamBitmap 944 | { 945 | ULONGLONG StreamSize; // Size of the stream, in bytes 946 | DWORD BitmapSize; // Size of the block map, in bytes 947 | DWORD BlockCount; // Number of blocks in the stream 948 | DWORD BlockSize; // Size of one block 949 | DWORD IsComplete; // Nonzero if the file is complete 950 | 951 | // Followed by the BYTE array, each bit means availability of one block 952 | 953 | } TStreamBitmap; 954 | 955 | // UNICODE versions of the file access functions 956 | TFileStream * FileStream_CreateFile(const TCHAR * szFileName, DWORD dwStreamFlags); 957 | TFileStream * FileStream_OpenFile(const TCHAR * szFileName, DWORD dwStreamFlags); 958 | const TCHAR * FileStream_GetFileName(TFileStream * pStream); 959 | size_t FileStream_Prefix(const TCHAR * szFileName, DWORD * pdwProvider); 960 | 961 | bool FileStream_SetCallback(TFileStream * pStream, SFILE_DOWNLOAD_CALLBACK pfnCallback, void * pvUserData); 962 | 963 | bool FileStream_GetBitmap(TFileStream * pStream, void * pvBitmap, DWORD cbBitmap, LPDWORD pcbLengthNeeded); 964 | bool FileStream_Read(TFileStream * pStream, ULONGLONG * pByteOffset, void * pvBuffer, DWORD dwBytesToRead); 965 | bool FileStream_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite); 966 | bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize); 967 | bool FileStream_GetSize(TFileStream * pStream, ULONGLONG * pFileSize); 968 | bool FileStream_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset); 969 | bool FileStream_GetTime(TFileStream * pStream, ULONGLONG * pFT); 970 | bool FileStream_GetFlags(TFileStream * pStream, LPDWORD pdwStreamFlags); 971 | bool FileStream_Replace(TFileStream * pStream, TFileStream * pNewStream); 972 | void FileStream_Close(TFileStream * pStream); 973 | 974 | //----------------------------------------------------------------------------- 975 | // Functions prototypes for Storm.dll 976 | 977 | // Typedefs for functions exported by Storm.dll 978 | typedef LCID (WINAPI * SFILESETLOCALE)(LCID); 979 | typedef bool (WINAPI * SFILEOPENARCHIVE)(const char *, DWORD, DWORD, HANDLE *); 980 | typedef bool (WINAPI * SFILECLOSEARCHIVE)(HANDLE); 981 | typedef bool (WINAPI * SFILEOPENFILEEX)(HANDLE, const char *, DWORD, HANDLE *); 982 | typedef bool (WINAPI * SFILECLOSEFILE)(HANDLE); 983 | typedef DWORD (WINAPI * SFILEGETFILESIZE)(HANDLE, LPDWORD); 984 | typedef DWORD (WINAPI * SFILESETFILEPOINTER)(HANDLE, LONG, LONG *, DWORD); 985 | typedef bool (WINAPI * SFILEREADFILE)(HANDLE, void *, DWORD, LPDWORD, LPOVERLAPPED); 986 | 987 | //----------------------------------------------------------------------------- 988 | // Functions for manipulation with StormLib global flags 989 | 990 | LCID WINAPI SFileGetLocale(); 991 | LCID WINAPI SFileSetLocale(LCID lcNewLocale); 992 | 993 | //----------------------------------------------------------------------------- 994 | // Functions for archive manipulation 995 | 996 | bool WINAPI SFileOpenArchive(const TCHAR * szMpqName, DWORD dwPriority, DWORD dwFlags, HANDLE * phMpq); 997 | bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwCreateFlags, DWORD dwMaxFileCount, HANDLE * phMpq); 998 | bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCreateInfo, HANDLE * phMpq); 999 | 1000 | bool WINAPI SFileSetDownloadCallback(HANDLE hMpq, SFILE_DOWNLOAD_CALLBACK DownloadCB, void * pvUserData); 1001 | bool WINAPI SFileFlushArchive(HANDLE hMpq); 1002 | bool WINAPI SFileCloseArchive(HANDLE hMpq); 1003 | 1004 | // Adds another listfile into MPQ. The currently added listfile(s) remain, 1005 | // so you can use this API to combining more listfiles. 1006 | // Note that this function is internally called by SFileFindFirstFile 1007 | int WINAPI SFileAddListFile(HANDLE hMpq, const char * szListFile); 1008 | 1009 | // Archive compacting 1010 | bool WINAPI SFileSetCompactCallback(HANDLE hMpq, SFILE_COMPACT_CALLBACK CompactCB, void * pvUserData); 1011 | bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool bReserved); 1012 | 1013 | // Changing the maximum file count 1014 | DWORD WINAPI SFileGetMaxFileCount(HANDLE hMpq); 1015 | bool WINAPI SFileSetMaxFileCount(HANDLE hMpq, DWORD dwMaxFileCount); 1016 | 1017 | // Changing (attributes) file 1018 | DWORD WINAPI SFileGetAttributes(HANDLE hMpq); 1019 | bool WINAPI SFileSetAttributes(HANDLE hMpq, DWORD dwFlags); 1020 | bool WINAPI SFileUpdateFileAttributes(HANDLE hMpq, const char * szFileName); 1021 | 1022 | //----------------------------------------------------------------------------- 1023 | // Functions for manipulation with patch archives 1024 | 1025 | bool WINAPI SFileOpenPatchArchive(HANDLE hMpq, const TCHAR * szPatchMpqName, const char * szPatchPathPrefix, DWORD dwFlags); 1026 | bool WINAPI SFileIsPatchedArchive(HANDLE hMpq); 1027 | 1028 | //----------------------------------------------------------------------------- 1029 | // Functions for file manipulation 1030 | 1031 | // Reading from MPQ file 1032 | bool WINAPI SFileHasFile(HANDLE hMpq, const char * szFileName); 1033 | bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope, HANDLE * phFile); 1034 | DWORD WINAPI SFileGetFileSize(HANDLE hFile, LPDWORD pdwFileSizeHigh); 1035 | DWORD WINAPI SFileSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod); 1036 | bool WINAPI SFileReadFile(HANDLE hFile, void * lpBuffer, DWORD dwToRead, LPDWORD pdwRead, LPOVERLAPPED lpOverlapped); 1037 | bool WINAPI SFileCloseFile(HANDLE hFile); 1038 | 1039 | // Retrieving info about a file in the archive 1040 | bool WINAPI SFileGetFileInfo(HANDLE hMpqOrFile, SFileInfoClass InfoClass, void * pvFileInfo, DWORD cbFileInfo, LPDWORD pcbLengthNeeded); 1041 | bool WINAPI SFileGetFileName(HANDLE hFile, char * szFileName); 1042 | bool WINAPI SFileFreeFileInfo(void * pvFileInfo, SFileInfoClass InfoClass); 1043 | 1044 | // High-level extract function 1045 | bool WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const TCHAR * szExtracted, DWORD dwSearchScope); 1046 | 1047 | //----------------------------------------------------------------------------- 1048 | // Functions for file and archive verification 1049 | 1050 | // Generates file CRC32 1051 | bool WINAPI SFileGetFileChecksums(HANDLE hMpq, const char * szFileName, LPDWORD pdwCrc32, char * pMD5); 1052 | 1053 | // Verifies file against its checksums stored in (attributes) attributes (depending on dwFlags). 1054 | // For dwFlags, use one or more of MPQ_ATTRIBUTE_MD5 1055 | DWORD WINAPI SFileVerifyFile(HANDLE hMpq, const char * szFileName, DWORD dwFlags); 1056 | 1057 | // Verifies raw data of the archive. Only works for MPQs version 4 or newer 1058 | int WINAPI SFileVerifyRawData(HANDLE hMpq, DWORD dwWhatToVerify, const char * szFileName); 1059 | 1060 | // Verifies the signature, if present 1061 | DWORD WINAPI SFileVerifyArchive(HANDLE hMpq); 1062 | 1063 | //----------------------------------------------------------------------------- 1064 | // Functions for file searching 1065 | 1066 | HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DATA * lpFindFileData, const char * szListFile); 1067 | bool WINAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData); 1068 | bool WINAPI SFileFindClose(HANDLE hFind); 1069 | 1070 | HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData); 1071 | bool WINAPI SListFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData); 1072 | bool WINAPI SListFileFindClose(HANDLE hFind); 1073 | 1074 | // Locale support 1075 | int WINAPI SFileEnumLocales(HANDLE hMpq, const char * szFileName, LCID * plcLocales, LPDWORD pdwMaxLocales, DWORD dwSearchScope); 1076 | 1077 | //----------------------------------------------------------------------------- 1078 | // Support for adding files to the MPQ 1079 | 1080 | bool WINAPI SFileCreateFile(HANDLE hMpq, const char * szArchivedName, ULONGLONG FileTime, DWORD dwFileSize, LCID lcLocale, DWORD dwFlags, HANDLE * phFile); 1081 | bool WINAPI SFileWriteFile(HANDLE hFile, const void * pvData, DWORD dwSize, DWORD dwCompression); 1082 | bool WINAPI SFileFinishFile(HANDLE hFile); 1083 | 1084 | bool WINAPI SFileAddFileEx(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwCompression, DWORD dwCompressionNext); 1085 | bool WINAPI SFileAddFile(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags); 1086 | bool WINAPI SFileAddWave(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwQuality); 1087 | bool WINAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope); 1088 | bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szOldFileName, const char * szNewFileName); 1089 | bool WINAPI SFileSetFileLocale(HANDLE hFile, LCID lcNewLocale); 1090 | bool WINAPI SFileSetDataCompression(DWORD DataCompression); 1091 | 1092 | bool WINAPI SFileSetAddFileCallback(HANDLE hMpq, SFILE_ADDFILE_CALLBACK AddFileCB, void * pvUserData); 1093 | 1094 | //----------------------------------------------------------------------------- 1095 | // Compression and decompression 1096 | 1097 | int WINAPI SCompImplode (void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); 1098 | int WINAPI SCompExplode (void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); 1099 | int WINAPI SCompCompress (void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, unsigned uCompressionMask, int nCmpType, int nCmpLevel); 1100 | int WINAPI SCompDecompress (void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); 1101 | int WINAPI SCompDecompress2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); 1102 | 1103 | //----------------------------------------------------------------------------- 1104 | // Non-Windows support for SetLastError/GetLastError 1105 | 1106 | #ifndef PLATFORM_WINDOWS 1107 | 1108 | void SetLastError(int err); 1109 | int GetLastError(); 1110 | 1111 | #endif 1112 | 1113 | //----------------------------------------------------------------------------- 1114 | // Functions from Storm.dll. They use slightly different names for keeping 1115 | // possibility to use them together with StormLib (StormXXX instead of SFileXXX) 1116 | 1117 | #ifdef __LINK_STORM_DLL__ 1118 | #define STORM_ALTERNATE_NAMES // Force storm_dll.h to use alternate fnc names 1119 | #include "..\storm_dll\storm_dll.h" 1120 | #endif // __LINK_STORM_DLL__ 1121 | 1122 | #ifdef __cplusplus 1123 | } // extern "C" 1124 | #endif 1125 | 1126 | #endif // __STORMLIB_H__ 1127 | --------------------------------------------------------------------------------