Enable StormLib for all .NET versions

This commit is contained in:
Matt Nadareski
2025-11-02 22:07:40 -05:00
parent 4840d9df6e
commit 42239919e5
6 changed files with 54 additions and 76 deletions

View File

@@ -9,7 +9,7 @@ Find the link to the Nuget package [here](https://www.nuget.org/packages/SabreTo
The following non-project libraries (or ports thereof) are used for file handling:
- [GrindCore.SharpCompress](https://github.com/Nanook/GrindCore.SharpCompress) - Common archive format extraction
- [StormLibSharp](https://github.com/robpaveza/stormlibsharp) - MoPaQ extraction [Unused in .NET Framework 2.0/3.5/4.0 and non-Windows builds due to Windows-specific libraries]
- [StormLibSharp](https://github.com/robpaveza/stormlibsharp) - MoPaQ extraction [Unused in non-Windows builds due to Windows-specific libraries]
The following projects have influenced this library:

View File

@@ -41,14 +41,6 @@
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
</PropertyGroup>
<!-- Exclude all external modules for .NET Framework 2.0 and .NET Framework 3.5 -->
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`))">
<DefaultItemExcludes>
$(DefaultItemExcludes);
_EXTERNAL\**
</DefaultItemExcludes>
</PropertyGroup>
<!-- These are needed for dealing with native Windows DLLs -->
<ItemGroup>
<Content Include="runtimes\**\*">

View File

@@ -168,11 +168,19 @@ namespace CascLibSharp
string build = "fre";
#endif
#if NET20 || NET35
string mainPath = Path.Combine(directory, Path.Combine("CascLib", Path.Combine(build, Path.Combine(arch, "CascLib.dll"))));
#else
string mainPath = Path.Combine(directory, "CascLib", build, arch, "CascLib.dll");
#endif
if (File.Exists(mainPath))
return FromFile(mainPath);
#if NET20 || NET35
string alternatePath = Path.Combine(directory, Path.Combine("CascLib", Path.Combine(arch, "CascLib.dll")));
#else
string alternatePath = Path.Combine(directory, "CascLib", arch, "CascLib.dll");
#endif
if (File.Exists(mainPath))
return FromFile(alternatePath);
@@ -183,12 +191,23 @@ namespace CascLibSharp
throw new FileNotFoundException(string.Format("Could not locate a copy of CascLib.dll to load. The following paths were tried:\n\t{0}\n\t{1}\n\t{2}\n\nEnsure that an architecture-appropriate copy of CascLib.dll is included in your project.", mainPath, alternatePath, localPath));
}
#if NET20 || NET35
private static CascApi? _sharedInstance = null;
#else
private static Lazy<CascApi> _sharedInstance = new Lazy<CascApi>(Load);
#endif
public static CascApi Instance
{
get
{
#if NET20 || NET35
if (_sharedInstance == null)
_sharedInstance = Load();
return _sharedInstance;
#else
return _sharedInstance.Value;
#endif
}
}

View File

@@ -11,10 +11,17 @@ namespace CascLibSharp
{
private CascApi _api;
private CascStorageSafeHandle? _handle;
#if NET20 || NET35
private bool? _hasListfile = null;
private CascKnownClient? _clientType = null;
private long? _fileCount = null;
private int? _gameBuild = null;
#else
private Lazy<bool> _hasListfile;
private Lazy<CascKnownClient> _clientType;
private Lazy<long> _fileCount;
private Lazy<int> _gameBuild;
#endif
/// <summary>
/// Creates a new CascStorageContext for the specified path.
@@ -29,10 +36,12 @@ namespace CascLibSharp
throw new CascException();
_handle.Api = _api;
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD2_0_OR_GREATER
_hasListfile = new Lazy<bool>(CheckHasListfile);
_clientType = new Lazy<CascKnownClient>(GetClient);
_fileCount = new Lazy<long>(GetFileCount);
_gameBuild = new Lazy<int>(GetGameBuild);
#endif
}
/// <summary>
@@ -128,7 +137,11 @@ namespace CascLibSharp
if (_handle == null || _handle.IsInvalid)
throw new ObjectDisposedException("CascStorageContext");
#if NET20 || NET35
if (this.GameClient == CascKnownClient.WorldOfWarcraft && string.IsNullOrEmpty(listFilePath))
#else
if (this.GameClient == CascKnownClient.WorldOfWarcraft && string.IsNullOrWhiteSpace(listFilePath))
#endif
throw new ArgumentNullException("listFilePath");
CascFindData cfd = new CascFindData();
@@ -158,7 +171,11 @@ namespace CascLibSharp
throw new CascException();
CascStorageFeatures features = (CascStorageFeatures)storageInfo;
#if NET20 || NET35
if ((features & CascStorageFeatures.HasListfile) != 0)
#else
if (features.HasFlag(CascStorageFeatures.HasListfile))
#endif
return true;
return false;
@@ -209,6 +226,10 @@ namespace CascLibSharp
{
get
{
#if NET20 || NET35
if (_hasListfile == null)
_hasListfile = CheckHasListfile();
#endif
return _hasListfile.Value;
}
}
@@ -220,6 +241,10 @@ namespace CascLibSharp
{
get
{
#if NET20 || NET35
if (_fileCount == null)
_fileCount = GetFileCount();
#endif
return _fileCount.Value;
}
}
@@ -231,6 +256,10 @@ namespace CascLibSharp
{
get
{
#if NET20 || NET35
if (_gameBuild == null)
_gameBuild = GetGameBuild();
#endif
return _gameBuild.Value;
}
}
@@ -242,6 +271,10 @@ namespace CascLibSharp
{
get
{
#if NET20 || NET35
if (_clientType == null)
_clientType = GetClient();
#endif
return _clientType.Value;
}
}

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.IO.MemoryMappedFiles;
using StormLibSharp.Native;
namespace StormLibSharp
@@ -30,24 +29,6 @@ namespace StormLibSharp
throw new Win32Exception(); // Implicitly calls GetLastError
}
public MpqArchive(MemoryMappedFile file, FileAccess accessType)
{
_accessType = accessType;
string? fileName = Win32Methods.GetFileNameOfMemoryMappedFile(file);
if (fileName == null)
throw new ArgumentException("Could not retrieve the name of the file to initialize.");
SFileOpenArchiveFlags flags = SFileOpenArchiveFlags.TypeIsMemoryMapped;
if (accessType == FileAccess.Read)
flags |= SFileOpenArchiveFlags.AccessReadOnly;
else
flags |= SFileOpenArchiveFlags.AccessReadWriteShare;
// constant 2 = SFILE_OPEN_HARD_DISK_FILE
if (!NativeMethods.SFileOpenArchive(fileName, 2, flags, out _handle))
throw new Win32Exception(); // Implicitly calls GetLastError
}
private MpqArchive(string filePath, MpqArchiveVersion version, MpqFileStreamAttributes listfileAttributes, MpqFileStreamAttributes attributesFileAttributes, int maxFileCount)
{
if (maxFileCount < 0)

View File

@@ -1,57 +1,10 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices;
namespace StormLibSharp.Native
{
internal static class Win32Methods
{
[DllImport("kernel32", ExactSpelling = false, SetLastError = true)]
public static extern uint GetMappedFileName(
IntPtr hProcess,
IntPtr fileHandle,
IntPtr lpFilename,
uint nSize
);
[DllImport("kernel32", ExactSpelling = false, SetLastError = true)]
public static extern uint GetFinalPathNameByHandle(
IntPtr hFile,
IntPtr lpszFilePath,
uint cchFilePath,
uint dwFlags
);
[DllImport("kernel32", SetLastError = false, ExactSpelling = false)]
public static extern int GetLastError();
public static string? GetFileNameOfMemoryMappedFile(MemoryMappedFile file)
{
const uint size = 522;
IntPtr path = Marshal.AllocCoTaskMem(unchecked((int)size)); // MAX_PATH + 1 char
string? result;
try
{
// constant 0x2 = VOLUME_NAME_NT
uint test = GetFinalPathNameByHandle(file.SafeMemoryMappedFileHandle.DangerousGetHandle(), path, size, 0x2);
if (test != 0)
throw new Win32Exception();
result = Marshal.PtrToStringAuto(path);
}
catch
{
uint test = GetMappedFileName(Process.GetCurrentProcess().Handle, file.SafeMemoryMappedFileHandle.DangerousGetHandle(), path, size);
if (test != 0)
throw new Win32Exception();
result = Marshal.PtrToStringAuto(path);
}
return result;
}
}
}