wabbajack/SevenZipExtractor/SevenZipInterface.cs
Timothy Baldridge 2d061bc1e4 Initial commit
2019-07-20 22:40:54 -06:00

529 lines
16 KiB
C#

// Version 1.5
using System;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Threading;
namespace SevenZipExtractor
{
[StructLayout(LayoutKind.Sequential)]
internal struct PropArray
{
uint length;
IntPtr pointerValues;
}
[StructLayout(LayoutKind.Explicit)]
internal struct PropVariant
{
[DllImport("ole32.dll")]
private static extern int PropVariantClear(ref PropVariant pvar);
[FieldOffset(0)] public ushort vt;
[FieldOffset(8)] public IntPtr pointerValue;
[FieldOffset(8)] public byte byteValue;
[FieldOffset(8)] public long longValue;
[FieldOffset(8)] public System.Runtime.InteropServices.ComTypes.FILETIME filetime;
[FieldOffset(8)] public PropArray propArray;
public VarEnum VarType
{
get
{
return (VarEnum) this.vt;
}
}
public void Clear()
{
switch (this.VarType)
{
case VarEnum.VT_EMPTY:
break;
case VarEnum.VT_NULL:
case VarEnum.VT_I2:
case VarEnum.VT_I4:
case VarEnum.VT_R4:
case VarEnum.VT_R8:
case VarEnum.VT_CY:
case VarEnum.VT_DATE:
case VarEnum.VT_ERROR:
case VarEnum.VT_BOOL:
//case VarEnum.VT_DECIMAL:
case VarEnum.VT_I1:
case VarEnum.VT_UI1:
case VarEnum.VT_UI2:
case VarEnum.VT_UI4:
case VarEnum.VT_I8:
case VarEnum.VT_UI8:
case VarEnum.VT_INT:
case VarEnum.VT_UINT:
case VarEnum.VT_HRESULT:
case VarEnum.VT_FILETIME:
this.vt = 0;
break;
default:
PropVariantClear(ref this);
break;
}
}
public object GetObject()
{
switch (this.VarType)
{
case VarEnum.VT_EMPTY:
return null;
case VarEnum.VT_FILETIME:
return DateTime.FromFileTime(this.longValue);
default:
GCHandle PropHandle = GCHandle.Alloc(this, GCHandleType.Pinned);
try
{
return Marshal.GetObjectForNativeVariant(PropHandle.AddrOfPinnedObject());
}
finally
{
PropHandle.Free();
}
}
}
}
[ComImport]
[Guid("23170F69-40C1-278A-0000-000000050000")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IProgress
{
void SetTotal(ulong total);
void SetCompleted([In] ref ulong completeValue);
}
[ComImport]
[Guid("23170F69-40C1-278A-0000-000600100000")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IArchiveOpenCallback
{
// ref ulong replaced with IntPtr because handlers ofter pass null value
// read actual value with Marshal.ReadInt64
void SetTotal(
IntPtr files, // [In] ref ulong files, can use 'ulong* files' but it is unsafe
IntPtr bytes); // [In] ref ulong bytes
void SetCompleted(
IntPtr files, // [In] ref ulong files
IntPtr bytes); // [In] ref ulong bytes
}
[ComImport]
[Guid("23170F69-40C1-278A-0000-000500100000")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface ICryptoGetTextPassword
{
[PreserveSig]
int CryptoGetTextPassword(
[MarshalAs(UnmanagedType.BStr)] out string password);
//[return : MarshalAs(UnmanagedType.BStr)]
//string CryptoGetTextPassword();
}
internal enum AskMode : int
{
kExtract = 0,
kTest,
kSkip
}
internal enum OperationResult : int
{
kOK = 0,
kUnSupportedMethod,
kDataError,
kCRCError
}
[ComImport]
[Guid("23170F69-40C1-278A-0000-000600300000")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IArchiveOpenVolumeCallback
{
void GetProperty(
ItemPropId propID, // PROPID
IntPtr value); // PROPVARIANT
[PreserveSig]
int GetStream(
[MarshalAs(UnmanagedType.LPWStr)] string name,
[MarshalAs(UnmanagedType.Interface)] out IInStream inStream);
}
[ComImport]
[Guid("23170F69-40C1-278A-0000-000600400000")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IInArchiveGetStream
{
[return: MarshalAs(UnmanagedType.Interface)]
ISequentialInStream GetStream(uint index);
}
[ComImport]
[Guid("23170F69-40C1-278A-0000-000300010000")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface ISequentialInStream
{
//[PreserveSig]
//int Read(
// [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data,
// uint size,
// IntPtr processedSize); // ref uint processedSize
uint Read(
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data,
uint size);
/*
Out: if size != 0, return_value = S_OK and (*processedSize == 0),
then there are no more bytes in stream.
if (size > 0) && there are bytes in stream,
this function must read at least 1 byte.
This function is allowed to read less than number of remaining bytes in stream.
You must call Read function in loop, if you need exact amount of data
*/
}
[ComImport]
[Guid("23170F69-40C1-278A-0000-000300020000")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface ISequentialOutStream
{
[PreserveSig]
int Write(
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data,
uint size,
IntPtr processedSize); // ref uint processedSize
/*
if (size > 0) this function must write at least 1 byte.
This function is allowed to write less than "size".
You must call Write function in loop, if you need to write exact amount of data
*/
}
[ComImport]
[Guid("23170F69-40C1-278A-0000-000300030000")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IInStream //: ISequentialInStream
{
//[PreserveSig]
//int Read(
// [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data,
// uint size,
// IntPtr processedSize); // ref uint processedSize
uint Read(
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data,
uint size);
//[PreserveSig]
void Seek(
long offset,
uint seekOrigin,
IntPtr newPosition); // ref long newPosition
}
[ComImport]
[Guid("23170F69-40C1-278A-0000-000300040000")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IOutStream //: ISequentialOutStream
{
[PreserveSig]
int Write(
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data,
uint size,
IntPtr processedSize); // ref uint processedSize
//[PreserveSig]
void Seek(
long offset,
uint seekOrigin,
IntPtr newPosition); // ref long newPosition
[PreserveSig]
int SetSize(long newSize);
}
internal enum ItemPropId : uint
{
kpidNoProperty = 0,
kpidHandlerItemIndex = 2,
kpidPath,
kpidName,
kpidExtension,
kpidIsFolder,
kpidSize,
kpidPackedSize,
kpidAttributes,
kpidCreationTime,
kpidLastAccessTime,
kpidLastWriteTime,
kpidSolid,
kpidCommented,
kpidEncrypted,
kpidSplitBefore,
kpidSplitAfter,
kpidDictionarySize,
kpidCRC,
kpidType,
kpidIsAnti,
kpidMethod,
kpidHostOS,
kpidFileSystem,
kpidUser,
kpidGroup,
kpidBlock,
kpidComment,
kpidPosition,
kpidPrefix,
kpidTotalSize = 0x1100,
kpidFreeSpace,
kpidClusterSize,
kpidVolumeName,
kpidLocalName = 0x1200,
kpidProvider,
kpidUserDefined = 0x10000
}
[ComImport]
[Guid("23170F69-40C1-278A-0000-000600600000")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
//[AutomationProxy(true)]
internal interface IInArchive
{
[PreserveSig]
int Open(
IInStream stream,
/*[MarshalAs(UnmanagedType.U8)]*/ [In] ref ulong maxCheckStartPosition,
[MarshalAs(UnmanagedType.Interface)] IArchiveOpenCallback openArchiveCallback);
void Close();
//void GetNumberOfItems([In] ref uint numItem);
uint GetNumberOfItems();
void GetProperty(
uint index,
ItemPropId propID, // PROPID
ref PropVariant value); // PROPVARIANT
[PreserveSig]
int Extract(
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] uint[] indices, //[In] ref uint indices,
uint numItems,
int testMode,
[MarshalAs(UnmanagedType.Interface)] IArchiveExtractCallback extractCallback);
// indices must be sorted
// numItems = 0xFFFFFFFF means all files
// testMode != 0 means "test files operation"
void GetArchiveProperty(
uint propID, // PROPID
ref PropVariant value); // PROPVARIANT
//void GetNumberOfProperties([In] ref uint numProperties);
uint GetNumberOfProperties();
void GetPropertyInfo(
uint index,
[MarshalAs(UnmanagedType.BStr)] out string name,
out ItemPropId propID, // PROPID
out ushort varType); //VARTYPE
//void GetNumberOfArchiveProperties([In] ref uint numProperties);
uint GetNumberOfArchiveProperties();
void GetArchivePropertyInfo(
uint index,
[MarshalAs(UnmanagedType.BStr)] string name,
ref uint propID, // PROPID
ref ushort varType); //VARTYPE
}
internal enum ArchivePropId : uint
{
kName = 0,
kClassID,
kExtension,
kAddExtension,
kUpdate,
kKeepName,
kStartSignature,
kFinishSignature,
kAssociate
}
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
internal delegate int CreateObjectDelegate(
[In] ref Guid classID,
[In] ref Guid interfaceID,
//out IntPtr outObject);
[MarshalAs(UnmanagedType.Interface)] out object outObject);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
internal delegate int GetHandlerPropertyDelegate(
ArchivePropId propID,
ref PropVariant value); // PROPVARIANT
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
internal delegate int GetNumberOfFormatsDelegate(out uint numFormats);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
internal delegate int GetHandlerProperty2Delegate(
uint formatIndex,
ArchivePropId propID,
ref PropVariant value); // PROPVARIANT
internal class StreamWrapper : IDisposable
{
protected Stream BaseStream;
protected StreamWrapper(Stream baseStream)
{
this.BaseStream = baseStream;
}
public void Dispose()
{
this.BaseStream.Close();
}
public virtual void Seek(long offset, uint seekOrigin, IntPtr newPosition)
{
long Position = (uint) this.BaseStream.Seek(offset, (SeekOrigin) seekOrigin);
if (newPosition != IntPtr.Zero)
{
Marshal.WriteInt64(newPosition, Position);
}
}
}
internal class InStreamWrapper : StreamWrapper, ISequentialInStream, IInStream
{
public InStreamWrapper(Stream baseStream) : base(baseStream)
{
}
public uint Read(byte[] data, uint size)
{
return (uint) this.BaseStream.Read(data, 0, (int) size);
}
}
// Can close base stream after period of inactivity and reopen it when needed.
// Useful for long opened archives (prevent locking archive file on disk).
internal class InStreamTimedWrapper : StreamWrapper, ISequentialInStream, IInStream
{
private string BaseStreamFileName;
private long BaseStreamLastPosition;
private Timer CloseTimer;
private const int KeepAliveInterval = 10 * 1000; // 10 sec
public InStreamTimedWrapper(Stream baseStream)
: base(baseStream)
{
if ((this.BaseStream is FileStream) && !this.BaseStream.CanWrite && this.BaseStream.CanSeek)
{
this.BaseStreamFileName = ((FileStream) this.BaseStream).Name;
this.CloseTimer = new Timer(new TimerCallback(this.CloseStream), null, KeepAliveInterval, Timeout.Infinite);
}
}
private void CloseStream(object state)
{
if (this.CloseTimer != null)
{
this.CloseTimer.Dispose();
this.CloseTimer = null;
}
if (this.BaseStream != null)
{
if (this.BaseStream.CanSeek)
{
this.BaseStreamLastPosition = this.BaseStream.Position;
}
this.BaseStream.Close();
this.BaseStream = null;
}
}
protected void ReopenStream()
{
if (this.BaseStream == null)
{
if (this.BaseStreamFileName != null)
{
this.BaseStream = new FileStream(this.BaseStreamFileName, FileMode.Open, FileAccess.Read, FileShare.Read);
this.BaseStream.Position = this.BaseStreamLastPosition;
this.CloseTimer = new Timer(new TimerCallback(this.CloseStream), null, KeepAliveInterval, Timeout.Infinite);
}
else
{
throw new ObjectDisposedException("StreamWrapper");
}
}
else if (this.CloseTimer != null)
{
this.CloseTimer.Change(KeepAliveInterval, Timeout.Infinite);
}
}
public uint Read(byte[] data, uint size)
{
this.ReopenStream();
return (uint) this.BaseStream.Read(data, 0, (int) size);
}
public override void Seek(long offset, uint seekOrigin, IntPtr newPosition)
{
this.ReopenStream();
base.Seek(offset, seekOrigin, newPosition);
}
}
internal class OutStreamWrapper : StreamWrapper, ISequentialOutStream, IOutStream
{
public OutStreamWrapper(Stream baseStream) : base(baseStream)
{
}
public int SetSize(long newSize)
{
this.BaseStream.SetLength(newSize);
return 0;
}
public int Write(byte[] data, uint size, IntPtr processedSize)
{
this.BaseStream.Write(data, 0, (int) size);
if (processedSize != IntPtr.Zero)
{
Marshal.WriteInt32(processedSize, (int) size);
}
return 0;
}
}
}