├── 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 |
--------------------------------------------------------------------------------