mirror of
https://github.com/SabreTools/SabreTools.Serialization.git
synced 2026-07-08 18:06:41 +00:00
CascLibSharp isn't used and can be removed
This commit is contained in:
@@ -1,239 +0,0 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using CascLibSharp.Native;
|
||||
|
||||
namespace CascLibSharp
|
||||
{
|
||||
internal sealed class CascApi
|
||||
{
|
||||
#region Imported method signature type definitions
|
||||
|
||||
[return: MarshalAs(UnmanagedType.I1)]
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
internal delegate bool FnCascOpenStorage(
|
||||
[MarshalAs(UnmanagedType.LPTStr)] string szDataPath,
|
||||
uint dwFlags,
|
||||
out CascStorageSafeHandle phStorage);
|
||||
|
||||
[return: MarshalAs(UnmanagedType.I1)]
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
internal delegate bool FnCascGetStorageInfo(
|
||||
CascStorageSafeHandle hStorage,
|
||||
CascStorageInfoClass infoClass,
|
||||
ref uint pvStorageInfo,
|
||||
IntPtr cbStorageInfo,
|
||||
ref uint pcbLengthNeeded);
|
||||
|
||||
[return: MarshalAs(UnmanagedType.I1)]
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
internal delegate bool FnCascCloseStorage(IntPtr hStorage);
|
||||
|
||||
[return: MarshalAs(UnmanagedType.I1)]
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
internal delegate bool FnCascOpenFileByIndexKey(
|
||||
CascStorageSafeHandle hStorage,
|
||||
ref QueryKey pIndexKey,
|
||||
uint dwFlags,
|
||||
out CascStorageFileSafeHandle phFile);
|
||||
|
||||
[return: MarshalAs(UnmanagedType.I1)]
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
internal delegate bool FnCascOpenFileByEncodingKey(
|
||||
CascStorageSafeHandle hStorage,
|
||||
ref QueryKey pEncodingKey,
|
||||
uint dwFlags,
|
||||
out CascStorageFileSafeHandle phFile);
|
||||
|
||||
[return: MarshalAs(UnmanagedType.I1)]
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
internal delegate bool FnCascOpenFile(
|
||||
CascStorageSafeHandle hStorage,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string szFileName,
|
||||
uint dwLocale,
|
||||
uint dwFlags,
|
||||
out CascStorageFileSafeHandle phFile);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
internal delegate uint FnCascGetFileSize(
|
||||
CascStorageFileSafeHandle hFile,
|
||||
out uint pdwFileSizeHigh);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
internal delegate uint FnCascSetFilePointer(
|
||||
CascStorageFileSafeHandle hFile,
|
||||
uint lFilePos,
|
||||
ref uint plFilePosHigh,
|
||||
SeekOrigin dwMoveMethod);
|
||||
|
||||
[return: MarshalAs(UnmanagedType.I1)]
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
internal delegate bool FnCascReadFile(
|
||||
CascStorageFileSafeHandle hFile,
|
||||
IntPtr lpBuffer,
|
||||
uint dwToRead,
|
||||
out uint dwRead);
|
||||
|
||||
[return: MarshalAs(UnmanagedType.I1)]
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
internal delegate bool FnCascCloseFile(IntPtr hFile);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
internal delegate CascFileEnumerationSafeHandle FnCascFindFirstFile(
|
||||
CascStorageSafeHandle hStorage,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string szMask,
|
||||
ref CascFindData pFindData,
|
||||
[MarshalAs(UnmanagedType.LPTStr)] string? szListFile);
|
||||
|
||||
[return: MarshalAs(UnmanagedType.I1)]
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
internal delegate bool FnCascFindNextFile(
|
||||
CascFileEnumerationSafeHandle hFind,
|
||||
ref CascFindData pFindData);
|
||||
|
||||
[return: MarshalAs(UnmanagedType.I1)]
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
internal delegate bool FnCascFindClose(IntPtr hFind);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Field and method defs
|
||||
|
||||
public readonly FnCascOpenStorage? CascOpenStorage;
|
||||
public readonly FnCascGetStorageInfo? CascGetStorageInfo;
|
||||
public readonly FnCascCloseStorage? CascCloseStorage;
|
||||
|
||||
public readonly FnCascOpenFileByIndexKey? CascOpenFileByIndexKey;
|
||||
public readonly FnCascOpenFileByEncodingKey? CascOpenFileByEncodingKey;
|
||||
public readonly FnCascOpenFile? CascOpenFile;
|
||||
|
||||
public readonly FnCascGetFileSize? CascGetFileSizeBase;
|
||||
public long CascGetFileSize(CascStorageFileSafeHandle hFile)
|
||||
{
|
||||
uint low = CascGetFileSizeBase!(hFile, out uint high);
|
||||
long result = (high << 32) | low;
|
||||
|
||||
return result;
|
||||
}
|
||||
public readonly FnCascSetFilePointer? CascSetFilePointerBase;
|
||||
public long CascSetFilePointer(CascStorageFileSafeHandle hFile, long filePos, SeekOrigin moveMethod)
|
||||
{
|
||||
uint low, high;
|
||||
unchecked
|
||||
{
|
||||
low = (uint)(filePos & 0xffffffff);
|
||||
high = (uint)(((ulong)filePos & 0xffffffff00000000) >> 32);
|
||||
}
|
||||
|
||||
low = CascSetFilePointerBase!(hFile, low, ref high, moveMethod);
|
||||
|
||||
long result = (high << 32) | low;
|
||||
|
||||
return result;
|
||||
}
|
||||
public readonly FnCascReadFile? CascReadFile;
|
||||
public readonly FnCascCloseFile? CascCloseFile;
|
||||
|
||||
public readonly FnCascFindFirstFile? CascFindFirstFile;
|
||||
public readonly FnCascFindNextFile? CascFindNextFile;
|
||||
public readonly FnCascFindClose? CascFindClose;
|
||||
|
||||
#endregion
|
||||
|
||||
internal CascApi(IntPtr hModule)
|
||||
{
|
||||
SetFn(ref CascOpenStorage, hModule, "CascOpenStorage");
|
||||
SetFn(ref CascGetStorageInfo, hModule, "CascGetStorageInfo");
|
||||
SetFn(ref CascCloseStorage, hModule, "CascCloseStorage");
|
||||
|
||||
SetFn(ref CascOpenFileByIndexKey, hModule, "CascOpenFileByIndexKey");
|
||||
SetFn(ref CascOpenFileByEncodingKey, hModule, "CascOpenFileByEncodingKey");
|
||||
SetFn(ref CascOpenFile, hModule, "CascOpenFile");
|
||||
SetFn(ref CascGetFileSizeBase, hModule, "CascGetFileSize");
|
||||
SetFn(ref CascSetFilePointerBase, hModule, "CascSetFilePointer");
|
||||
SetFn(ref CascReadFile, hModule, "CascReadFile");
|
||||
SetFn(ref CascCloseFile, hModule, "CascCloseFile");
|
||||
|
||||
SetFn(ref CascFindFirstFile, hModule, "CascFindFirstFile");
|
||||
SetFn(ref CascFindNextFile, hModule, "CascFindNextFile");
|
||||
SetFn(ref CascFindClose, hModule, "CascFindClose");
|
||||
}
|
||||
|
||||
private static void SetFn<T>(ref T? target, IntPtr hModule, string procName)
|
||||
where T : class
|
||||
{
|
||||
IntPtr procAddr = NativeMethods.GetProcAddress(hModule, procName);
|
||||
if (procAddr == IntPtr.Zero)
|
||||
throw new Win32Exception();
|
||||
|
||||
target = Marshal.GetDelegateForFunctionPointer(procAddr, typeof(T)) as T;
|
||||
}
|
||||
|
||||
private static CascApi Load()
|
||||
{
|
||||
string myPath = Assembly.GetExecutingAssembly().Location;
|
||||
string directory = Path.GetDirectoryName(myPath) ?? string.Empty;
|
||||
string arch = IntPtr.Size == 8 ? "x64" : "x86";
|
||||
#if DEBUG
|
||||
string build = "dbg";
|
||||
#else
|
||||
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);
|
||||
|
||||
string localPath = Path.Combine(directory, "CascLib.dll");
|
||||
if (File.Exists(localPath))
|
||||
return FromFile(localPath);
|
||||
|
||||
throw new FileNotFoundException(string.Format("Could not locate a copy of CascLib.dll to load. The following paths were tried:\n\t{0}\n\t{1}\n\t{2}\n\nEnsure that an architecture-appropriate copy of CascLib.dll is included in your project.", mainPath, alternatePath, localPath));
|
||||
}
|
||||
|
||||
#if NET20 || NET35
|
||||
private static CascApi? _sharedInstance = null;
|
||||
#else
|
||||
private static readonly Lazy<CascApi> _sharedInstance = new(Load);
|
||||
#endif
|
||||
public static CascApi Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NET20 || NET35
|
||||
if (_sharedInstance == null)
|
||||
_sharedInstance = Load();
|
||||
|
||||
return _sharedInstance;
|
||||
#else
|
||||
return _sharedInstance.Value;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public static CascApi FromFile(string filePath)
|
||||
{
|
||||
if (!File.Exists(filePath))
|
||||
throw new FileNotFoundException("The CascLib.dll library could not be loaded at the specified path.", filePath);
|
||||
|
||||
IntPtr hMod = NativeMethods.LoadLibraryEx(filePath, IntPtr.Zero, 0);
|
||||
if (hMod == IntPtr.Zero)
|
||||
throw new Win32Exception();
|
||||
|
||||
return new CascApi(hMod);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace CascLibSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an exception raised as part of a CASC operation.
|
||||
/// </summary>
|
||||
public class CascException : Win32Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new CascException based on the last Win32 error.
|
||||
/// </summary>
|
||||
public CascException()
|
||||
: base(Marshal.GetLastWin32Error())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new CascException based on the specified Win32 error code.
|
||||
/// </summary>
|
||||
/// <param name="errorCode">A Win32 error code.</param>
|
||||
public CascException(int errorCode)
|
||||
: base(errorCode)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new CascException based on the specified message.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message.</param>
|
||||
public CascException(string message)
|
||||
: base(message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new CascException based on the specified Win32 error code and custom error message.
|
||||
/// </summary>
|
||||
/// <param name="errorCode">A Win32 error code.</param>
|
||||
/// <param name="message">The error message.</param>
|
||||
public CascException(int errorCode, string message)
|
||||
: base(errorCode, message)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using CascLibSharp.Native;
|
||||
|
||||
namespace CascLibSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a view of the data in a file contained within CASC storage.
|
||||
/// </summary>
|
||||
public class CascFileStream : Stream
|
||||
{
|
||||
private readonly CascStorageFileSafeHandle _handle;
|
||||
private readonly CascApi _api;
|
||||
|
||||
internal CascFileStream(CascStorageFileSafeHandle? handle, CascApi? api)
|
||||
{
|
||||
Debug.Assert(handle != null);
|
||||
Debug.Assert(!handle!.IsInvalid);
|
||||
Debug.Assert(api != null);
|
||||
|
||||
_api = api!;
|
||||
_handle = handle;
|
||||
}
|
||||
|
||||
private void AssertValidHandle()
|
||||
{
|
||||
if (_handle == null || _handle.IsInvalid)
|
||||
throw new ObjectDisposedException("CascFileStream");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether this Stream may be read. Always returns true as long as the Stream has not been disposed.
|
||||
/// </summary>
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return _handle != null && !_handle.IsInvalid; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether this Stream may seek. Always returns true as long as the Stream has not been disposed.
|
||||
/// </summary>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return _handle != null && !_handle.IsInvalid; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether this Stream may write. Always returns false.
|
||||
/// </summary>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes writes to the backing store. This method always throws because writing is not supported.
|
||||
/// </summary>
|
||||
/// <exception cref="NotSupportedException">Always thrown.</exception>
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the Stream.
|
||||
/// </summary>
|
||||
/// <exception cref="ObjectDisposedException">Thrown if the Stream has been disposed.</exception>
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
AssertValidHandle();
|
||||
return _api!.CascGetFileSize(_handle);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current position within the Stream.
|
||||
/// </summary>
|
||||
/// <exception cref="ObjectDisposedException">Thrown if the Stream has been disposed.</exception>
|
||||
/// <exception cref="CascException">Thrown if the attempt to seek fails.</exception>
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
AssertValidHandle();
|
||||
return _api!.CascSetFilePointer(_handle, 0, SeekOrigin.Current);
|
||||
}
|
||||
set
|
||||
{
|
||||
AssertValidHandle();
|
||||
long result = _api!.CascSetFilePointer(_handle, value, SeekOrigin.Begin);
|
||||
if (result != value)
|
||||
throw new CascException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads data into the buffer.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The destination into which the data should be read.</param>
|
||||
/// <param name="offset">The offset into the buffer to read from the current position of the Stream.</param>
|
||||
/// <param name="count">The number of bytes to read.</param>
|
||||
/// <returns>The number of bytes actually read.</returns>
|
||||
/// <exception cref="ObjectDisposedException">Thrown if the Stream has been disposed.</exception>
|
||||
/// <exception cref="CascException">Thrown if the CASC provider fails to read.</exception>
|
||||
public override unsafe int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
AssertValidHandle();
|
||||
long len = Length;
|
||||
if (offset < 0 || offset > len)
|
||||
throw new ArgumentException(nameof(offset));
|
||||
if (count < 0)
|
||||
throw new ArgumentException(nameof(count));
|
||||
if (offset + count > len)
|
||||
throw new ArgumentException(nameof(count));
|
||||
|
||||
uint read = 0;
|
||||
fixed (byte* pBuffer = &buffer[0])
|
||||
{
|
||||
if (!_api.CascReadFile!(_handle, new IntPtr((void*)pBuffer), unchecked((uint)count), out read))
|
||||
throw new CascException();
|
||||
}
|
||||
|
||||
return unchecked((int)read);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seeks to a specific position within the Stream.
|
||||
/// </summary>
|
||||
/// <param name="offset">The offset from the specified origin to seek.</param>
|
||||
/// <param name="origin">The relative location (beginning, current, or end) from which to seek.</param>
|
||||
/// <returns>The new position in the stream.</returns>
|
||||
/// <exception cref="ObjectDisposedException">Thrown if the Stream has been disposed.</exception>
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
AssertValidHandle();
|
||||
return _api.CascSetFilePointer(_handle, offset, origin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the length of the Stream. Always throws because writing is not supported.
|
||||
/// </summary>
|
||||
/// <param name="value">The new length of the Stream.</param>
|
||||
/// <exception cref="NotSupportedException">Always thrown.</exception>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the specified data to the Stream. Always throws because writing is not supported.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The data to write.</param>
|
||||
/// <param name="offset">The starting position in the buffer.</param>
|
||||
/// <param name="count">The number of bytes.</param>
|
||||
/// <exception cref="NotSupportedException">Always thrown.</exception>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace CascLibSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a file found in a CASC container search.
|
||||
/// </summary>
|
||||
public class CascFoundFile
|
||||
{
|
||||
#if NET20 || NET35 || NET40
|
||||
private readonly WeakReference _ownerContext;
|
||||
#else
|
||||
private readonly WeakReference<CascStorageContext> _ownerContext;
|
||||
#endif
|
||||
|
||||
internal CascFoundFile(string? fileName, IntPtr plainName, byte[] encodingKey, CascLocales locales, long fileSize, CascStorageContext ownerContext)
|
||||
{
|
||||
FileName = fileName;
|
||||
PlainFileName = Marshal.PtrToStringAnsi(plainName);
|
||||
EncodingKey = encodingKey;
|
||||
Locales = locales;
|
||||
FileSize = fileSize;
|
||||
|
||||
#if NET20 || NET35 || NET40
|
||||
_ownerContext = new WeakReference(ownerContext);
|
||||
#else
|
||||
_ownerContext = new WeakReference<CascStorageContext>(ownerContext);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full path to this file.
|
||||
/// </summary>
|
||||
public string? FileName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plain (no directory-qualified) file name of this file.
|
||||
/// </summary>
|
||||
public string? PlainFileName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the CASC encoding key for this file.
|
||||
/// </summary>
|
||||
public byte[] EncodingKey { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the locales supported by this resource.
|
||||
/// </summary>
|
||||
public CascLocales Locales { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the file in bytes.
|
||||
/// </summary>
|
||||
public long FileSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Opens the found file for reading.
|
||||
/// </summary>
|
||||
/// <returns>A CascFileStream, which acts as a Stream for a CASC stored file.</returns>
|
||||
public CascFileStream Open()
|
||||
{
|
||||
#if NET20 || NET35 || NET40
|
||||
CascStorageContext? context = _ownerContext.Target as CascStorageContext;
|
||||
if (context == null)
|
||||
throw new ObjectDisposedException("The owning context has been closed.");
|
||||
#else
|
||||
if (!_ownerContext.TryGetTarget(out CascStorageContext? context) || context == null)
|
||||
throw new ObjectDisposedException("The owning context has been closed.");
|
||||
#endif
|
||||
|
||||
return context.OpenFileByEncodingKey(EncodingKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,453 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using CascLibSharp.Native;
|
||||
|
||||
namespace CascLibSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a CASC storage directory.
|
||||
/// </summary>
|
||||
public class CascStorageContext : IDisposable
|
||||
{
|
||||
private readonly 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 readonly Lazy<bool> _hasListfile;
|
||||
private readonly Lazy<CascKnownClient> _clientType;
|
||||
private readonly Lazy<long> _fileCount;
|
||||
private readonly Lazy<int> _gameBuild;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new CascStorageContext for the specified path.
|
||||
/// </summary>
|
||||
/// <param name="dataPath">The path to a game's data directory.</param>
|
||||
/// <example>An example directory is <c>c:\Program Files (x86)\Heroes of the Storm\HeroesData</c>.</example>
|
||||
public CascStorageContext(string dataPath)
|
||||
{
|
||||
_api = CascApi.Instance;
|
||||
|
||||
if (!_api.CascOpenStorage!(dataPath, 0, out _handle) || _handle.IsInvalid)
|
||||
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>
|
||||
/// Opens a file by its fully-qualified name.
|
||||
/// </summary>
|
||||
/// <param name="fileName">The name of the file.</param>
|
||||
/// <param name="locale">The file's locale (defaults to English-United States).</param>
|
||||
/// <returns>A CascFileStream, which implements a Stream.</returns>
|
||||
/// <exception cref="ObjectDisposedException">Thrown if the CascStorageContext has been disposed.</exception>
|
||||
/// <exception cref="System.IO.FileNotFoundException">Thrown if the file does not exist within the CASC storage container.</exception>
|
||||
public CascFileStream OpenFile(string fileName, CascLocales locale = CascLocales.EnUs)
|
||||
{
|
||||
if (_handle == null || _handle.IsInvalid)
|
||||
throw new ObjectDisposedException("CascStorageContext");
|
||||
|
||||
CascStorageFileSafeHandle hFile;
|
||||
if (!_api.CascOpenFile!(_handle, fileName, (uint)locale, 0, out hFile))
|
||||
throw new CascException();
|
||||
|
||||
hFile.Api = _api;
|
||||
|
||||
return new CascFileStream(hFile, _api);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a file by its index key.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// An index key is a binary representation of a file. I do not know what it comes from; I know that it's used to identify files inside of CASC,
|
||||
/// but I don't know how someone obtains the index key. They are not produced in the public API of CascLib.
|
||||
/// </remarks>
|
||||
/// <param name="indexKey">The index key to search.</param>
|
||||
/// <returns>A CascFileStream, which implements a Stream.</returns>
|
||||
/// <exception cref="ObjectDisposedException">Thrown if the CascStorageContext has been disposed.</exception>
|
||||
/// <exception cref="System.IO.FileNotFoundException">Thrown if the file does not exist within the CASC storage container.</exception>
|
||||
public CascFileStream OpenFileByIndexKey(byte[] indexKey)
|
||||
{
|
||||
if (_handle == null || _handle.IsInvalid)
|
||||
throw new ObjectDisposedException("CascStorageContext");
|
||||
|
||||
using CoTaskMem mem = CoTaskMem.FromBytes(indexKey);
|
||||
|
||||
var qk = new QueryKey();
|
||||
qk.cbData = unchecked((uint)indexKey.Length);
|
||||
qk.pbData = mem.Pointer;
|
||||
|
||||
CascStorageFileSafeHandle hFile;
|
||||
if (!_api.CascOpenFileByIndexKey!(_handle, ref qk, 0, out hFile))
|
||||
throw new CascException();
|
||||
|
||||
hFile.Api = _api;
|
||||
|
||||
return new CascFileStream(hFile, _api);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a file by its encoding key.
|
||||
/// </summary>
|
||||
/// <param name="encodingKey">A 16-byte key representing the file. Encoding keys may be obtained via <see cref="SearchFiles(string, string)"/>.</param>
|
||||
/// <returns>A CascFileStream, which implements a Stream.</returns>
|
||||
/// <exception cref="ObjectDisposedException">Thrown if the CascStorageContext has been disposed.</exception>
|
||||
/// <exception cref="System.IO.FileNotFoundException">Thrown if the file does not exist within the CASC storage container.</exception>
|
||||
public CascFileStream OpenFileByEncodingKey(byte[] encodingKey)
|
||||
{
|
||||
if (_handle == null || _handle.IsInvalid)
|
||||
throw new ObjectDisposedException("CascStorageContext");
|
||||
|
||||
using CoTaskMem mem = CoTaskMem.FromBytes(encodingKey);
|
||||
|
||||
var qk = new QueryKey();
|
||||
qk.cbData = unchecked((uint)encodingKey.Length);
|
||||
qk.pbData = mem.Pointer;
|
||||
|
||||
CascStorageFileSafeHandle hFile;
|
||||
if (!_api.CascOpenFileByEncodingKey!(_handle, ref qk, 0, out hFile))
|
||||
throw new CascException();
|
||||
|
||||
hFile.Api = _api;
|
||||
|
||||
return new CascFileStream(hFile, _api);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches the files in the CASC container for files that match the specified pattern.
|
||||
/// </summary>
|
||||
/// <param name="mask">The mask to search. * and ? are valid tokens for substitution.</param>
|
||||
/// <param name="listFilePath">A path to a listfile. Required if the CASC container is for World of Warcraft.</param>
|
||||
/// <returns>An enumeration of matching file references in the CASC container.</returns>
|
||||
public IEnumerable<CascFoundFile> SearchFiles(string mask, string? listFilePath = null)
|
||||
{
|
||||
if (_handle == null || _handle.IsInvalid)
|
||||
throw new ObjectDisposedException("CascStorageContext");
|
||||
|
||||
#if NET20 || NET35
|
||||
if (GameClient == CascKnownClient.WorldOfWarcraft && string.IsNullOrEmpty(listFilePath))
|
||||
#else
|
||||
if (GameClient == CascKnownClient.WorldOfWarcraft && string.IsNullOrWhiteSpace(listFilePath))
|
||||
#endif
|
||||
throw new ArgumentNullException("listFilePath");
|
||||
|
||||
var cfd = new CascFindData();
|
||||
using var handle = _api.CascFindFirstFile!(_handle, mask, ref cfd, listFilePath);
|
||||
if (handle.IsInvalid)
|
||||
yield break;
|
||||
|
||||
handle.Api = _api;
|
||||
|
||||
yield return cfd.ToFoundFile(this);
|
||||
|
||||
while (_api.CascFindNextFile!(handle, ref cfd))
|
||||
{
|
||||
yield return cfd.ToFoundFile(this);
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckHasListfile()
|
||||
{
|
||||
if (_handle == null || _handle.IsInvalid)
|
||||
throw new ObjectDisposedException("CascStorageContext");
|
||||
|
||||
uint storageInfo = 0, lengthNeeded = 4;
|
||||
if (!_api.CascGetStorageInfo!(_handle, CascStorageInfoClass.Features, ref storageInfo, new IntPtr(4), ref lengthNeeded))
|
||||
throw new CascException();
|
||||
|
||||
CascStorageFeatures features = (CascStorageFeatures)storageInfo;
|
||||
#if NET20 || NET35
|
||||
if ((features & CascStorageFeatures.HasListfile) != 0)
|
||||
#else
|
||||
if (features.HasFlag(CascStorageFeatures.HasListfile))
|
||||
#endif
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private CascKnownClient GetClient()
|
||||
{
|
||||
if (_handle == null || _handle.IsInvalid)
|
||||
throw new ObjectDisposedException("CascStorageContext");
|
||||
|
||||
uint storageInfo = 0, lengthNeeded = 4;
|
||||
if (!_api.CascGetStorageInfo!(_handle, CascStorageInfoClass.GameInfo, ref storageInfo, new IntPtr(4), ref lengthNeeded))
|
||||
throw new CascException();
|
||||
|
||||
CascGameId gameId = (CascGameId)storageInfo;
|
||||
|
||||
return gameId.ToKnownClient();
|
||||
}
|
||||
|
||||
private long GetFileCount()
|
||||
{
|
||||
if (_handle == null || _handle.IsInvalid)
|
||||
throw new ObjectDisposedException("CascStorageContext");
|
||||
|
||||
uint storageInfo = 0, lengthNeeded = 4;
|
||||
if (!_api.CascGetStorageInfo!(_handle, CascStorageInfoClass.Features, ref storageInfo, new IntPtr(4), ref lengthNeeded))
|
||||
throw new CascException();
|
||||
|
||||
return storageInfo;
|
||||
}
|
||||
|
||||
private int GetGameBuild()
|
||||
{
|
||||
if (_handle == null || _handle.IsInvalid)
|
||||
throw new ObjectDisposedException("CascStorageContext");
|
||||
|
||||
uint storageInfo = 0, lengthNeeded = 4;
|
||||
if (!_api.CascGetStorageInfo!(_handle, CascStorageInfoClass.Features, ref storageInfo, new IntPtr(4), ref lengthNeeded))
|
||||
throw new CascException();
|
||||
|
||||
return unchecked((int)storageInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the CASC container has a listfile or if one must be supplied while searching.
|
||||
/// </summary>
|
||||
public bool HasListfile
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NET20 || NET35
|
||||
if (_hasListfile == null)
|
||||
_hasListfile = CheckHasListfile();
|
||||
#endif
|
||||
return _hasListfile.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of files in the CASC container.
|
||||
/// </summary>
|
||||
public long FileCount
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NET20 || NET35
|
||||
if (_fileCount == null)
|
||||
_fileCount = GetFileCount();
|
||||
#endif
|
||||
return _fileCount.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the build number of the game.
|
||||
/// </summary>
|
||||
public int GameBuild
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NET20 || NET35
|
||||
if (_gameBuild == null)
|
||||
_gameBuild = GetGameBuild();
|
||||
#endif
|
||||
return _gameBuild.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the game client of the container, if it can be determined.
|
||||
/// </summary>
|
||||
public CascKnownClient GameClient
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NET20 || NET35
|
||||
if (_clientType == null)
|
||||
_clientType = GetClient();
|
||||
#endif
|
||||
return _clientType.Value;
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable implementation
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes the storage context.
|
||||
/// </summary>
|
||||
~CascStorageContext()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the object.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the object, cleaning up the unmanaged objects.
|
||||
/// </summary>
|
||||
/// <param name="disposing">True if this is being called via the Dispose() method; false if it's being called by the finalizer.</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (_handle != null && !_handle.IsInvalid)
|
||||
{
|
||||
_handle.Close();
|
||||
_handle = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Known locales supported by CASC.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum CascLocales
|
||||
{
|
||||
/// <summary>
|
||||
/// All available locales.
|
||||
/// </summary>
|
||||
All = -1,
|
||||
|
||||
/// <summary>
|
||||
/// No locales.
|
||||
/// </summary>
|
||||
///
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// Unknown
|
||||
/// </summary>
|
||||
Unknown1 = 1,
|
||||
|
||||
/// <summary>
|
||||
/// English, United States
|
||||
/// </summary>
|
||||
EnUs = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Korean, South Korea
|
||||
/// </summary>
|
||||
KoKr = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved (unknown)
|
||||
/// </summary>
|
||||
Reserved = 8,
|
||||
|
||||
/// <summary>
|
||||
/// French, France
|
||||
/// </summary>
|
||||
FrFr = 0x10,
|
||||
|
||||
/// <summary>
|
||||
/// German, Germany
|
||||
/// </summary>
|
||||
DeDe = 0x20,
|
||||
|
||||
/// <summary>
|
||||
/// Chinese, China
|
||||
/// </summary>
|
||||
ZhCn = 0x40,
|
||||
|
||||
/// <summary>
|
||||
/// Spanish, Spain
|
||||
/// </summary>
|
||||
EsEs = 0x80,
|
||||
|
||||
/// <summary>
|
||||
/// Chinese, Taiwan
|
||||
/// </summary>
|
||||
ZhTw = 0x100,
|
||||
|
||||
/// <summary>
|
||||
/// English, Great Britain
|
||||
/// </summary>
|
||||
EnGb = 0x200,
|
||||
|
||||
/// <summary>
|
||||
/// English, China
|
||||
/// </summary>
|
||||
EnCn = 0x400,
|
||||
|
||||
/// <summary>
|
||||
/// English, Taiwan
|
||||
/// </summary>
|
||||
EnTw = 0x800,
|
||||
|
||||
/// <summary>
|
||||
/// Spanish, Mexico
|
||||
/// </summary>
|
||||
EsMx = 0x1000,
|
||||
|
||||
/// <summary>
|
||||
/// Russian, Russia
|
||||
/// </summary>
|
||||
RuRu = 0x2000,
|
||||
|
||||
/// <summary>
|
||||
/// Portuguese, Brazil
|
||||
/// </summary>
|
||||
PtBr = 0x4000,
|
||||
|
||||
/// <summary>
|
||||
/// Italian, Italy
|
||||
/// </summary>
|
||||
ItIt = 0x8000,
|
||||
|
||||
/// <summary>
|
||||
/// Portuguese, Portugal
|
||||
/// </summary>
|
||||
PtPt = 0x10000,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Known clients supporting CASC
|
||||
/// </summary>
|
||||
public enum CascKnownClient
|
||||
{
|
||||
/// <summary>
|
||||
/// The game client was unrecognized.
|
||||
/// </summary>
|
||||
Unknown = -1,
|
||||
|
||||
/// <summary>
|
||||
/// Heroes of the Storm
|
||||
/// </summary>
|
||||
HeroesOfTheStorm = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Diablo 3
|
||||
/// </summary>
|
||||
Diablo3 = 1,
|
||||
|
||||
/// <summary>
|
||||
/// World of Warcraft
|
||||
/// </summary>
|
||||
WorldOfWarcraft = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Overwatch
|
||||
/// </summary>
|
||||
Overwatch = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Starcraft 2
|
||||
/// </summary>
|
||||
Starcraft2 = 4,
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace CascLibSharp.Native
|
||||
{
|
||||
internal class CascFileEnumerationSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
|
||||
{
|
||||
public CascFileEnumerationSafeHandle()
|
||||
: base(true) { }
|
||||
|
||||
public CascFileEnumerationSafeHandle(IntPtr handle)
|
||||
: this()
|
||||
{
|
||||
SetHandle(handle);
|
||||
}
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
var api = Api ?? CascApi.Instance;
|
||||
return api.CascFindClose!(DangerousGetHandle());
|
||||
}
|
||||
|
||||
internal CascApi? Api
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace CascLibSharp.Native
|
||||
{
|
||||
internal class CascStorageFileSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
|
||||
{
|
||||
public CascStorageFileSafeHandle()
|
||||
: base(true)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public CascStorageFileSafeHandle(IntPtr handle)
|
||||
: this()
|
||||
{
|
||||
SetHandle(handle);
|
||||
}
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
var api = Api ?? CascApi.Instance;
|
||||
return api.CascCloseFile!(DangerousGetHandle());
|
||||
}
|
||||
|
||||
internal CascApi? Api
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace CascLibSharp.Native
|
||||
{
|
||||
internal class CascStorageSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
|
||||
{
|
||||
public CascStorageSafeHandle()
|
||||
: base(true)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public CascStorageSafeHandle(IntPtr handle)
|
||||
: this()
|
||||
{
|
||||
SetHandle(handle);
|
||||
}
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
var api = Api ?? CascApi.Instance;
|
||||
return api.CascCloseStorage!(DangerousGetHandle());
|
||||
}
|
||||
|
||||
internal CascApi? Api
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace CascLibSharp.Native
|
||||
{
|
||||
internal sealed class CoTaskMem : IDisposable
|
||||
{
|
||||
private IntPtr _mem;
|
||||
private int _size;
|
||||
|
||||
public CoTaskMem(int size)
|
||||
{
|
||||
_mem = Marshal.AllocCoTaskMem(size);
|
||||
if (_mem == IntPtr.Zero)
|
||||
throw new OutOfMemoryException();
|
||||
|
||||
_size = size;
|
||||
}
|
||||
|
||||
public static CoTaskMem FromBytes(byte[] b)
|
||||
{
|
||||
var result = new CoTaskMem(b.Length);
|
||||
Marshal.Copy(b, 0, result._mem, b.Length);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public IntPtr Pointer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_mem == IntPtr.Zero)
|
||||
throw new ObjectDisposedException("CoTaskMem");
|
||||
|
||||
return _mem;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (_mem != IntPtr.Zero)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(_mem);
|
||||
_mem = IntPtr.Zero;
|
||||
_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
~CoTaskMem()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace CascLibSharp.Native
|
||||
{
|
||||
internal static class NativeMethods
|
||||
{
|
||||
[DllImport("kernel32", SetLastError = true)]
|
||||
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
|
||||
|
||||
[DllImport("kernel32", SetLastError = true)]
|
||||
public static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags);
|
||||
}
|
||||
|
||||
internal enum CascStorageInfoClass
|
||||
{
|
||||
FileCount,
|
||||
Features,
|
||||
GameInfo,
|
||||
GameBuild,
|
||||
}
|
||||
|
||||
internal enum CascStorageFeatures
|
||||
{
|
||||
None = 0x0,
|
||||
HasListfile = 0x1,
|
||||
}
|
||||
|
||||
/*
|
||||
* #define CASC_GAME_HOTS 0x00010000 // Heroes of the Storm
|
||||
#define CASC_GAME_WOW6 0x00020000 // World of Warcraft - Warlords of Draenor
|
||||
#define CASC_GAME_DIABLO3 0x00030000 // Diablo 3 since PTR 2.2.0
|
||||
#define CASC_GAME_OVERWATCH 0x00040000 // Overwatch since PTR 24919*
|
||||
*/
|
||||
internal enum CascGameId
|
||||
{
|
||||
Hots = 0x00010000,
|
||||
Wow6 = 0x00020000,
|
||||
Diablo3 = 0x00030000,
|
||||
Overwatch = 0x00040000,
|
||||
Starcraft2 = 0x00050000,
|
||||
}
|
||||
|
||||
internal static class GameConverterExtensions
|
||||
{
|
||||
private static readonly Dictionary<CascGameId, CascKnownClient> GameClientMap = new()
|
||||
{
|
||||
{ CascGameId.Hots, CascKnownClient.HeroesOfTheStorm },
|
||||
{ CascGameId.Wow6, CascKnownClient.WorldOfWarcraft },
|
||||
{ CascGameId.Diablo3, CascKnownClient.Diablo3 },
|
||||
{ CascGameId.Overwatch, CascKnownClient.Overwatch },
|
||||
{ CascGameId.Starcraft2, CascKnownClient.Starcraft2 },
|
||||
};
|
||||
|
||||
private static readonly Dictionary<CascKnownClient, CascGameId> ClientGameMap = new()
|
||||
{
|
||||
{ CascKnownClient.HeroesOfTheStorm, CascGameId.Hots },
|
||||
{ CascKnownClient.WorldOfWarcraft, CascGameId.Wow6 },
|
||||
{ CascKnownClient.Diablo3, CascGameId.Diablo3 },
|
||||
{ CascKnownClient.Overwatch, CascGameId.Overwatch },
|
||||
{ CascKnownClient.Starcraft2, CascGameId.Starcraft2 },
|
||||
};
|
||||
|
||||
public static CascKnownClient ToKnownClient(this CascGameId gameId)
|
||||
{
|
||||
if (!GameClientMap.TryGetValue(gameId, out CascKnownClient result))
|
||||
result = CascKnownClient.Unknown;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static CascGameId ToGameId(this CascKnownClient knownClient)
|
||||
{
|
||||
if (!ClientGameMap.TryGetValue(knownClient, out CascGameId result))
|
||||
throw new ArgumentException("Invalid client.");
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
internal struct QueryKey
|
||||
{
|
||||
public IntPtr pbData;
|
||||
public uint cbData;
|
||||
}
|
||||
|
||||
internal unsafe struct CascFindData
|
||||
{
|
||||
const int MAX_PATH = 260;
|
||||
|
||||
public fixed byte szFileName[MAX_PATH];
|
||||
public IntPtr szPlainName;
|
||||
//public ulong FileNameHash;
|
||||
public fixed byte EncodingKey[16];
|
||||
//public uint dwPackageIndex;
|
||||
public uint dwLocaleFlags;
|
||||
public uint dwFileSize;
|
||||
|
||||
public unsafe CascFoundFile ToFoundFile(CascStorageContext ownerContext)
|
||||
{
|
||||
string? fileName = null;
|
||||
fixed (void* pFileName = szFileName)
|
||||
{
|
||||
fileName = Marshal.PtrToStringAnsi(new IntPtr(pFileName));
|
||||
}
|
||||
|
||||
byte[] encodingKey = new byte[16];
|
||||
fixed (void* pEncodingKey = EncodingKey)
|
||||
{
|
||||
Marshal.Copy(new IntPtr(pEncodingKey), encodingKey, 0, 16);
|
||||
}
|
||||
|
||||
return new CascFoundFile(fileName, szPlainName, encodingKey, (CascLocales)dwLocaleFlags, dwFileSize, ownerContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
# External Library Notes
|
||||
|
||||
This directory contains multiple external libraries. Here is the status of each:
|
||||
This directory contains external libraries.
|
||||
|
||||
| Directory | Library | Status |
|
||||
| --------- | ------- | ------ |
|
||||
| CascLibSharp | [stormlibsharp](https://github.com/robpaveza/stormlibsharp) | Direct code duplicate |
|
||||
| StormLibSharp | [stormlibsharp](https://github.com/robpaveza/stormlibsharp) | Direct code duplicate |
|
||||
|
||||
**CascLibSharp** and **StormLibSharp** have multiple changes made to accomodate modern .NET versions. The original code is MIT licensed.
|
||||
**StormLibSharp** has multiple changes made to accomodate modern .NET versions. The original code is MIT licensed.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user