mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-07-17 06:14:59 +00:00
Remove libgsf/libmsi/ComHandler
This commit is contained in:
3
BurnOutSharp/External/README.MD
vendored
3
BurnOutSharp/External/README.MD
vendored
@@ -4,8 +4,5 @@ This directory contains multiple external libraries. Here is the status of each:
|
||||
|
||||
| Directory | Library | Status |
|
||||
| --------- | ------- | ------ |
|
||||
| libgsf | [libgsf](https://github.com/GNOME/libgsf) | 90% ported to C#, currently unused |
|
||||
| libmsi | [msitools](https://github.com/GNOME/msitools) | 100% ported to C#, breaking bugs, currently unused |
|
||||
| psxt001z | [psxt001z](https://github.com/Dremora/psxt001z) | 90% ported to C#, device reading disabled |
|
||||
| SevenZip | [7-Zip (ComHandler.cpp)](https://github.com/mcmilk/7-Zip/blob/63128687a9dffbe60091d04df3baf38af60dc2fa/CPP/7zip/Archive/ComHandler.cpp) | 25% ported to C#, currently unused |
|
||||
| stormlibsharp | [stormlibsharp](https://github.com/robpaveza/stormlibsharp) | External submodule |
|
||||
907
BurnOutSharp/External/SevenZip/ComHandler.cs
vendored
907
BurnOutSharp/External/SevenZip/ComHandler.cs
vendored
@@ -1,907 +0,0 @@
|
||||
// ComHandler.cpp
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace NArchive
|
||||
{
|
||||
enum kProps : byte
|
||||
{
|
||||
kpidPath,
|
||||
kpidSize,
|
||||
kpidPackSize,
|
||||
kpidCTime,
|
||||
kpidMTime
|
||||
};
|
||||
|
||||
enum kArcProps : byte
|
||||
{
|
||||
kpidExtension,
|
||||
kpidClusterSize,
|
||||
kpidSectorSize
|
||||
};
|
||||
}
|
||||
|
||||
//namespace NArchive
|
||||
//{
|
||||
// // #define BitConverter.ToUInt16(p) GetUi16(p)
|
||||
// // #define BitConverter.ToUInt32(p) GetUi32(p)
|
||||
|
||||
// class CHandler : IInArchive, IInArchiveGetStream, CMyUnknownImp
|
||||
// {
|
||||
// private CMyComPtr<IInStream> _stream;
|
||||
// NArchive.NCom.CDatabase _db;
|
||||
|
||||
// public MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream);
|
||||
// public INTERFACE_IInArchive();
|
||||
// public STDMETHOD(GetStream)(uint index, ISequentialInStream** stream);
|
||||
// }
|
||||
|
||||
// IMP_IInArchive_Props
|
||||
// IMP_IInArchive_ArcProps
|
||||
|
||||
// STDMETHODIMP CHandler::GetArchiveProperty(kProps propID, PROPVARIANT* value)
|
||||
// {
|
||||
// COM_TRY_BEGIN
|
||||
// NWindows::NCOM::CPropVariant prop;
|
||||
// switch (propID)
|
||||
// {
|
||||
// case kpidExtension: prop = kExtensions[(ulong)_db.Type]; break;
|
||||
// case kpidPhySize: prop = _db.PhySize; break;
|
||||
// case kpidClusterSize: prop = (uint)1 << _db.SectorSizeBits; break;
|
||||
// case kpidSectorSize: prop = (uint)1 << _db.MiniSectorSizeBits; break;
|
||||
// case kpidMainSubfile: if (_db.MainSubfile >= 0) prop = (uint)_db.MainSubfile; break;
|
||||
// case kpidIsNotArcType: if (_db.IsNotArcType()) prop = true; break;
|
||||
// }
|
||||
// prop.Detach(value);
|
||||
// return true;
|
||||
// COM_TRY_END
|
||||
// }
|
||||
|
||||
// STDMETHODIMP CHandler::GetProperty(uint index, PROPID propID, PROPVARIANT* value)
|
||||
// {
|
||||
// COM_TRY_BEGIN
|
||||
// NWindows::NCOM::CPropVariant prop;
|
||||
// const CRef &ref = _db.Refs[index];
|
||||
// const CItem &item = _db.Items[ref.Did];
|
||||
|
||||
// switch (propID)
|
||||
// {
|
||||
// case kpidPath: prop = _db.GetItemPath(index); break;
|
||||
// case kpidIsDir: prop = item.IsDir(); break;
|
||||
// case kpidCTime: prop = item.CTime; break;
|
||||
// case kpidMTime: prop = item.MTime; break;
|
||||
// case kpidPackSize: if (!item.IsDir()) prop = _db.GetItemPackSize(item.Size); break;
|
||||
// case kpidSize: if (!item.IsDir()) prop = item.Size; break;
|
||||
// }
|
||||
// prop.Detach(value);
|
||||
// return true;
|
||||
// COM_TRY_END
|
||||
// }
|
||||
|
||||
// STDMETHODIMP CHandler::Open(Stream inStream,
|
||||
// const ulong* /* maxCheckStartPosition */,
|
||||
// IArchiveOpenCallback* /* openArchiveCallback */)
|
||||
// {
|
||||
// COM_TRY_BEGIN
|
||||
// Close();
|
||||
// try
|
||||
// {
|
||||
// if (_db.Open(inStream) != true)
|
||||
// return false;
|
||||
// _stream = inStream;
|
||||
// }
|
||||
// catch (...) { return false; }
|
||||
// return true;
|
||||
// COM_TRY_END
|
||||
// }
|
||||
|
||||
// STDMETHODIMP CHandler::Close()
|
||||
// {
|
||||
// _db.Clear();
|
||||
// _stream.Release();
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// STDMETHODIMP CHandler::Extract(const uint* indices, uint numItems,
|
||||
// int testMode, IArchiveExtractCallback*extractCallback)
|
||||
//{
|
||||
// COM_TRY_BEGIN
|
||||
// bool allFilesMode = (numItems == (uint)(int)-1);
|
||||
// if (allFilesMode)
|
||||
// numItems = _db.Refs.Size();
|
||||
// if (numItems == 0)
|
||||
// return true;
|
||||
// uint i;
|
||||
// ulong totalSize = 0;
|
||||
// for (i = 0; i < numItems; i++)
|
||||
// {
|
||||
// const CItem &item = _db.Items[_db.Refs[allFilesMode ? i : indices[i]].Did];
|
||||
// if (!item.IsDir())
|
||||
// totalSize += item.Size;
|
||||
// }
|
||||
// RINOK(extractCallback.SetTotal(totalSize));
|
||||
|
||||
// ulong totalPackSize;
|
||||
// totalSize = totalPackSize = 0;
|
||||
|
||||
// NCompress::CCopyCoder* copyCoderSpec = new NCompress::CCopyCoder();
|
||||
// CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
|
||||
|
||||
// CLocalProgress* lps = new CLocalProgress;
|
||||
// CMyComPtr<ICompressProgressInfo> progress = lps;
|
||||
// lps.Init(extractCallback, false);
|
||||
|
||||
// for (i = 0; i < numItems; i++)
|
||||
// {
|
||||
// lps.InSize = totalPackSize;
|
||||
// lps.OutSize = totalSize;
|
||||
// RINOK(lps.SetCur());
|
||||
// int index = allFilesMode ? i : indices[i];
|
||||
// const CItem &item = _db.Items[_db.Refs[index].Did];
|
||||
|
||||
// CMyComPtr<ISequentialOutStream> outStream;
|
||||
// int askMode = testMode ?
|
||||
// NExtract::NAskMode::kTest :
|
||||
// NExtract::NAskMode::kExtract;
|
||||
// RINOK(extractCallback.GetStream(index, &outStream, askMode));
|
||||
|
||||
// if (item.IsDir())
|
||||
// {
|
||||
// RINOK(extractCallback.PrepareOperation(askMode));
|
||||
// RINOK(extractCallback.SetOperationResult(NExtract::NOperationResult::kOK));
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// totalPackSize += _db.GetItemPackSize(item.Size);
|
||||
// totalSize += item.Size;
|
||||
|
||||
// if (!testMode && !outStream)
|
||||
// continue;
|
||||
// RINOK(extractCallback.PrepareOperation(askMode));
|
||||
// int res = NExtract::NOperationResult::kDataError;
|
||||
// CMyComPtr<ISequentialInStream> inStream;
|
||||
// uint hres = GetStream(index, &inStream);
|
||||
// if (hres == false)
|
||||
// res = NExtract::NOperationResult::kDataError;
|
||||
// else if (hres == E_NOTIMPL)
|
||||
// res = NExtract::NOperationResult::kUnsupportedMethod;
|
||||
// else
|
||||
// {
|
||||
// RINOK(hres);
|
||||
// if (inStream)
|
||||
// {
|
||||
// RINOK(copyCoder.Code(inStream, outStream, null, null, progress));
|
||||
// if (copyCoderSpec.TotalSize == item.Size)
|
||||
// res = NExtract::NOperationResult::kOK;
|
||||
// }
|
||||
// }
|
||||
// outStream.Release();
|
||||
// RINOK(extractCallback.SetOperationResult(res));
|
||||
// }
|
||||
// return true;
|
||||
// COM_TRY_END
|
||||
//}
|
||||
|
||||
// STDMETHODIMP CHandler::GetNumberOfItems(uint * numItems)
|
||||
// {
|
||||
// *numItems = _db.Refs.Size();
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// STDMETHODIMP CHandler::GetStream(uint index, ISequentialInStream * *stream)
|
||||
// {
|
||||
// COM_TRY_BEGIN
|
||||
// * stream = 0;
|
||||
// uint itemIndex = _db.Refs[index].Did;
|
||||
// const CItem &item = _db.Items[itemIndex];
|
||||
// CClusterInStream* streamSpec = new CClusterInStream;
|
||||
// CMyComPtr<ISequentialInStream> streamTemp = streamSpec;
|
||||
// streamSpec.Stream = _stream;
|
||||
// streamSpec.StartOffset = 0;
|
||||
|
||||
// bool isLargeStream = (itemIndex == 0 || _db.IsLargeStream(item.Size));
|
||||
// int bsLog = isLargeStream ? _db.SectorSizeBits : _db.MiniSectorSizeBits;
|
||||
// streamSpec.BlockSizeLog = bsLog;
|
||||
// streamSpec.Size = item.Size;
|
||||
|
||||
// uint clusterSize = (uint)1 << bsLog;
|
||||
// ulong numClusters64 = (item.Size + clusterSize - 1) >> bsLog;
|
||||
// if (numClusters64 >= ((uint)1 << 31))
|
||||
// return E_NOTIMPL;
|
||||
// streamSpec.Vector.ClearAndReserve((ulong)numClusters64);
|
||||
// uint sid = item.Sid;
|
||||
// ulong size = item.Size;
|
||||
|
||||
// if (size != 0)
|
||||
// {
|
||||
// for (; ; size -= clusterSize)
|
||||
// {
|
||||
// if (isLargeStream)
|
||||
// {
|
||||
// if (sid >= _db.FatSize)
|
||||
// return false;
|
||||
// streamSpec.Vector.AddInReserved(sid + 1);
|
||||
// sid = _db.Fat[sid];
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// ulong val = 0;
|
||||
// if (sid >= _db.MatSize || !_db.GetMiniCluster(sid, val) || val >= (ulong)1 << 32)
|
||||
// return false;
|
||||
// streamSpec.Vector.AddInReserved((uint)val);
|
||||
// sid = _db.Mat[sid];
|
||||
// }
|
||||
// if (size <= clusterSize)
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// if (sid != NFatID.kEndOfChain)
|
||||
// return false;
|
||||
// RINOK(streamSpec.InitAndSeek());
|
||||
// *stream = streamTemp.Detach();
|
||||
// return true;
|
||||
// COM_TRY_END
|
||||
// }
|
||||
//}
|
||||
|
||||
namespace NArchive.NCom
|
||||
{
|
||||
public enum EType
|
||||
{
|
||||
k_Type_Common,
|
||||
k_Type_Msi,
|
||||
k_Type_Msp,
|
||||
k_Type_Doc,
|
||||
k_Type_Ppt,
|
||||
k_Type_Xls
|
||||
};
|
||||
|
||||
public enum NFatID : uint
|
||||
{
|
||||
kFree = 0xFFFFFFFF,
|
||||
kEndOfChain = 0xFFFFFFFE,
|
||||
kFatSector = 0xFFFFFFFD,
|
||||
kMatSector = 0xFFFFFFFC,
|
||||
kMaxValue = 0xFFFFFFFA,
|
||||
}
|
||||
|
||||
public enum NItemType : byte
|
||||
{
|
||||
kEmpty = 0,
|
||||
kStorage = 1,
|
||||
kStream = 2,
|
||||
kLockbytes = 3,
|
||||
kProperty = 4,
|
||||
kRootStorage = 5,
|
||||
}
|
||||
|
||||
public class CItem
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public byte[] Name { get; set; } = new byte[CDatabase.kNameSizeMax];
|
||||
|
||||
// public ushort NameSize { get; set; }
|
||||
|
||||
// public uint Flags { get; set; }
|
||||
|
||||
public DateTime? CTime { get; set; }
|
||||
|
||||
public DateTime? MTime { get; set; }
|
||||
|
||||
public ulong Size { get; set; }
|
||||
|
||||
public uint LeftDid { get; set; }
|
||||
|
||||
public uint RightDid { get; set; }
|
||||
|
||||
public uint SonDid { get; set; }
|
||||
|
||||
public uint Sid { get; set; }
|
||||
|
||||
public NItemType Type { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public bool IsEmpty() => Type == NItemType.kEmpty;
|
||||
|
||||
public bool IsDir() => Type == NItemType.kStorage || Type == NItemType.kRootStorage;
|
||||
|
||||
public void Parse(in byte[] p, int offset, bool mode64bit)
|
||||
{
|
||||
Array.Copy(p, offset, Name, 0, CDatabase.kNameSizeMax);
|
||||
// NameSize = BitConverter.ToUInt16(p, offset + 64);
|
||||
Type = (NItemType)p[offset + 66];
|
||||
LeftDid = BitConverter.ToUInt32(p, offset + 68);
|
||||
RightDid = BitConverter.ToUInt32(p, offset + 72);
|
||||
SonDid = BitConverter.ToUInt32(p, offset + 76);
|
||||
// Flags = BitConverter.ToUInt32(p + offset + 96);
|
||||
CDatabase.GetFileTimeFromMem(p, offset + 100, out DateTime? ctime);
|
||||
CTime = ctime;
|
||||
CDatabase.GetFileTimeFromMem(p, offset + 108, out DateTime? mtime);
|
||||
MTime = mtime;
|
||||
Sid = BitConverter.ToUInt32(p, offset + 116);
|
||||
Size = BitConverter.ToUInt32(p, offset + 120);
|
||||
if (mode64bit)
|
||||
Size |= ((ulong)BitConverter.ToUInt32(p, offset + 124) << 32);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class CRef
|
||||
{
|
||||
public int Parent { get; set; }
|
||||
|
||||
public uint Did { get; set; }
|
||||
}
|
||||
|
||||
public class CDatabase
|
||||
{
|
||||
#region Constants
|
||||
|
||||
internal static readonly byte[] kSignature = { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
|
||||
|
||||
internal static readonly string[] kExtensions =
|
||||
{
|
||||
"compound",
|
||||
"msi",
|
||||
"msp",
|
||||
"doc",
|
||||
"ppt",
|
||||
"xls",
|
||||
};
|
||||
|
||||
internal const int kNameSizeMax = 64;
|
||||
|
||||
internal const uint kNoDid = 0xFFFFFFFF;
|
||||
|
||||
internal const string k_Msi_Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._";
|
||||
|
||||
// static const char * const k_Msi_ID = ""; // "{msi}";
|
||||
internal const char k_Msi_SpecChar = '!';
|
||||
|
||||
internal const int k_Msi_NumBits = 6;
|
||||
internal const int k_Msi_NumChars = 1 << k_Msi_NumBits;
|
||||
internal const int k_Msi_CharMask = k_Msi_NumChars - 1;
|
||||
internal const char k_Msi_StartUnicodeChar = (char)0x3800;
|
||||
internal const int k_Msi_UnicodeRange = k_Msi_NumChars * (k_Msi_NumChars + 1);
|
||||
|
||||
// There is name "[!]MsiPatchSequence" in msp files
|
||||
internal const int kMspSequence_Size = 18;
|
||||
internal static readonly byte[] kMspSequence = new byte[kMspSequence_Size]
|
||||
{
|
||||
0x40, 0x48, 0x96, 0x45, 0x6C, 0x3E, 0xE4, 0x45,
|
||||
0xE6, 0x42, 0x16, 0x42, 0x37, 0x41, 0x27, 0x41,
|
||||
0x37, 0x41
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
internal uint NumSectorsInMiniStream { get; set; }
|
||||
|
||||
internal uint[] MiniSids { get; set; }
|
||||
|
||||
public uint[] Fat { get; set; }
|
||||
|
||||
public uint FatSize { get; set; }
|
||||
|
||||
public uint[] Mat { get; set; }
|
||||
|
||||
public uint MatSize { get; set; }
|
||||
|
||||
public List<CItem> Items { get; set; }
|
||||
|
||||
public List<CRef> Refs { get; set; }
|
||||
|
||||
public uint LongStreamMinSize { get; set; }
|
||||
|
||||
public int SectorSizeBits { get; set; }
|
||||
|
||||
public int MiniSectorSizeBits { get; set; }
|
||||
|
||||
public int MainSubfile { get; set; }
|
||||
|
||||
public long PhySize { get; set; }
|
||||
|
||||
public EType Type { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public bool IsNotArcType() => Type != EType.k_Type_Msi && Type != EType.k_Type_Msp;
|
||||
|
||||
public void UpdatePhySize(long val)
|
||||
{
|
||||
if (PhySize < val)
|
||||
PhySize = val;
|
||||
}
|
||||
|
||||
public bool ReadSector(Stream inStream, byte[] buf, int sectorSizeBits, uint sid)
|
||||
{
|
||||
UpdatePhySize(((long)sid + 2) << sectorSizeBits);
|
||||
|
||||
long offset = ((long)sid + 1) << sectorSizeBits;
|
||||
if (inStream.Seek(offset, SeekOrigin.Begin) != offset)
|
||||
return false;
|
||||
|
||||
return inStream.Read(buf, 0, 1 << sectorSizeBits) == 1 << sectorSizeBits;
|
||||
}
|
||||
|
||||
public bool ReadIDs(Stream inStream, byte[] buf, int sectorSizeBits, uint sid, uint[] dest, int destPtr)
|
||||
{
|
||||
ReadSector(inStream, buf, sectorSizeBits, sid);
|
||||
int sectorSize = 1 << sectorSizeBits;
|
||||
for (int i = destPtr, t = 0; t < sectorSize; i++, t += 4)
|
||||
{
|
||||
dest[i] = BitConverter.ToUInt32(buf, t);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Update_PhySize_WithItem(int index)
|
||||
{
|
||||
CItem item = Items[index];
|
||||
bool isLargeStream = (index == 0 || IsLargeStream(item.Size));
|
||||
if (!isLargeStream)
|
||||
return true;
|
||||
|
||||
int bsLog = isLargeStream ? SectorSizeBits : MiniSectorSizeBits;
|
||||
// streamSpec.Size = item.Size;
|
||||
|
||||
uint clusterSize = (uint)1 << bsLog;
|
||||
ulong numClusters64 = (item.Size + clusterSize - 1) >> bsLog;
|
||||
if (numClusters64 >= ((uint)1 << 31))
|
||||
return false;
|
||||
|
||||
uint sid = item.Sid;
|
||||
ulong size = item.Size;
|
||||
|
||||
if (size != 0)
|
||||
{
|
||||
for (; ; size -= clusterSize)
|
||||
{
|
||||
// if (isLargeStream)
|
||||
{
|
||||
if (sid >= FatSize)
|
||||
return false;
|
||||
|
||||
UpdatePhySize((sid + 2) << bsLog);
|
||||
sid = Fat[(int)sid];
|
||||
}
|
||||
|
||||
if (size <= clusterSize)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sid != (uint)NFatID.kEndOfChain)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
PhySize = 0;
|
||||
|
||||
Array.Clear(Fat, 0, Fat.Length);
|
||||
Array.Clear(MiniSids, 0, MiniSids.Length);
|
||||
Array.Clear(Mat, 0, Mat.Length);
|
||||
Items.Clear();
|
||||
Refs.Clear();
|
||||
}
|
||||
|
||||
public bool IsLargeStream(ulong size) => size >= LongStreamMinSize;
|
||||
|
||||
public string GetItemPath(uint index)
|
||||
{
|
||||
string s = string.Empty;
|
||||
while (index != kNoDid)
|
||||
{
|
||||
CRef cref = Refs[(int)index];
|
||||
CItem item = Items[(int)cref.Did];
|
||||
if (!string.IsNullOrEmpty(s))
|
||||
s = Path.DirectorySeparatorChar + s;
|
||||
s.Insert(0, ConvertName(item.Name));
|
||||
index = (uint)cref.Parent;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public ulong GetItemPackSize(ulong size)
|
||||
{
|
||||
ulong mask = (ulong)(1 << (IsLargeStream(size) ? SectorSizeBits : MiniSectorSizeBits)) - 1;
|
||||
return (size + mask) & ~mask;
|
||||
}
|
||||
|
||||
public bool GetMiniCluster(uint sid, ref ulong res)
|
||||
{
|
||||
int subBits = SectorSizeBits - MiniSectorSizeBits;
|
||||
uint fid = sid >> (int)subBits;
|
||||
if (fid >= NumSectorsInMiniStream)
|
||||
return false;
|
||||
res = (ulong)(((MiniSids[(int)fid] + 1) << subBits) + (sid & ((1 << subBits) - 1)));
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Open(Stream inStream)
|
||||
{
|
||||
MainSubfile = -1;
|
||||
Type = EType.k_Type_Common;
|
||||
int kHeaderSize = 512;
|
||||
byte[] p = new byte[kHeaderSize];
|
||||
PhySize = kHeaderSize;
|
||||
if (inStream.Read(p, 0, kHeaderSize) != kHeaderSize)
|
||||
return false;
|
||||
|
||||
if (!p.Take(kHeaderSize).SequenceEqual(kSignature))
|
||||
return false;
|
||||
if (BitConverter.ToUInt16(p, 0x1A) > 4) // majorVer
|
||||
return false;
|
||||
if (BitConverter.ToUInt16(p, 0x1C) != 0xFFFE) // Little-endian
|
||||
return false;
|
||||
int sectorSizeBits = BitConverter.ToUInt16(p, 0x1E);
|
||||
bool mode64bit = (sectorSizeBits >= 12);
|
||||
int miniSectorSizeBits = BitConverter.ToUInt16(p, 0x20);
|
||||
SectorSizeBits = sectorSizeBits;
|
||||
MiniSectorSizeBits = miniSectorSizeBits;
|
||||
|
||||
if (sectorSizeBits > 24 ||
|
||||
sectorSizeBits < 7 ||
|
||||
miniSectorSizeBits > 24 ||
|
||||
miniSectorSizeBits < 2 ||
|
||||
miniSectorSizeBits > sectorSizeBits)
|
||||
return false;
|
||||
uint numSectorsForFAT = BitConverter.ToUInt32(p, 0x2C); // SAT
|
||||
LongStreamMinSize = BitConverter.ToUInt32(p, 0x38);
|
||||
|
||||
uint sectSize = (uint)1 << sectorSizeBits;
|
||||
|
||||
byte[] sect = new byte[sectSize];
|
||||
|
||||
int ssb2 = sectorSizeBits - 2;
|
||||
int numSidsInSec = 1 << ssb2;
|
||||
int numFatItems = (int)(numSectorsForFAT << ssb2);
|
||||
if ((numFatItems >> ssb2) != numSectorsForFAT)
|
||||
return false;
|
||||
FatSize = (uint)numFatItems;
|
||||
|
||||
{
|
||||
uint numSectorsForBat = BitConverter.ToUInt32(p, 0x48); // master sector allocation table
|
||||
const uint kNumHeaderBatItems = 109;
|
||||
uint numBatItems = kNumHeaderBatItems + (numSectorsForBat << ssb2);
|
||||
if (numBatItems < kNumHeaderBatItems || ((numBatItems - kNumHeaderBatItems) >> ssb2) != numSectorsForBat)
|
||||
return false;
|
||||
uint[] bat = new uint[numBatItems];
|
||||
uint i;
|
||||
for (i = 0; i < kNumHeaderBatItems; i++)
|
||||
bat[i] = BitConverter.ToUInt32(p, (int)(0x4c + i * 4));
|
||||
uint sid = BitConverter.ToUInt32(p, 0x44);
|
||||
for (uint s = 0; s < numSectorsForBat; s++)
|
||||
{
|
||||
if (!ReadIDs(inStream, sect, sectorSizeBits, sid, bat, (int)i))
|
||||
return false;
|
||||
i += (uint)numSidsInSec - 1;
|
||||
sid = bat[i];
|
||||
}
|
||||
numBatItems = i;
|
||||
|
||||
Fat = new uint[numFatItems];
|
||||
uint j = 0;
|
||||
|
||||
for (i = 0; i < numFatItems; j++, i += (uint)numSidsInSec)
|
||||
{
|
||||
if (j >= numBatItems)
|
||||
return false;
|
||||
if (!ReadIDs(inStream, sect, sectorSizeBits, bat[j], Fat, (int)i))
|
||||
return false;
|
||||
}
|
||||
numFatItems = (int)i;
|
||||
FatSize = i;
|
||||
}
|
||||
|
||||
uint numMatItems;
|
||||
{
|
||||
uint numSectorsForMat = BitConverter.ToUInt32(p, 0x40);
|
||||
numMatItems = numSectorsForMat << ssb2;
|
||||
if ((numMatItems >> ssb2) != numSectorsForMat)
|
||||
return false;
|
||||
Mat = new uint[numMatItems];
|
||||
uint i;
|
||||
uint sid = BitConverter.ToUInt32(p, 0x3C); // short-sector table SID
|
||||
for (i = 0; i < numMatItems; i += (uint)numSidsInSec)
|
||||
{
|
||||
if (!ReadIDs(inStream, sect, sectorSizeBits, sid, Mat, (int)i))
|
||||
return false;
|
||||
if (sid >= numFatItems)
|
||||
return false;
|
||||
sid = Fat[sid];
|
||||
}
|
||||
|
||||
if (sid != (uint)NFatID.kEndOfChain)
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
byte[] used = new byte[numFatItems];
|
||||
for (uint i = 0; i < numFatItems; i++)
|
||||
used[i] = 0;
|
||||
uint sid = BitConverter.ToUInt32(p, 0x30); // directory stream SID
|
||||
for (; ; )
|
||||
{
|
||||
if (sid >= numFatItems)
|
||||
return false;
|
||||
if (used[sid] != 0)
|
||||
return false;
|
||||
used[sid] = 1;
|
||||
if (!ReadSector(inStream, sect, sectorSizeBits, sid))
|
||||
return false;
|
||||
for (uint i = 0; i < sectSize; i += 128)
|
||||
{
|
||||
CItem item = new CItem();
|
||||
item.Parse(sect, (int)i, mode64bit);
|
||||
Items.Add(item);
|
||||
}
|
||||
sid = Fat[sid];
|
||||
if (sid == (uint)NFatID.kEndOfChain)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CItem root = Items[0];
|
||||
|
||||
{
|
||||
uint numSectorsInMiniStream;
|
||||
{
|
||||
ulong numSatSects64 = (root.Size + sectSize - 1) >> sectorSizeBits;
|
||||
if (numSatSects64 > (uint)NFatID.kMaxValue)
|
||||
return false;
|
||||
numSectorsInMiniStream = (uint)numSatSects64;
|
||||
}
|
||||
NumSectorsInMiniStream = numSectorsInMiniStream;
|
||||
MiniSids = new uint[numSectorsInMiniStream];
|
||||
{
|
||||
ulong matSize64 = (root.Size + ((ulong)1 << miniSectorSizeBits) - 1) >> miniSectorSizeBits;
|
||||
if (matSize64 > (uint)NFatID.kMaxValue)
|
||||
return false;
|
||||
MatSize = (uint)matSize64;
|
||||
if (numMatItems < MatSize)
|
||||
return false;
|
||||
}
|
||||
|
||||
uint sid = root.Sid;
|
||||
for (uint i = 0; ; i++)
|
||||
{
|
||||
if (sid == (uint)NFatID.kEndOfChain)
|
||||
{
|
||||
if (i != numSectorsInMiniStream)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
if (i >= numSectorsInMiniStream)
|
||||
return false;
|
||||
MiniSids[i] = sid;
|
||||
if (sid >= numFatItems)
|
||||
return false;
|
||||
sid = Fat[sid];
|
||||
}
|
||||
}
|
||||
|
||||
if (!AddNode(-1, root.SonDid))
|
||||
return false;
|
||||
|
||||
ulong numCabs = 0;
|
||||
|
||||
for (int i = 0; i < Refs.Count; i++)
|
||||
{
|
||||
CItem item = Items[(int)Refs[i].Did];
|
||||
if (item.IsDir() || numCabs > 1)
|
||||
continue;
|
||||
bool isMsiName = false;
|
||||
string msiName = ConvertName(item.Name, ref isMsiName);
|
||||
if (isMsiName && !string.IsNullOrEmpty(msiName))
|
||||
{
|
||||
// bool isThereExt = (msiName.Find(L'.') >= 0);
|
||||
bool isMsiSpec = (msiName[0] == k_Msi_SpecChar);
|
||||
string extension = Path.GetExtension(msiName).Trim('.');
|
||||
|
||||
if ((msiName.Length >= 4 && extension.Equals("cab", StringComparison.OrdinalIgnoreCase)
|
||||
|| (!isMsiSpec && msiName.Length >= 3 && extension.Equals("exe", StringComparison.OrdinalIgnoreCase)))
|
||||
// || (!isMsiSpec && !isThereExt)
|
||||
)
|
||||
{
|
||||
numCabs++;
|
||||
MainSubfile = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (numCabs > 1)
|
||||
MainSubfile = -1;
|
||||
|
||||
{
|
||||
for (int t = 0; t < Items.Count; t++)
|
||||
{
|
||||
Update_PhySize_WithItem(t);
|
||||
}
|
||||
}
|
||||
{
|
||||
for (int t = 0; t < Items.Count; t++)
|
||||
{
|
||||
CItem item = Items[t];
|
||||
|
||||
if (IsMsiName(item.Name))
|
||||
{
|
||||
Type = EType.k_Type_Msi;
|
||||
|
||||
if (item.Name.Take(kMspSequence_Size).SequenceEqual(kMspSequence))
|
||||
{
|
||||
Type = EType.k_Type_Msp;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (AreEqualNames(item.Name, "WordDocument"))
|
||||
{
|
||||
Type = EType.k_Type_Doc;
|
||||
break;
|
||||
}
|
||||
if (AreEqualNames(item.Name, "PowerPoint Document"))
|
||||
{
|
||||
Type = EType.k_Type_Ppt;
|
||||
break;
|
||||
}
|
||||
if (AreEqualNames(item.Name, "Workbook"))
|
||||
{
|
||||
Type = EType.k_Type_Xls;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
internal bool AddNode(int parent, uint did)
|
||||
{
|
||||
if (did == kNoDid)
|
||||
return true;
|
||||
if (did >= (uint)Items.Count)
|
||||
return false;
|
||||
CItem item = Items[(int)did];
|
||||
if (item.IsEmpty())
|
||||
return false;
|
||||
|
||||
CRef cref = new CRef();
|
||||
cref.Parent = parent;
|
||||
cref.Did = did;
|
||||
Refs.Add(cref);
|
||||
int index = Refs.Count - 1;
|
||||
if (Refs.Count > Items.Count)
|
||||
return false;
|
||||
|
||||
if (!AddNode(parent, item.LeftDid))
|
||||
return false;
|
||||
if (!AddNode(parent, item.RightDid))
|
||||
return false;
|
||||
if (item.IsDir() && !AddNode(index, item.SonDid))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static void GetFileTimeFromMem(in byte[] p, int offset, out DateTime? ft)
|
||||
{
|
||||
long time = BitConverter.ToInt64(p, offset);
|
||||
ft = DateTime.FromFileTime(time);
|
||||
}
|
||||
|
||||
internal static string CompoundNameToFileName(in string s)
|
||||
{
|
||||
string res = string.Empty;
|
||||
for (int i = 0; i < s.Length; i++)
|
||||
{
|
||||
char c = s[i];
|
||||
if (c < 0x20)
|
||||
res += $"[{c}]";
|
||||
else
|
||||
res += c;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
internal static bool IsMsiName(in byte[] p)
|
||||
{
|
||||
uint c = BitConverter.ToUInt16(p, 0);
|
||||
return c >= k_Msi_StartUnicodeChar && c <= k_Msi_StartUnicodeChar + k_Msi_UnicodeRange;
|
||||
}
|
||||
|
||||
internal static bool AreEqualNames(in byte[] rawName, in string asciiName)
|
||||
{
|
||||
for (int i = 0; i < kNameSizeMax / 2; i++)
|
||||
{
|
||||
char c = (char)BitConverter.ToUInt16(rawName, i * 2);
|
||||
char c2 = asciiName[i];
|
||||
if (c != c2)
|
||||
return false;
|
||||
if (c == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool CompoundMsiNameToFileName(in string name, ref string res)
|
||||
{
|
||||
res = string.Empty;
|
||||
for (int i = 0; i < name.Length; i++)
|
||||
{
|
||||
char c = name[i];
|
||||
if (c < (char)k_Msi_StartUnicodeChar || c > (char)(k_Msi_StartUnicodeChar + k_Msi_UnicodeRange))
|
||||
return false;
|
||||
/*
|
||||
if (i == 0)
|
||||
res += k_Msi_ID;
|
||||
*/
|
||||
c -= k_Msi_StartUnicodeChar;
|
||||
|
||||
int c0 = c & k_Msi_CharMask;
|
||||
int c1 = c >> k_Msi_NumBits;
|
||||
|
||||
if (c1 <= k_Msi_NumChars)
|
||||
{
|
||||
res += k_Msi_Chars[c0];
|
||||
if (c1 == k_Msi_NumChars)
|
||||
break;
|
||||
res += k_Msi_Chars[c1];
|
||||
}
|
||||
else
|
||||
res += k_Msi_SpecChar;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static string ConvertName(in byte[] p, ref bool isMsi)
|
||||
{
|
||||
isMsi = false;
|
||||
string s = string.Empty;
|
||||
|
||||
for (int i = 0; i < kNameSizeMax; i += 2)
|
||||
{
|
||||
char c = (char)BitConverter.ToUInt16(p, i);
|
||||
if (c == 0)
|
||||
break;
|
||||
s += c;
|
||||
}
|
||||
|
||||
string msiName = string.Empty;
|
||||
if (CompoundMsiNameToFileName(s, ref msiName))
|
||||
{
|
||||
isMsi = true;
|
||||
return msiName;
|
||||
}
|
||||
return CompoundNameToFileName(s);
|
||||
}
|
||||
|
||||
internal static string ConvertName(in byte[] p)
|
||||
{
|
||||
bool isMsi = false;
|
||||
return ConvertName(p, ref isMsi);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
81
BurnOutSharp/External/libgsf/GsfBlob.cs
vendored
81
BurnOutSharp/External/libgsf/GsfBlob.cs
vendored
@@ -1,81 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-blob.c: a chunk of data
|
||||
*
|
||||
* Copyright (C) 2006 Novell Inc
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace LibGSF
|
||||
{
|
||||
// TODO: Can this be made internal?
|
||||
public class GsfBlob
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public long Size { get; private set; }
|
||||
|
||||
public byte[] Data { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfBlob() { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new GsfBlob object to hold the specified data. The blob can then
|
||||
/// be used as a facility for reference-counting for the data. The data is
|
||||
/// copied internally, so the blob does not hold references to external chunks
|
||||
/// of memory.
|
||||
/// </summary>
|
||||
/// <param name="size">Size of the data in bytes.</param>
|
||||
/// <param name="data_to_copy">Data which will be copied into the blob, or null if <paramref name="size"/> is zero.</param>
|
||||
/// <param name="error">Location to store error, or null.</param>
|
||||
/// <returns>A newly-created GsfBlob, or null if the data could not be copied.</returns>
|
||||
public static GsfBlob Create(long size, byte[] data_to_copy, int dataPtr, ref Exception error)
|
||||
{
|
||||
if (!((size > 0 && data_to_copy != null) || (size == 0 && data_to_copy == null)))
|
||||
return null;
|
||||
if (error != null)
|
||||
return null;
|
||||
|
||||
byte[] data;
|
||||
if (data_to_copy != null)
|
||||
{
|
||||
data = new byte[size];
|
||||
Array.Copy(data_to_copy, dataPtr, data, 0, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
data = null;
|
||||
}
|
||||
|
||||
return new GsfBlob
|
||||
{
|
||||
Size = size,
|
||||
Data = data,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
289
BurnOutSharp/External/libgsf/GsfClipData.cs
vendored
289
BurnOutSharp/External/libgsf/GsfClipData.cs
vendored
@@ -1,289 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-clip-data.c: clipboard data
|
||||
*
|
||||
* Copyright (C) 2006 Novell Inc
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using static LibGSF.GsfUtils;
|
||||
|
||||
namespace LibGSF
|
||||
{
|
||||
#region Enums
|
||||
|
||||
// TODO: Can this be made internal?
|
||||
public enum GsfClipFormat
|
||||
{
|
||||
/// <summary>
|
||||
/// Windows clipboard format
|
||||
/// </summary>
|
||||
GSF_CLIP_FORMAT_WINDOWS_CLIPBOARD = -1,
|
||||
|
||||
/// <summary>
|
||||
/// Macintosh clipboard format
|
||||
/// </summary>
|
||||
GSF_CLIP_FORMAT_MACINTOSH_CLIPBOARD = -2,
|
||||
|
||||
/// <summary>
|
||||
/// GUID that contains a format identifier
|
||||
/// </summary>
|
||||
GSF_CLIP_FORMAT_GUID = -3,
|
||||
|
||||
/// <summary>
|
||||
/// No clipboard data
|
||||
/// </summary>
|
||||
GSF_CLIP_FORMAT_NO_DATA = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Custom clipboard format
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// In the file it's actually any positive integer
|
||||
/// </remarks>
|
||||
GSF_CLIP_FORMAT_CLIPBOARD_FORMAT_NAME = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Unknown clipboard type or invalid data
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is our own value for unknown types or invalid data
|
||||
/// </remarks>
|
||||
GSF_CLIP_FORMAT_UNKNOWN
|
||||
}
|
||||
|
||||
// TODO: Can this be made internal?
|
||||
public enum GsfClipFormatWindows
|
||||
{
|
||||
/// <summary>
|
||||
/// Our own value
|
||||
/// </summary>
|
||||
GSF_CLIP_FORMAT_WINDOWS_ERROR = -1,
|
||||
|
||||
/// <summary>
|
||||
/// Our own value
|
||||
/// </summary>
|
||||
GSF_CLIP_FORMAT_WINDOWS_UNKNOWN = -2,
|
||||
|
||||
/// <summary>
|
||||
/// CF_METAFILEPICT
|
||||
/// </summary>
|
||||
GSF_CLIP_FORMAT_WINDOWS_METAFILE = 3,
|
||||
|
||||
/// <summary>
|
||||
/// CF_DIB
|
||||
/// </summary>
|
||||
GSF_CLIP_FORMAT_WINDOWS_DIB = 8,
|
||||
|
||||
/// <summary>
|
||||
/// CF_ENHMETAFILE
|
||||
/// </summary>
|
||||
GSF_CLIP_FORMAT_WINDOWS_ENHANCED_METAFILE = 14
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: Can this be made internal?
|
||||
public class GsfClipData
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public GsfClipFormat Format { get; private set; }
|
||||
|
||||
public GsfBlob DataBlob { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Classes
|
||||
|
||||
private class FormatOffsetPair
|
||||
{
|
||||
public GsfClipFormatWindows Format { get; set; }
|
||||
|
||||
public long Offset { get; set; }
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfClipData() { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new GsfClipData object. This function acquires a reference to the
|
||||
/// <paramref name="data_blob"/>, so you should unref the blob on your own if you no longer need it
|
||||
/// directly.
|
||||
/// </summary>
|
||||
/// <param name="format">Format for the data inside the <paramref name="data_blob"/></param>
|
||||
/// <param name="data_blob">Object which holds the binary contents for the GsfClipData</param>
|
||||
/// <returns>A newly-created GsfClipData.</returns>
|
||||
public static GsfClipData Create(GsfClipFormat format, GsfBlob data_blob)
|
||||
{
|
||||
if (data_blob == null)
|
||||
return null;
|
||||
|
||||
return new GsfClipData
|
||||
{
|
||||
Format = format,
|
||||
DataBlob = data_blob,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Queries the Windows clipboard data format for a GsfClipData. The <paramref name="clip_data"/> must
|
||||
/// have been created with #GSF_CLIP_FORMAT_WINDOWS_CLIPBOARD.
|
||||
/// </summary>
|
||||
/// <param name="error">Location to store error, or NULL</param>
|
||||
/// <returns>A GsfClipFormatWindows value.</returns>
|
||||
public GsfClipFormatWindows GetWindowsClipboardFormat(ref Exception error)
|
||||
{
|
||||
if (error == null)
|
||||
return GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_ERROR;
|
||||
if (Format != GsfClipFormat.GSF_CLIP_FORMAT_WINDOWS_CLIPBOARD)
|
||||
return GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_ERROR;
|
||||
|
||||
long size = DataBlob.Size;
|
||||
if (size < 4)
|
||||
{
|
||||
error = new InvalidDataException("The clip_data is in Windows clipboard format, but it is smaller than the required 4 bytes.");
|
||||
return GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_ERROR;
|
||||
}
|
||||
|
||||
byte[] data = DataBlob.Data;
|
||||
uint value = GSF_LE_GET_GUINT32(data, 0);
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case (uint)GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_METAFILE:
|
||||
return CheckFormatWindows(GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_METAFILE, "Windows Metafile format", size, ref error);
|
||||
|
||||
case (uint)GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_DIB:
|
||||
case 2: /* CF_BITMAP */
|
||||
return CheckFormatWindows(GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_DIB, "Windows DIB or BITMAP format", size, ref error);
|
||||
|
||||
case (uint)GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_ENHANCED_METAFILE:
|
||||
return CheckFormatWindows(GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_ENHANCED_METAFILE, "Windows Enhanced Metafile format", size, ref error);
|
||||
|
||||
default:
|
||||
return GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries a pointer directly to the clipboard data of a GsfClipData. The
|
||||
/// resulting pointer is not necessarily the same data pointer that was passed to
|
||||
/// gsf_blob_new() prior to creating the <paramref name="clip_data"/>. For example, if the data is
|
||||
/// in GSF_CLIP_FORMAT_WINDOWS_CLIPBOARD format, then it will have extra header
|
||||
/// bytes in front of the actual metafile data. This function will skip over
|
||||
/// those header bytes if necessary and return a pointer to the "real" data.
|
||||
/// </summary>
|
||||
/// <param name="ret_size">Location to return the size of the returned data buffer.</param>
|
||||
/// <param name="error">Location to store error, or NULL.</param>
|
||||
/// <returns>
|
||||
/// Pointer to the real clipboard data. The size in bytes of this
|
||||
/// buffer is returned in the <paramref name="ret_size"/> argument.
|
||||
/// </returns>
|
||||
public byte[] PeekRealData(ref long ret_size, ref Exception error)
|
||||
{
|
||||
if (error == null)
|
||||
return null;
|
||||
|
||||
byte[] data = DataBlob.Data;
|
||||
|
||||
long offset;
|
||||
if (Format == GsfClipFormat.GSF_CLIP_FORMAT_WINDOWS_CLIPBOARD)
|
||||
{
|
||||
GsfClipFormatWindows win_format = GetWindowsClipboardFormat(ref error);
|
||||
if (win_format == GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_ERROR)
|
||||
return null;
|
||||
|
||||
// GetWindowsClipboardFormat() already did the size checks for us,
|
||||
// so we can jump to the offset right away without doing extra checks.
|
||||
|
||||
offset = GetWindowsClipboardDataOffset(win_format);
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
ret_size = DataBlob.Size - offset;
|
||||
return new ReadOnlySpan<byte>(data, (int)offset, (int)ret_size).ToArray();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private static void SetErrorMissingClipboardData(ref Exception error, string format_name, long at_least_size)
|
||||
{
|
||||
error = new InvalidDataException($"The clip_data is in {format_name}, but it is smaller than at least {at_least_size} bytes");
|
||||
}
|
||||
|
||||
private static long GetWindowsClipboardDataOffset(GsfClipFormatWindows format)
|
||||
{
|
||||
FormatOffsetPair[] pairs =
|
||||
{
|
||||
new FormatOffsetPair { Format = GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_UNKNOWN, Offset = 4 },
|
||||
new FormatOffsetPair { Format = GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_METAFILE, Offset = 12 },
|
||||
new FormatOffsetPair { Format = GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_DIB, Offset = 4 },
|
||||
|
||||
// FIXME: does this have a PACKEDMETA in front as well, similar to GSF_CLIP_FORMAT_WINDOWS_METAFILE?
|
||||
new FormatOffsetPair { Format = GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_ENHANCED_METAFILE, Offset = 4 }
|
||||
};
|
||||
|
||||
int num_pairs = pairs.Length;
|
||||
for (int i = 0; i < num_pairs; i++)
|
||||
{
|
||||
if (pairs[i].Format == format)
|
||||
return pairs[i].Offset;
|
||||
}
|
||||
|
||||
Console.Error.WriteLine("Should not have reached this point");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks that the specified blob size matches the expected size for the format.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The same format if the size is correct, or
|
||||
/// GSF_CLIP_FORMAT_WINDOWS_ERROR if the size is too small.
|
||||
/// </returns>
|
||||
private static GsfClipFormatWindows CheckFormatWindows(GsfClipFormatWindows format, string format_name, long blob_size, ref Exception error)
|
||||
{
|
||||
long offset = GetWindowsClipboardDataOffset(format);
|
||||
if (blob_size <= offset)
|
||||
{
|
||||
SetErrorMissingClipboardData(ref error, format_name, offset + 1);
|
||||
format = GsfClipFormatWindows.GSF_CLIP_FORMAT_WINDOWS_ERROR;
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
619
BurnOutSharp/External/libgsf/GsfDocMetaData.cs
vendored
619
BurnOutSharp/External/libgsf/GsfDocMetaData.cs
vendored
@@ -1,619 +0,0 @@
|
||||
/*
|
||||
* gsf-doc-meta-data.c:
|
||||
*
|
||||
* Copyright (C) 2002-2006 Dom Lachowicz (cinamod@hotmail.com)
|
||||
* Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using LibGSF.Input;
|
||||
using LibGSF.Output;
|
||||
using static LibGSF.GsfMSOleUtils;
|
||||
using static LibGSF.GsfUtils;
|
||||
|
||||
namespace LibGSF
|
||||
{
|
||||
// TODO: Can this be made internal?
|
||||
public class GsfDocProp
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public object Value { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optionally null
|
||||
/// </summary>
|
||||
public string LinkedTo { get; set; }
|
||||
|
||||
public uint RefCount { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfDocProp() { }
|
||||
|
||||
/// <param name="name">The name of the property.</param>
|
||||
/// <returns>A new GsfDocProp.</returns>
|
||||
public static GsfDocProp Create(string name)
|
||||
{
|
||||
if (name == null)
|
||||
return null;
|
||||
|
||||
return new GsfDocProp
|
||||
{
|
||||
Name = name,
|
||||
Value = null,
|
||||
LinkedTo = null,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Release the given property.
|
||||
/// </summary>
|
||||
public void Free()
|
||||
{
|
||||
RefCount--;
|
||||
if (RefCount == 0)
|
||||
{
|
||||
LinkedTo = null;
|
||||
if (Value != null)
|
||||
Value = null;
|
||||
|
||||
Name = null;
|
||||
}
|
||||
}
|
||||
|
||||
public GsfDocProp Reference()
|
||||
{
|
||||
RefCount++;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <returns>The current value of prop, and replaces it with <paramref name="val"/>.</returns>
|
||||
public object SwapValue(object val)
|
||||
{
|
||||
object old_val = Value;
|
||||
Value = val;
|
||||
return old_val;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A debugging utility to dump prop as text via Console
|
||||
/// </summary>
|
||||
/// <remarks>New in 1.14.2</remarks>
|
||||
public void Dump()
|
||||
{
|
||||
if (Value is List<GsfDocProp> va)
|
||||
{
|
||||
for (int i = 0; i < va.Count; i++)
|
||||
{
|
||||
Console.WriteLine($"\t[{i}] = Name: {va[i].Name}, Value: {va[i].Value}, Link: {va[i].LinkedTo}, Ref: {va[i].RefCount}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"\t= {Value}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
// TODO: Can this be made internal?
|
||||
public class GsfDocMetaData
|
||||
{
|
||||
#region Properties
|
||||
|
||||
internal Dictionary<string, GsfDocProp> Table = new Dictionary<string, GsfDocProp>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfDocMetaData() { }
|
||||
|
||||
/// <returns>A new metadata property collection</returns>
|
||||
public static GsfDocMetaData Create() => new GsfDocMetaData();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <returns>
|
||||
/// The property with <paramref name="name"/> in meta. The caller can
|
||||
/// modify the property value and link but not the name.
|
||||
/// </returns>
|
||||
public GsfDocProp Lookup(string name)
|
||||
{
|
||||
if (name == null)
|
||||
return null;
|
||||
|
||||
if (!Table.ContainsKey(name))
|
||||
return null;
|
||||
|
||||
return Table[name];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Take ownership of <paramref name="name"/> and <paramref name="value"/> and insert a property into meta.
|
||||
/// If a property exists with @name, it is replaced (The link is lost)
|
||||
/// </summary>
|
||||
/// <param name="name">The id.</param>
|
||||
public void Insert(string name, object value)
|
||||
{
|
||||
if (name == null)
|
||||
return;
|
||||
|
||||
GsfDocProp docProp = GsfDocProp.Create(name);
|
||||
docProp.Value = value;
|
||||
docProp.LinkedTo = null;
|
||||
docProp.RefCount = 1;
|
||||
|
||||
Table[name] = docProp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If <paramref name="name"/> does not exist in the collection, do nothing. If @name does exist,
|
||||
/// remove it and its value from the collection
|
||||
/// </summary>
|
||||
/// <param name="name">The non-null string name of the property</param>
|
||||
public void Remove(string name)
|
||||
{
|
||||
if (name == null)
|
||||
return;
|
||||
|
||||
if (!Table.ContainsKey(name))
|
||||
return;
|
||||
|
||||
Table.Remove(name);
|
||||
}
|
||||
|
||||
/// <returns>The property with <paramref name="name"/> in meta.</returns>
|
||||
public GsfDocProp Steal(string name)
|
||||
{
|
||||
if (name == null)
|
||||
return null;
|
||||
|
||||
if (!Table.ContainsKey(name))
|
||||
return null;
|
||||
|
||||
GsfDocProp prop = Table[name];
|
||||
if (prop != null)
|
||||
Table.Remove(name);
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
public void Store(GsfDocProp prop)
|
||||
{
|
||||
if (prop == null)
|
||||
return;
|
||||
|
||||
if (prop != Lookup(prop.Name))
|
||||
return;
|
||||
|
||||
Table[prop.Name] = prop;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Iterate through each (key, value) pair in this collection
|
||||
/// </summary>
|
||||
/// <param name="func">The function called once for each element in the collection</param>
|
||||
/// <param name="user_data">Any supplied user data</param>
|
||||
public void ForEach(Action<string, GsfDocProp, object> func, object user_data)
|
||||
{
|
||||
if (Table.Count == 0)
|
||||
return;
|
||||
|
||||
// Sort the pairs by property name in order to generate consistent files
|
||||
List<KeyValuePair<string, GsfDocProp>> pairs = Table.ToList();
|
||||
pairs.Sort((a, b) => DerefStrcmp(a.Key, b.Key));
|
||||
|
||||
for (int i = 0; i < pairs.Count; i++)
|
||||
{
|
||||
func(pairs[i].Key, pairs[i].Value, user_data);
|
||||
}
|
||||
}
|
||||
|
||||
/// <returns>The number of items in this collection</returns>
|
||||
public int Size() => Table.Count;
|
||||
|
||||
/// <summary>
|
||||
/// A debugging utility to dump the content of meta via Console
|
||||
/// </summary>
|
||||
public void Dump()
|
||||
{
|
||||
ForEach(PrintProperty, null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MS-OLE
|
||||
|
||||
/// <summary>
|
||||
/// Read a stream formated as a set of MS OLE properties from <paramref name="input"/> and store the
|
||||
/// results in <paramref name="accum"/>.
|
||||
/// </summary>
|
||||
/// <returns>an Exception if there was an error.</returns>
|
||||
/// <remarks>Since: 1.14.24</remarks>
|
||||
public Exception ReadFromMSOLE(GsfInput input)
|
||||
{
|
||||
GsfDocProp prop;
|
||||
|
||||
// http://bugzilla.gnome.org/show_bug.cgi?id=352055
|
||||
// psiwin generates files with empty property sections
|
||||
if (input.Size <= 0)
|
||||
return null;
|
||||
|
||||
byte[] data = input.Read(28, null);
|
||||
if (data == null)
|
||||
return new Exception("Unable to read MS property stream header");
|
||||
|
||||
/*
|
||||
* Validate the Property Set Header.
|
||||
* Format (bytes):
|
||||
* 00 - 01 Byte order 0xfffe
|
||||
* 02 - 03 Format 0
|
||||
* 04 - 05 OS Version high word is the OS
|
||||
* 06 - 07 low word is the OS version
|
||||
* 0 = win16
|
||||
* 1 = mac
|
||||
* 2 = win32
|
||||
* 08 - 23 Class Identifier Usually Format ID
|
||||
* 24 - 27 Section count Should be at least 1
|
||||
*/
|
||||
ushort os = GSF_LE_GET_GUINT16(data, 6);
|
||||
ushort version = GSF_LE_GET_GUINT16(data, 2);
|
||||
uint num_sections = BitConverter.ToUInt32(data, 24);
|
||||
|
||||
if (GSF_LE_GET_GUINT16(data, 0) != 0xfffe
|
||||
|| (version != 0 && version != 1)
|
||||
|| os > 2
|
||||
|| num_sections > input.Size / 20
|
||||
|| num_sections > 100) // Arbitrary sanity check
|
||||
{
|
||||
return new Exception("Invalid MS property stream header");
|
||||
}
|
||||
|
||||
// Extract the section info
|
||||
/*
|
||||
* The Format ID/Offset list follows.
|
||||
* Format:
|
||||
* 00 - 16 Section Name Format ID
|
||||
* 16 - 19 Section Offset The offset is the number of
|
||||
* bytes from the start of the
|
||||
* whole stream to where the
|
||||
* section begins.
|
||||
*/
|
||||
GsfMSOleMetaDataSection[] sections = new GsfMSOleMetaDataSection[num_sections];
|
||||
for (uint i = 0; i < num_sections; i++)
|
||||
{
|
||||
data = input.Read(20, null);
|
||||
if (data == null)
|
||||
return new Exception("Unable to read MS property stream header");
|
||||
|
||||
byte[] guid = new ReadOnlySpan<byte>(data, 0, 16).ToArray();
|
||||
if (guid.SequenceEqual(ComponentGUID))
|
||||
{
|
||||
sections[i].Type = GsfMSOLEMetaDataType.COMPONENT_PROP;
|
||||
}
|
||||
else if (guid.SequenceEqual(DocumentGUID))
|
||||
{
|
||||
sections[i].Type = GsfMSOLEMetaDataType.DOC_PROP;
|
||||
}
|
||||
else if (guid.SequenceEqual(UserGUID))
|
||||
{
|
||||
sections[i].Type = GsfMSOLEMetaDataType.USER_PROP;
|
||||
}
|
||||
else
|
||||
{
|
||||
sections[i].Type = GsfMSOLEMetaDataType.USER_PROP;
|
||||
Console.Error.WriteLine("Unknown property section type, treating it as USER");
|
||||
}
|
||||
|
||||
sections[i].Offset = BitConverter.ToUInt32(data, 16);
|
||||
}
|
||||
|
||||
/*
|
||||
* A section is the third part of the property set stream.
|
||||
* Format (bytes):
|
||||
* 00 - 03 Section size A byte count for the section (which is inclusive
|
||||
* of the byte count itself and should always be a
|
||||
* multiple of 4);
|
||||
* 04 - 07 Property count A count of the number of properties
|
||||
* 08 - xx An array of 32-bit Property ID/Offset pairs
|
||||
* yy - zz An array of Property Type indicators/Value pairs
|
||||
*/
|
||||
for (uint i = 0; i < num_sections; i++)
|
||||
{
|
||||
if (input.Seek(sections[i].Offset, SeekOrigin.Begin) || (data = input.Read(8, null)) == null)
|
||||
return new Exception("Invalid MS property section");
|
||||
|
||||
sections[i].IConvHandle = null;
|
||||
sections[i].CharSize = 1;
|
||||
sections[i].Dict = null;
|
||||
sections[i].Size = BitConverter.ToUInt32(data, 0); // Includes header
|
||||
sections[i].NumProps = BitConverter.ToUInt32(data, 4);
|
||||
|
||||
if (sections[i].NumProps <= 0)
|
||||
continue;
|
||||
|
||||
if (sections[i].NumProps > input.Remaining() / 8)
|
||||
return new Exception("Invalid MS property stream header or file truncated");
|
||||
|
||||
if (sections[i].Offset + sections[i].Size > input.Size)
|
||||
return new Exception("Invalid MS property stream header or file truncated");
|
||||
|
||||
/*
|
||||
* Get and save all the Property ID/Offset pairs.
|
||||
* Format (bytes):
|
||||
* 00 - 03 id Property ID
|
||||
* 04 - 07 offset The distance from the start of the section to the
|
||||
* start of the Property Type/Value pair.
|
||||
*/
|
||||
GsfMSOleMetaDataProp[] props = new GsfMSOleMetaDataProp[sections[i].NumProps];
|
||||
for (uint j = 0; j < sections[i].NumProps; j++)
|
||||
{
|
||||
if ((data = input.Read(8, null)) == null)
|
||||
return new Exception("Invalid MS property section");
|
||||
|
||||
props[j].Id = BitConverter.ToUInt32(data, 0);
|
||||
props[j].Offset = BitConverter.ToUInt32(data, 4);
|
||||
}
|
||||
|
||||
// FIXME: Should we check that ids are distinct?
|
||||
|
||||
// Order prop info by offset to facilitate bounds checking
|
||||
List<GsfMSOleMetaDataProp> tempProps = new List<GsfMSOleMetaDataProp>(props);
|
||||
tempProps.Sort(PropertyCompare);
|
||||
props = tempProps.ToArray();
|
||||
|
||||
// Sanity checks.
|
||||
for (uint j = 0; j < sections[i].NumProps; j++)
|
||||
{
|
||||
uint end = (uint)((j == sections[i].NumProps - 1) ? sections[i].Size : props[j + 1].Offset);
|
||||
if (props[j].Offset < 0 || props[j].Offset + 4 > end)
|
||||
return new Exception("Invalid MS property section");
|
||||
}
|
||||
|
||||
// Find and process the code page.
|
||||
// Property ID 1 is reserved as an indicator of the code page.
|
||||
sections[i].IConvHandle = null;
|
||||
sections[i].CharSize = 1;
|
||||
|
||||
for (uint j = 0; j < sections[i].NumProps; j++) // First codepage
|
||||
{
|
||||
if (props[j].Id == 1)
|
||||
{
|
||||
sections[i].PropertyRead(input, props, j, this);
|
||||
if ((prop = Lookup(GsfMetaNames.GSF_META_NAME_CODEPAGE)) != null)
|
||||
{
|
||||
object val = prop.Value;
|
||||
if (val != null && val is int)
|
||||
{
|
||||
int codepage = (int)val;
|
||||
sections[i].IConvHandle = Encoding.GetEncoding(codepage);
|
||||
sections[i].CharSize = (uint)CodePageCharSize(codepage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sections[i].IConvHandle == null)
|
||||
sections[i].IConvHandle = Encoding.GetEncoding(1252);
|
||||
|
||||
// Find and process the Property Set Dictionary
|
||||
// Property ID 0 is reserved as an indicator of the dictionary.
|
||||
// For User Defined Sections, Property ID 0 is NOT a dictionary.
|
||||
|
||||
for (uint j = 0; j < sections[i].NumProps; j++) // The dictionary
|
||||
{
|
||||
if (props[j].Id == 0)
|
||||
sections[i].PropertyRead(input, props, j, this);
|
||||
}
|
||||
|
||||
// Process all the properties
|
||||
for (uint j = 0; j < sections[i].NumProps; j++) // The rest
|
||||
{
|
||||
if (props[j].Id > 1)
|
||||
sections[i].PropertyRead(input, props, j, this);
|
||||
}
|
||||
|
||||
sections[i].IConvHandle = null;
|
||||
if (sections[i].Dict != null)
|
||||
sections[i].Dict = null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary></summary>
|
||||
/// <param name="doc_not_component">A kludge to differentiate DocumentSummary from Summary</param>
|
||||
/// <returns>True on success</returns>
|
||||
/// <remarks>Since: 1.14.24</remarks>
|
||||
public bool WriteToMSOLE(GsfOutput output, bool doc_not_component)
|
||||
{
|
||||
byte[] header =
|
||||
{
|
||||
0xfe, 0xff, // Byte order
|
||||
0, 0, // Format
|
||||
0x04, 0x0a, // OS : XP == 0xA04
|
||||
0x02, 0x00, // Win32 == 2
|
||||
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // Clasid = 0
|
||||
};
|
||||
|
||||
bool success = false;
|
||||
byte[] buf = new byte[4];
|
||||
|
||||
const int default_codepage = 1252;
|
||||
|
||||
WritePropState state = new WritePropState
|
||||
{
|
||||
CodePage = 0,
|
||||
IConvHandle = null,
|
||||
CharSize = 1,
|
||||
Output = output,
|
||||
Dict = null,
|
||||
BuiltIn = new WritePropStatePropList
|
||||
{
|
||||
Count = 1, // Codepage
|
||||
Props = null,
|
||||
},
|
||||
User = new WritePropStatePropList
|
||||
{
|
||||
Count = 2, // Codepage and Dictionary
|
||||
Props = null,
|
||||
},
|
||||
DocNotComponent = doc_not_component,
|
||||
};
|
||||
|
||||
foreach (var prop in Table)
|
||||
{
|
||||
state.CountProperties(prop.Key, prop.Value);
|
||||
}
|
||||
|
||||
state.IConvHandle = Encoding.GetEncoding(default_codepage);
|
||||
if (state.CodePage == 0)
|
||||
{
|
||||
state.GuessCodePage(false);
|
||||
if (state.Dict != null)
|
||||
state.GuessCodePage(true);
|
||||
|
||||
if (state.CodePage == 0)
|
||||
state.CodePage = default_codepage;
|
||||
}
|
||||
|
||||
state.IConvHandle = Encoding.GetEncoding(state.CodePage);
|
||||
state.CharSize = CodePageCharSize(state.CodePage);
|
||||
|
||||
// Write stream header
|
||||
GSF_LE_SET_GUINT32(buf, 0, (uint)(state.Dict != null ? 2 : 1));
|
||||
if (!output.Write(header.Length, header) || !output.Write(4, buf))
|
||||
{
|
||||
state.IConvHandle = null;
|
||||
state.BuiltIn.Props = null;
|
||||
state.User.Props = null;
|
||||
state.Dict = null;
|
||||
return success;
|
||||
}
|
||||
|
||||
// Write section header(s)
|
||||
GSF_LE_SET_GUINT32(buf, 0, (uint)(state.Dict != null ? 0x44 : 0x30));
|
||||
if (!output.Write(16, doc_not_component ? DocumentGUID : ComponentGUID) || !output.Write(4, buf))
|
||||
{
|
||||
state.IConvHandle = null;
|
||||
state.BuiltIn.Props = null;
|
||||
state.User.Props = null;
|
||||
state.Dict = null;
|
||||
return success;
|
||||
}
|
||||
|
||||
if (state.Dict != null)
|
||||
{
|
||||
GSF_LE_SET_GUINT32(buf, 0, 0);
|
||||
if (!output.Write(UserGUID.Length, UserGUID) || !output.Write(4, buf)) // Bogus position, fix it later
|
||||
{
|
||||
state.IConvHandle = null;
|
||||
state.BuiltIn.Props = null;
|
||||
state.User.Props = null;
|
||||
state.Dict = null;
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
// Write section(s)
|
||||
if (!state.WriteSection(false))
|
||||
{
|
||||
state.IConvHandle = null;
|
||||
state.BuiltIn.Props = null;
|
||||
state.User.Props = null;
|
||||
state.Dict = null;
|
||||
return success;
|
||||
}
|
||||
|
||||
if (state.Dict != null)
|
||||
{
|
||||
long baseOffset = state.Output.CurrentOffset;
|
||||
GSF_LE_SET_GUINT32(buf, 0, (uint)baseOffset);
|
||||
if (!state.Output.Seek(0x40, SeekOrigin.Begin)
|
||||
|| !output.Write(4, buf)
|
||||
|| !state.Output.Seek(0, SeekOrigin.End)
|
||||
|| !state.WriteSection(true))
|
||||
{
|
||||
state.IConvHandle = null;
|
||||
state.BuiltIn.Props = null;
|
||||
state.User.Props = null;
|
||||
state.Dict = null;
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
state.IConvHandle = null;
|
||||
state.BuiltIn.Props = null;
|
||||
state.User.Props = null;
|
||||
state.Dict = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private static int DerefStrcmp(string a, string b) => a.CompareTo(b);
|
||||
|
||||
private static void PrintProperty(string name, GsfDocProp prop, object user_data)
|
||||
{
|
||||
if (prop.LinkedTo != null)
|
||||
Console.WriteLine($"prop '{name}' LINKED TO -> {prop.LinkedTo}");
|
||||
else
|
||||
Console.WriteLine($"prop '{name}'");
|
||||
|
||||
prop.Dump();
|
||||
}
|
||||
|
||||
private int CodePageCharSize(int codepage) => (codepage == 1200 || codepage == 1201 ? 2 : 1);
|
||||
|
||||
private static int PropertyCompare(GsfMSOleMetaDataProp prop_a, GsfMSOleMetaDataProp prop_b)
|
||||
{
|
||||
if (prop_a.Offset < prop_b.Offset)
|
||||
return -1;
|
||||
else if (prop_a.Offset > prop_b.Offset)
|
||||
return +1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
864
BurnOutSharp/External/libgsf/GsfLibXML.cs
vendored
864
BurnOutSharp/External/libgsf/GsfLibXML.cs
vendored
@@ -1,864 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-libxml.c :
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Schema;
|
||||
using LibGSF.Input;
|
||||
using LibGSF.Output;
|
||||
|
||||
namespace LibGSF
|
||||
{
|
||||
#region Enums
|
||||
|
||||
/// <summary>
|
||||
/// Controls the handling of character data within a parser node.
|
||||
/// </summary>
|
||||
public enum GsfXMLContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Node has no cstr contents
|
||||
/// </summary>
|
||||
GSF_XML_NO_CONTENT = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Node has cstr contents
|
||||
/// </summary>
|
||||
GSF_XML_CONTENT,
|
||||
|
||||
/// <summary>
|
||||
/// Node has contents that is shared with children
|
||||
/// </summary>
|
||||
GSF_XML_SHARED_CONTENT,
|
||||
|
||||
/// <summary>
|
||||
/// Node is second or later occurrence
|
||||
/// </summary>
|
||||
GSF_XML_2ND
|
||||
}
|
||||
|
||||
public enum GsfXMLOutState
|
||||
{
|
||||
GSF_XML_OUT_NOCONTENT,
|
||||
GSF_XML_OUT_CHILD,
|
||||
GSF_XML_OUT_CHILD_PRETTY,
|
||||
GSF_XML_OUT_CONTENT
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Delegates
|
||||
|
||||
public delegate void GsfXMLInExtDtor(GsfXMLIn xin, object old_state);
|
||||
|
||||
public delegate bool GsfXMLInUnknownFunc(GsfXMLIn xin, string elem, string[] attrs);
|
||||
|
||||
public delegate bool GsfXMLProbeFunc(string name, string prefix, string URI, int nb_namespaces, string[] namespaces, int nb_attributes, int nb_defaulted, string[] attributes);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Classes
|
||||
|
||||
// TODO: Figure out what the structure of this is
|
||||
public class GsfXMLBlob
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class GsfXMLIn : GsfInput
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// User data
|
||||
/// </summary>
|
||||
public object UserState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The current node content
|
||||
/// </summary>
|
||||
public string Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current document being parsed
|
||||
/// </summary>
|
||||
public XmlDocument Doc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current node (not on the stack)
|
||||
/// </summary>
|
||||
public XmlNode Node { get; set; }
|
||||
|
||||
public Stack<XmlNode> NodeStack { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Properties
|
||||
|
||||
public GsfInput Input { get; set; }
|
||||
|
||||
public Stack<string> ContentsStack { get; internal set; }
|
||||
|
||||
public bool Initialized { get; internal set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Take the first node from <paramref name="doc"/> as the current node and call its start handler.
|
||||
/// </summary>
|
||||
/// <param name="new_state">Arbitrary content for the parser</param>
|
||||
public void PushState(XmlDocument doc, object new_state, GsfXMLInExtDtor dtor, string[] attrs)
|
||||
{
|
||||
if (doc == null)
|
||||
return;
|
||||
if (doc.DocumentType == null)
|
||||
return;
|
||||
|
||||
// TODO: Figure out how to define Start here
|
||||
PushChild(doc.FirstChild, attrs, null);
|
||||
}
|
||||
|
||||
public void StartElement(string name, string ns)
|
||||
{
|
||||
if ((name != null && Node.Name != name) || (ns != null && Node.NamespaceURI != ns))
|
||||
return;
|
||||
|
||||
Node = Node.NextSibling;
|
||||
}
|
||||
|
||||
public void EndElement()
|
||||
{
|
||||
if (Node.NodeType != XmlNodeType.EndElement)
|
||||
throw new XmlException();
|
||||
|
||||
Node = Node.NextSibling;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This function will not be called when parsing an empty document.
|
||||
/// </summary>
|
||||
public void StartDocument()
|
||||
{
|
||||
Initialized = true;
|
||||
Node = Doc.FirstChild;
|
||||
NodeStack = new Stack<XmlNode>();
|
||||
ContentsStack = new Stack<string>();
|
||||
}
|
||||
|
||||
public void EndDocument()
|
||||
{
|
||||
Content = null;
|
||||
if (Initialized)
|
||||
{
|
||||
NodeStack = null;
|
||||
Initialized = false;
|
||||
|
||||
if (Node != Doc.DocumentType)
|
||||
Console.Error.WriteLine("Document likely damaged.");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private string NodeName(XmlNode node) => (node.Name != null) ? node.Name : "{catch all)}";
|
||||
|
||||
private void PushChild(XmlNode node, string[] attrs, Action<GsfXMLIn, string[]> start)
|
||||
{
|
||||
if (node.InnerText != null)
|
||||
{
|
||||
if (Content.Length != 0)
|
||||
{
|
||||
ContentsStack.Push(Content);
|
||||
Content = new string('\0', 128);
|
||||
}
|
||||
else
|
||||
{
|
||||
ContentsStack.Push(null);
|
||||
}
|
||||
}
|
||||
|
||||
NodeStack.Push(Node);
|
||||
Node = node;
|
||||
|
||||
start?.Invoke(this, attrs);
|
||||
}
|
||||
|
||||
#endregion
|
||||
};
|
||||
|
||||
public class GsfXMLInParser : XmlReader
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Parent GsfXMLIn for reading
|
||||
/// </summary>
|
||||
public GsfXMLIn Parent { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current user state
|
||||
/// </summary>
|
||||
public GsfXMLIn UserState { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal reader instance for unhandled functionality
|
||||
/// </summary>
|
||||
private readonly XmlReader inst;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Override Properties
|
||||
|
||||
public override int AttributeCount => inst.AttributeCount;
|
||||
|
||||
public override string BaseURI => inst.BaseURI;
|
||||
|
||||
public override int Depth => inst.Depth;
|
||||
|
||||
public override bool EOF => inst.EOF;
|
||||
|
||||
public override bool IsEmptyElement => inst.IsEmptyElement;
|
||||
|
||||
public override string LocalName => inst.LocalName;
|
||||
|
||||
public override string NamespaceURI => inst.NamespaceURI;
|
||||
|
||||
public override XmlNameTable NameTable => inst.NameTable;
|
||||
|
||||
public override XmlNodeType NodeType => inst.NodeType;
|
||||
|
||||
public override string Prefix => inst.Prefix;
|
||||
|
||||
public override ReadState ReadState => inst.ReadState;
|
||||
|
||||
public override string Value => inst.Value;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public GsfXMLInParser(GsfXMLIn parent)
|
||||
{
|
||||
if (parent == null)
|
||||
return;
|
||||
|
||||
Parent = parent;
|
||||
|
||||
XmlReaderSettings settings = new XmlReaderSettings
|
||||
{
|
||||
ValidationType = ValidationType.None,
|
||||
ValidationFlags = XmlSchemaValidationFlags.AllowXmlAttributes,
|
||||
};
|
||||
|
||||
inst = Create(inputUri: null, settings);
|
||||
|
||||
Parent.StartDocument();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfXMLInParser()
|
||||
{
|
||||
Parent.EndDocument();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region XmlReader Custom Implementation
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void ReadStartElement() => Parent.StartElement(null, null);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void ReadStartElement(string name) => Parent.StartElement(name, null);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void ReadStartElement(string localname, string ns) => Parent.StartElement(localname, ns);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void ReadEndElement() => Parent.EndElement();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ReadElementContentAsString() => Parent.Node.InnerText;
|
||||
|
||||
#endregion
|
||||
|
||||
#region XmlReader Default Implementation
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetAttribute(int i) => inst.GetAttribute(i);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetAttribute(string name) => inst.GetAttribute(name);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetAttribute(string name, string namespaceURI) => inst.GetAttribute(name, namespaceURI);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string LookupNamespace(string prefix) => inst.LookupNamespace(prefix);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool MoveToAttribute(string name) => inst.MoveToAttribute(name);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool MoveToAttribute(string name, string ns) => inst.MoveToAttribute(name, ns);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool MoveToElement() => inst.MoveToElement();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool MoveToFirstAttribute() => inst.MoveToFirstAttribute();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool MoveToNextAttribute() => inst.MoveToNextAttribute();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Read() => inst.Read();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool ReadAttributeValue() => inst.ReadAttributeValue();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void ResolveEntity() => inst.ResolveEntity();
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class GsfXMLOut
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public GsfOutput Output { get; set; } = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Properties
|
||||
|
||||
public string DocType { get; private set; } = null;
|
||||
|
||||
public Stack<string> Stack { get; private set; } = new Stack<string>();
|
||||
|
||||
public GsfXMLOutState State { get; private set; } = GsfXMLOutState.GSF_XML_OUT_CHILD;
|
||||
|
||||
public int Indent { get; private set; } = 0;
|
||||
|
||||
public bool PrettyPrint { get; private set; } = true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
protected GsfXMLOut() { }
|
||||
|
||||
/// <summary>
|
||||
/// Create an XML output stream.
|
||||
/// </summary>
|
||||
public static GsfXMLOut Create(GsfOutput output)
|
||||
{
|
||||
if (output == null)
|
||||
return null;
|
||||
|
||||
return new GsfXMLOut()
|
||||
{
|
||||
Output = output
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Write the document start
|
||||
/// </summary>
|
||||
public void StartDocument()
|
||||
{
|
||||
string header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
Output.Write(header.Length, Encoding.UTF8.GetBytes(header));
|
||||
if (DocType != null)
|
||||
Output.PutString(DocType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Output a start element <paramref name="id"/>, if necessary preceeded by an XML declaration.
|
||||
/// </summary>
|
||||
/// <param name="id">Element name</param>
|
||||
public void StartElement(string id)
|
||||
{
|
||||
if (id == null)
|
||||
return;
|
||||
|
||||
if (State == GsfXMLOutState.GSF_XML_OUT_NOCONTENT)
|
||||
{
|
||||
if (PrettyPrint)
|
||||
Output.Write(2, Encoding.UTF8.GetBytes(">\n"));
|
||||
else
|
||||
Output.Write(1, Encoding.UTF8.GetBytes(">"));
|
||||
}
|
||||
|
||||
OutIndent();
|
||||
Output.PrintF($"<{id}");
|
||||
|
||||
Stack.Push(id);
|
||||
Indent++;
|
||||
State = GsfXMLOutState.GSF_XML_OUT_NOCONTENT;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes/ends an XML element.
|
||||
/// </summary>
|
||||
/// <returns>The element that has been closed.</returns>
|
||||
public string EndElement()
|
||||
{
|
||||
if (Stack == null || Stack.Count == 0)
|
||||
return null;
|
||||
|
||||
string id = Stack.Pop();
|
||||
Indent--;
|
||||
switch (State)
|
||||
{
|
||||
case GsfXMLOutState.GSF_XML_OUT_NOCONTENT:
|
||||
if (PrettyPrint)
|
||||
Output.Write(3, Encoding.UTF8.GetBytes("/>\n"));
|
||||
else
|
||||
Output.Write(2, Encoding.UTF8.GetBytes("/>"));
|
||||
|
||||
break;
|
||||
|
||||
case GsfXMLOutState.GSF_XML_OUT_CHILD_PRETTY:
|
||||
OutIndent();
|
||||
if (PrettyPrint)
|
||||
Output.PrintF($"</{id}>\n");
|
||||
else
|
||||
Output.PrintF($"</{id}>");
|
||||
|
||||
break;
|
||||
|
||||
case GsfXMLOutState.GSF_XML_OUT_CHILD:
|
||||
case GsfXMLOutState.GSF_XML_OUT_CONTENT:
|
||||
if (PrettyPrint)
|
||||
Output.PrintF($"</{id}>\n");
|
||||
else
|
||||
Output.PrintF($"</{id}>");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
State = PrettyPrint ? GsfXMLOutState.GSF_XML_OUT_CHILD_PRETTY : GsfXMLOutState.GSF_XML_OUT_CHILD;
|
||||
return id;
|
||||
}
|
||||
|
||||
/// <param name="pp">New state of pretty-print flag.</param>
|
||||
/// <returns>The previous state of the pretty-print flag.</returns>
|
||||
public bool SetPrettyPrint(bool pp)
|
||||
{
|
||||
bool res = PrettyPrint;
|
||||
if (pp != res)
|
||||
PrettyPrint = pp;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convenience routine to output a simple <paramref name="id"/> element with content <paramref name="content"/>.
|
||||
/// </summary>
|
||||
/// <param name="id">Element name</param>
|
||||
/// <param name="content">Content of the element</param>
|
||||
public void OutSimpleElement(string id, string content)
|
||||
{
|
||||
StartElement(id);
|
||||
if (content != null)
|
||||
AddString(null, content);
|
||||
|
||||
EndElement();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convenience routine to output an element <paramref name="id"/> with integer value <paramref name="val"/>.
|
||||
/// </summary>
|
||||
/// <param name="id">Element name</param>
|
||||
/// <param name="val">Element value</param>
|
||||
public void OutSimpleSignedElement(string id, long val)
|
||||
{
|
||||
StartElement(id);
|
||||
AddSigned(null, val);
|
||||
EndElement();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convenience routine to output an element <paramref name="id"/> with float value <paramref name="val"/> using
|
||||
/// <paramref name="precision"/> significant digits.
|
||||
/// </summary>
|
||||
/// <param name="id">Element name</param>
|
||||
/// <param name="val">Element value</param>
|
||||
/// <param name="precision">The number of significant digits to use, -1 meaning "enough".</param>
|
||||
public void OutSimpleFloatElement(string id, double val, int precision)
|
||||
{
|
||||
StartElement(id);
|
||||
AddFloat(null, val, precision);
|
||||
EndElement();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dump <paramref name="valUtf8"/> to an attribute named @id without checking to see if
|
||||
/// the content needs escaping. A useful performance enhancement when
|
||||
/// the application knows that structure of the content well. If
|
||||
/// <paramref name="valUtf8"/> is null do nothing (no warning, no output)
|
||||
/// </summary>
|
||||
/// <param name="id">Tag id, or null for node content</param>
|
||||
/// <param name="valUtf8">A UTF-8 encoded string to export</param>
|
||||
public void AddStringUnchecked(string id, string valUtf8)
|
||||
{
|
||||
if (valUtf8 == null)
|
||||
return;
|
||||
|
||||
if (id == null)
|
||||
{
|
||||
CloseTagIfNecessary();
|
||||
Output.Write(valUtf8.Length, Encoding.UTF8.GetBytes(valUtf8));
|
||||
}
|
||||
else
|
||||
{
|
||||
Output.PrintF($" {id}=\"{valUtf8}\"");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dump <paramref name="valUtf8"/> to an attribute named <paramref name="id"/> or as the nodes content escaping
|
||||
/// characters as necessary. If @valUtf8 is %null do nothing (no warning, no
|
||||
/// output)
|
||||
/// </summary>
|
||||
/// <param name="id">Tag id, or null for node content</param>
|
||||
/// <param name="valUtf8">A UTF-8 encoded string</param>
|
||||
public void AddString(string id, string valUtf8)
|
||||
{
|
||||
if (valUtf8 == null)
|
||||
return;
|
||||
|
||||
int cur = 0; // valUtf8[0];
|
||||
int start = 0; // valUtf8[0];
|
||||
|
||||
if (id == null)
|
||||
CloseTagIfNecessary();
|
||||
else
|
||||
Output.PrintF($" {id}=\"");
|
||||
|
||||
while (valUtf8[cur] != '\0')
|
||||
{
|
||||
if (valUtf8[cur] == '<')
|
||||
{
|
||||
if (cur != start)
|
||||
Output.Write(cur - start, Encoding.UTF8.GetBytes(valUtf8));
|
||||
|
||||
start = ++cur;
|
||||
Output.Write(4, Encoding.UTF8.GetBytes("<"));
|
||||
}
|
||||
else if (valUtf8[cur] == '>')
|
||||
{
|
||||
if (cur != start)
|
||||
Output.Write(cur - start, Encoding.UTF8.GetBytes(valUtf8));
|
||||
|
||||
start = ++cur;
|
||||
Output.Write(4, Encoding.UTF8.GetBytes(">"));
|
||||
}
|
||||
else if (valUtf8[cur] == '&')
|
||||
{
|
||||
if (cur != start)
|
||||
Output.Write(cur - start, Encoding.UTF8.GetBytes(valUtf8));
|
||||
|
||||
start = ++cur;
|
||||
Output.Write(5, Encoding.UTF8.GetBytes("&"));
|
||||
}
|
||||
else if (valUtf8[cur] == '"')
|
||||
{
|
||||
if (cur != start)
|
||||
Output.Write(cur - start, Encoding.UTF8.GetBytes(valUtf8));
|
||||
|
||||
start = ++cur;
|
||||
Output.Write(6, Encoding.UTF8.GetBytes("""));
|
||||
}
|
||||
else if ((valUtf8[cur] == '\n' || valUtf8[cur] == '\r' || valUtf8[cur] == '\t') && id != null)
|
||||
{
|
||||
string tempBuf = $"&#{(int)valUtf8[cur]};";
|
||||
byte[] buf = Encoding.UTF8.GetBytes(tempBuf);
|
||||
|
||||
if (cur != start)
|
||||
Output.Write(cur - start, Encoding.UTF8.GetBytes(valUtf8));
|
||||
|
||||
start = ++cur;
|
||||
Output.Write(buf.Length, buf);
|
||||
}
|
||||
else if ((valUtf8[cur] >= 0x20 && valUtf8[cur] != 0x7f) || (valUtf8[cur] == '\n' || valUtf8[cur] == '\r' || valUtf8[cur] == '\t'))
|
||||
{
|
||||
cur++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is immensely pathetic, but XML 1.0 does not
|
||||
// allow certain characters to be encoded. XML 1.1
|
||||
// does allow this, but libxml2 does not support it.
|
||||
Console.Error.WriteLine($"Unknown char {valUtf8[cur]} in string");
|
||||
|
||||
if (cur != start)
|
||||
Output.Write(cur - start, Encoding.UTF8.GetBytes(valUtf8));
|
||||
|
||||
start = ++cur;
|
||||
}
|
||||
}
|
||||
|
||||
if (cur != start)
|
||||
Output.Write(cur - start, Encoding.UTF8.GetBytes(valUtf8));
|
||||
|
||||
if (id != null)
|
||||
Output.Write(1, Encoding.UTF8.GetBytes("\""));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dump <paramref name="len"/> bytes in <paramref name="data"/> into the content of node <paramref name="id"/> using base64
|
||||
/// </summary>
|
||||
/// <param name="id">Tag id, or null for node content</param>
|
||||
/// <param name="data">Data to be written</param>
|
||||
/// <param name="len">Length of data</param>
|
||||
public void AddBase64(string id, byte[] data, int len) => AddStringUnchecked(id, Convert.ToBase64String(data, 0, len));
|
||||
|
||||
/// <summary>
|
||||
/// Dump boolean value <paramref name="val"/> to an attribute named <paramref name="id"/> or as the nodes content
|
||||
/// </summary>
|
||||
/// <param name="id">Tag id, or %null for node content</param>
|
||||
/// <param name="val">A boolean</param>
|
||||
/// <remarks>Use '1' or '0' to simplify import</remarks>
|
||||
public void AddBool(string id, bool val) => AddStringUnchecked(id, val ? "1" : "0");
|
||||
|
||||
/// <summary>
|
||||
/// Dump integer value <paramref name="val"/> to an attribute named <paramref name="id"/> or as the nodes content
|
||||
/// </summary>
|
||||
/// <param name="id">Tag id, or null for node content</param>
|
||||
/// <param name="val">The value</param>
|
||||
public void AddSigned(string id, long val) => AddStringUnchecked(id, val.ToString());
|
||||
|
||||
/// <summary>
|
||||
/// Dump unsigned integer value <paramref name="val"/> to an attribute named <paramref name="id"/> or as the nodes
|
||||
/// </summary>
|
||||
/// <param name="id">Tag id, or null for node content</param>
|
||||
/// <param name="val">The value</param>
|
||||
public void AddUnsigned(string id, ulong val) => AddStringUnchecked(id, val.ToString());
|
||||
|
||||
/// <summary>
|
||||
/// Dump float value <paramref name="val"/> to an attribute named <paramref name="id"/> or as the nodes
|
||||
/// content with precision <paramref name="precision"/>. The number will be formattted
|
||||
/// according to the "C" locale.
|
||||
/// </summary>
|
||||
/// <param name="id">Tag id, or null for node content</param>
|
||||
/// <param name="val">The value</param>
|
||||
/// <param name="precision">The number of significant digits to use, -1 meaning "enough".</param>
|
||||
public void AddFloat(string id, double val, int precision)
|
||||
{
|
||||
if (precision < 0 || precision > 17)
|
||||
AddStringUnchecked(id, val.ToString());
|
||||
else
|
||||
AddStringUnchecked(id, val.ToString($"F{precision}"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dump Color <paramref name="r"/>.<paramref name="g"/>.<paramref name="b"/> to an attribute named <paramref name="id"/> or as the nodes content
|
||||
/// </summary>
|
||||
/// <param name="id">Tag id, or null for node content</param>
|
||||
/// <param name="r">Red value</param>
|
||||
/// <param name="g">Green value</param>
|
||||
/// <param name="b">Blue value</param>
|
||||
public void AddColor(string id, uint r, uint g, uint b)
|
||||
{
|
||||
string buf = $"{r:X}:{g:X}:{b:X}\0";
|
||||
AddStringUnchecked(id, buf);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Output the value of <paramref name="val"/> as a string. Does NOT store any type information
|
||||
/// with the string, just the value.
|
||||
/// </summary>
|
||||
/// <param name="id">Tag id, or null for node content</param>
|
||||
public void AddValue(string id, object val)
|
||||
{
|
||||
if (val == null)
|
||||
return;
|
||||
|
||||
if (val is char)
|
||||
{
|
||||
// FIXME: What if we are in 0x80-0xff?
|
||||
AddString(id, $"{(char)val}\0");
|
||||
}
|
||||
else if (val is byte)
|
||||
{
|
||||
// FIXME: What if we are in 0x80-0xff?
|
||||
AddString(id, $"{(byte)val}\0");
|
||||
}
|
||||
else if (val is bool)
|
||||
{
|
||||
AddString(id, (bool)val ? "t" : "f");
|
||||
}
|
||||
else if (val is int)
|
||||
{
|
||||
AddSigned(id, (int)val);
|
||||
}
|
||||
else if (val is uint)
|
||||
{
|
||||
AddUnsigned(id, (uint)val);
|
||||
}
|
||||
else if (val is long)
|
||||
{
|
||||
AddSigned(id, (long)val);
|
||||
}
|
||||
else if (val is ulong)
|
||||
{
|
||||
AddUnsigned(id, (ulong)val);
|
||||
}
|
||||
else if (val is float)
|
||||
{
|
||||
AddFloat(id, (float)val, -1);
|
||||
}
|
||||
else if (val is double)
|
||||
{
|
||||
AddFloat(id, (double)val, -1);
|
||||
}
|
||||
else if (val is string)
|
||||
{
|
||||
AddString(id, (string)val);
|
||||
}
|
||||
else if (val is DateTime)
|
||||
{
|
||||
DateTime ts = (DateTime)val;
|
||||
string str = ts.ToString("yyyy-MM-dd hh:mm:ss");
|
||||
AddString(id, str);
|
||||
}
|
||||
|
||||
// FIXME FIXME FIXME Add some error checking
|
||||
}
|
||||
|
||||
public void SetOutput(GsfOutput output)
|
||||
{
|
||||
if (output.Wrap(this))
|
||||
Output = output;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private void OutIndent()
|
||||
{
|
||||
if (!PrettyPrint)
|
||||
return;
|
||||
|
||||
// 2-space indent steps
|
||||
for (int i = Indent; i > 0; i--)
|
||||
{
|
||||
Output.Write(2, Encoding.UTF8.GetBytes(" "));
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseTagIfNecessary()
|
||||
{
|
||||
if (State == GsfXMLOutState.GSF_XML_OUT_NOCONTENT)
|
||||
{
|
||||
State = GsfXMLOutState.GSF_XML_OUT_CONTENT;
|
||||
Output.Write(1, new byte[] { (byte)'>' });
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class GsfLibXML
|
||||
{
|
||||
/// <summary>
|
||||
/// Try to parse <paramref name="str"/> as a value of type <paramref name="t"/> into <paramref name="res"/>.
|
||||
/// </summary>
|
||||
/// <param name="res">Result value</param>
|
||||
/// <param name="t">Type of data</param>
|
||||
/// <param name="str">Value string</param>
|
||||
/// <returns>
|
||||
/// True when parsing of <paramref name="str"/> as a value of type <paramref name="t"/> was succesfull;
|
||||
/// false otherwise.
|
||||
/// </returns>
|
||||
public static bool ValueFromString(ref object res, Type t, string str)
|
||||
{
|
||||
if (str == null)
|
||||
return false;
|
||||
|
||||
res = null;
|
||||
|
||||
// Handle nullable DateTime
|
||||
if (t == typeof(DateTime))
|
||||
t = typeof(DateTime?);
|
||||
|
||||
// If the passed-in type is derived from G_TYPE_ENUM
|
||||
// or G_TYPE_FLAGS, we cannot switch on its type
|
||||
// because we don't know its GType at compile time.
|
||||
// We just pretend to have a G_TYPE_ENUM/G_TYPE_FLAGS.
|
||||
if (t.IsEnum)
|
||||
t = t.GetEnumUnderlyingType();
|
||||
|
||||
if (t == typeof(char))
|
||||
res = str[0];
|
||||
else if (t == typeof(byte))
|
||||
res = (byte)str[0];
|
||||
else if (t == typeof(bool))
|
||||
res = (char.ToLower(str[0]) == 't' || char.ToLower(str[0]) == 'y' || str[0] == '0');
|
||||
else if (t == typeof(int))
|
||||
res = int.Parse(str);
|
||||
else if (t == typeof(uint))
|
||||
res = uint.Parse(str);
|
||||
else if (t == typeof(long))
|
||||
res = long.Parse(str);
|
||||
else if (t == typeof(ulong))
|
||||
res = ulong.Parse(str);
|
||||
// TODO: Handle enum and flag strings
|
||||
else if (t == typeof(float))
|
||||
res = float.Parse(str);
|
||||
else if (t == typeof(double))
|
||||
res = double.Parse(str);
|
||||
else if (t == typeof(string))
|
||||
res = str;
|
||||
else if (t == typeof(DateTime?))
|
||||
res = DateTime.Parse(str);
|
||||
else
|
||||
Console.Error.WriteLine($"ValueFromString(): Don't know how to handle type '{t.Name}'");
|
||||
|
||||
return res != null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
663
BurnOutSharp/External/libgsf/GsfMSOleImpl.cs
vendored
663
BurnOutSharp/External/libgsf/GsfMSOleImpl.cs
vendored
@@ -1,663 +0,0 @@
|
||||
/* THIS IS NOT INSTALLED */
|
||||
|
||||
/*
|
||||
* gsf-MSOLE-impl.h:
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using static LibGSF.GsfUtils;
|
||||
|
||||
namespace LibGSF
|
||||
{
|
||||
#region Enums
|
||||
|
||||
internal enum DIRENT_TYPE : byte
|
||||
{
|
||||
DIRENT_TYPE_INVALID = 0,
|
||||
DIRENT_TYPE_DIR = 1,
|
||||
DIRENT_TYPE_FILE = 2,
|
||||
DIRENT_TYPE_LOCKBYTES = 3,
|
||||
DIRENT_TYPE_PROPERTY = 4,
|
||||
DIRENT_TYPE_ROOTDIR = 5,
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Classes
|
||||
|
||||
/// <summary>
|
||||
/// MS-OLE Header (.msi)
|
||||
/// </summary>
|
||||
internal class MSOleHeader
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// Independent of big block size size
|
||||
/// </summary>
|
||||
public const int OLE_HEADER_SIZE = 0x200;
|
||||
|
||||
/// <summary>
|
||||
/// OLE Signature as a byte array
|
||||
/// </summary>
|
||||
public static readonly byte[] SIGNATURE_BYTES = { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 };
|
||||
|
||||
/// <summary>
|
||||
/// OLE Signature as an unsigned Int64
|
||||
/// </summary>
|
||||
public const ulong SIGNATURE_VALUE = 0x00E11AB1A1E011CFD0;
|
||||
|
||||
#region Offsets
|
||||
|
||||
private const int OLE_HEADER_SIGNATURE = 0x00;
|
||||
|
||||
private const int OLE_HEADER_CLSID = 0x08;
|
||||
|
||||
private const int OLE_HEADER_MINOR_VER = 0x18;
|
||||
|
||||
private const int OLE_HEADER_MAJOR_VER = 0x1a;
|
||||
|
||||
private const int OLE_HEADER_BYTE_ORDER = 0x1c;
|
||||
|
||||
private const int OLE_HEADER_BB_SHIFT = 0x1e;
|
||||
|
||||
private const int OLE_HEADER_SB_SHIFT = 0x20;
|
||||
|
||||
private const int OLE_HEADER_RESERVED = 0x22;
|
||||
|
||||
private const int OLE_HEADER_CSECTDIR = 0x28;
|
||||
|
||||
private const int OLE_HEADER_NUM_BAT = 0x2c;
|
||||
|
||||
private const int OLE_HEADER_DIRENT_START = 0x30;
|
||||
|
||||
private const int OLE_HEADER_TRANSACTING_SIGNATURE = 0x34;
|
||||
|
||||
private const int OLE_HEADER_THRESHOLD = 0x38;
|
||||
|
||||
private const int OLE_HEADER_SBAT_START = 0x3c;
|
||||
|
||||
private const int OLE_HEADER_NUM_SBAT = 0x40;
|
||||
|
||||
private const int OLE_HEADER_METABAT_BLOCK = 0x44;
|
||||
|
||||
private const int OLE_HEADER_NUM_METABAT = 0x48;
|
||||
|
||||
private const int OLE_HEADER_START_BAT = 0x4c;
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// MS-OLE header signature
|
||||
/// </summary>
|
||||
/// <remarks>0x00</remarks>
|
||||
public byte[] SIGNATURE { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// See ReadClassStg
|
||||
/// </summary>
|
||||
/// <remarks>0x08</remarks>
|
||||
public byte[] CLSID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 0x33 and 0x3E have been seen
|
||||
/// </summary>
|
||||
/// <remarks>0x18</remarks>
|
||||
public ushort MINOR_VER { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 0x03 been seen in wild
|
||||
/// </summary>
|
||||
/// <remarks>0x1A</remarks>
|
||||
public ushort MAJOR_VER { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 0xFE 0xFF == Intel Little Endian
|
||||
/// </summary>
|
||||
/// <remarks>0x1C</remarks>
|
||||
public ushort BYTE_ORDER { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of bits to shift to get a big block size
|
||||
/// </summary>
|
||||
/// <remarks>0x1E</remarks>
|
||||
public ushort BB_SHIFT { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of bits to shift to get a small block size
|
||||
/// </summary>
|
||||
/// <remarks>0x20</remarks>
|
||||
public ushort SB_SHIFT { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 0x22..0x27 reserved == 0
|
||||
/// </summary>
|
||||
/// <remarks>0x22</remarks>
|
||||
public byte[] RESERVED { get; set; }
|
||||
|
||||
/// <remarks>0x28</remarks>
|
||||
public uint CSECTDIR { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reported number of big block BATs in the file
|
||||
/// </summary>
|
||||
/// <remarks>0x2C</remarks>
|
||||
public uint NUM_BAT { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Directory entry start offset
|
||||
/// </summary>
|
||||
/// <remarks>0x30</remarks>
|
||||
public uint DIRENT_START { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 0x34..0x37 transacting signature must be 0
|
||||
/// </summary>
|
||||
/// <remarks>0x34</remarks>
|
||||
public uint TRANSACTING_SIGNATURE { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// File size transition between small and big blocks
|
||||
/// </summary>
|
||||
/// <remarks>0x38</remarks>
|
||||
public uint THRESHOLD { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Offset where small block BATs start
|
||||
/// </summary>
|
||||
/// <remarks>0x3C</remarks>
|
||||
public uint SBAT_START { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reported number of small block BATs in the file
|
||||
/// </summary>
|
||||
/// <remarks>0x40</remarks>
|
||||
public uint NUM_SBAT { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Block ID of the first MetaBAT block
|
||||
/// </summary>
|
||||
/// <remarks>0x44</remarks>
|
||||
public uint METABAT_BLOCK { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of MetaBAT blocks in the file
|
||||
/// </summary>
|
||||
/// <remarks>0x48</remarks>
|
||||
public uint NUM_METABAT { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Block ID of the first BAT block
|
||||
/// </summary>
|
||||
/// <remarks>0x4C</remarks>
|
||||
public uint START_BAT { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Derived Properties
|
||||
|
||||
/// <summary>
|
||||
/// Indicate if the contents are Intel Little-Endian
|
||||
/// </summary>
|
||||
public bool LITTLE_ENDIAN => BYTE_ORDER == 0xFFFE;
|
||||
|
||||
/// <summary>
|
||||
/// Size of a big block, in bytes
|
||||
/// </summary>
|
||||
public int BB_SIZE => 1 << BB_SHIFT;
|
||||
|
||||
public int BB_FILTER => BB_SIZE << 1;
|
||||
|
||||
/// <summary>
|
||||
/// Size of a small block, in bytes
|
||||
/// </summary>
|
||||
public int SB_SIZE => 1 << SB_SHIFT;
|
||||
|
||||
public int SB_FILTER => SB_SIZE << 1;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private MSOleHeader() { }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new default MSOleHeader
|
||||
/// </summary>
|
||||
public static MSOleHeader CreateDefault()
|
||||
{
|
||||
MSOleHeader header = new MSOleHeader();
|
||||
|
||||
header.SIGNATURE = SIGNATURE_BYTES;
|
||||
header.CLSID = new byte[16];
|
||||
header.MINOR_VER = 0x003E;
|
||||
header.MAJOR_VER = 0x0003;
|
||||
header.BYTE_ORDER = 0xFFFE;
|
||||
header.BB_SHIFT = 0x0009;
|
||||
header.SB_SHIFT = 0x0006;
|
||||
header.RESERVED = new byte[6];
|
||||
header.CSECTDIR = 0x00000000;
|
||||
header.NUM_BAT = 0x00000000;
|
||||
header.DIRENT_START = 0xFFFFFFFF;
|
||||
header.TRANSACTING_SIGNATURE = 0x00000000;
|
||||
header.THRESHOLD = 0x00001000;
|
||||
|
||||
// Remainder have default values for their types
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new MSOleHeader from data
|
||||
/// </summary>
|
||||
public static MSOleHeader Create(byte[] data, int ptr, ref Exception err)
|
||||
{
|
||||
if (data == null || data.Length < OLE_HEADER_SIZE)
|
||||
return null;
|
||||
|
||||
MSOleHeader header = new MSOleHeader();
|
||||
|
||||
header.SIGNATURE = new byte[8];
|
||||
Array.Copy(data, ptr + OLE_HEADER_SIGNATURE, header.SIGNATURE, 0, 8);
|
||||
|
||||
if (!header.SIGNATURE.SequenceEqual(SIGNATURE_BYTES))
|
||||
{
|
||||
err = new ArgumentException("Signature is malformed");
|
||||
return null;
|
||||
}
|
||||
|
||||
header.CLSID = new byte[16];
|
||||
Array.Copy(data, ptr + OLE_HEADER_CLSID, header.CLSID, 0, 16);
|
||||
header.MINOR_VER = GSF_LE_GET_GUINT16(data, ptr + OLE_HEADER_MINOR_VER);
|
||||
header.MAJOR_VER = GSF_LE_GET_GUINT16(data, ptr + OLE_HEADER_MAJOR_VER);
|
||||
header.BYTE_ORDER = GSF_LE_GET_GUINT16(data, ptr + OLE_HEADER_BYTE_ORDER);
|
||||
header.BB_SHIFT = GSF_LE_GET_GUINT16(data, ptr + OLE_HEADER_BB_SHIFT);
|
||||
header.SB_SHIFT = GSF_LE_GET_GUINT16(data, ptr + OLE_HEADER_SB_SHIFT);
|
||||
|
||||
// It makes no sense to have a block larger than 2^31 for now.
|
||||
// Maybe relax this later, but not much.
|
||||
if (6 > header.BB_SHIFT || header.BB_SHIFT >= 31 || header.SB_SHIFT > header.BB_SHIFT)
|
||||
{
|
||||
err = new ArgumentException("Unreasonable block sizes");
|
||||
return null;
|
||||
}
|
||||
|
||||
header.RESERVED = new byte[6];
|
||||
Array.Copy(data, ptr + OLE_HEADER_RESERVED, header.RESERVED, 0, 6);
|
||||
header.CSECTDIR = GSF_LE_GET_GUINT32(data, ptr + OLE_HEADER_CSECTDIR);
|
||||
header.NUM_BAT = GSF_LE_GET_GUINT32(data, ptr + OLE_HEADER_NUM_BAT);
|
||||
header.DIRENT_START = GSF_LE_GET_GUINT32(data, ptr + OLE_HEADER_DIRENT_START);
|
||||
header.TRANSACTING_SIGNATURE = GSF_LE_GET_GUINT32(data, ptr + OLE_HEADER_TRANSACTING_SIGNATURE);
|
||||
|
||||
if (header.TRANSACTING_SIGNATURE != 0)
|
||||
{
|
||||
err = new ArgumentException("Transacting signature must be 0");
|
||||
return null;
|
||||
}
|
||||
|
||||
header.THRESHOLD = GSF_LE_GET_GUINT32(data, ptr + OLE_HEADER_THRESHOLD);
|
||||
header.SBAT_START = GSF_LE_GET_GUINT32(data, ptr + OLE_HEADER_SBAT_START);
|
||||
header.NUM_SBAT = GSF_LE_GET_GUINT32(data, ptr + OLE_HEADER_NUM_SBAT);
|
||||
header.METABAT_BLOCK = GSF_LE_GET_GUINT32(data, ptr + OLE_HEADER_METABAT_BLOCK);
|
||||
header.NUM_METABAT = GSF_LE_GET_GUINT32(data, ptr + OLE_HEADER_NUM_METABAT);
|
||||
header.START_BAT = GSF_LE_GET_GUINT32(data, ptr + OLE_HEADER_START_BAT);
|
||||
|
||||
if (header.NUM_SBAT == 0
|
||||
&& header.SBAT_START != GsfMSOleImpl.BAT_MAGIC_END_OF_CHAIN
|
||||
&& header.SBAT_START != GsfMSOleImpl.BAT_MAGIC_UNUSED)
|
||||
{
|
||||
Console.Error.WriteLine("There are not supposed to be any blocks in the small block allocation table, yet there is a link to some. Ignoring it.");
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write to data from an existing MSOleHeader
|
||||
/// </summary>
|
||||
public bool Write(byte[] data, int ptr)
|
||||
{
|
||||
if (data == null || data.Length < OLE_HEADER_SIZE)
|
||||
return false;
|
||||
|
||||
Array.Copy(SIGNATURE, 0, data, ptr + OLE_HEADER_SIGNATURE, SIGNATURE.Length);
|
||||
Array.Copy(CLSID, 0, data, ptr + OLE_HEADER_CLSID, CLSID.Length);
|
||||
GSF_LE_SET_GUINT16(data, ptr + OLE_HEADER_MINOR_VER, MINOR_VER);
|
||||
GSF_LE_SET_GUINT16(data, ptr + OLE_HEADER_MAJOR_VER, MAJOR_VER);
|
||||
GSF_LE_SET_GUINT16(data, ptr + OLE_HEADER_BYTE_ORDER, BYTE_ORDER);
|
||||
GSF_LE_SET_GUINT16(data, ptr + OLE_HEADER_BB_SHIFT, BB_SHIFT);
|
||||
GSF_LE_SET_GUINT16(data, ptr + OLE_HEADER_SB_SHIFT, SB_SHIFT);
|
||||
Array.Copy(RESERVED, 0, data, ptr + OLE_HEADER_RESERVED, RESERVED.Length);
|
||||
GSF_LE_SET_GUINT32(data, ptr + OLE_HEADER_CSECTDIR, CSECTDIR);
|
||||
GSF_LE_SET_GUINT32(data, ptr + OLE_HEADER_NUM_BAT, NUM_BAT);
|
||||
GSF_LE_SET_GUINT32(data, ptr + OLE_HEADER_DIRENT_START, DIRENT_START);
|
||||
GSF_LE_SET_GUINT32(data, ptr + OLE_HEADER_TRANSACTING_SIGNATURE, TRANSACTING_SIGNATURE);
|
||||
GSF_LE_SET_GUINT32(data, ptr + OLE_HEADER_THRESHOLD, THRESHOLD);
|
||||
GSF_LE_SET_GUINT32(data, ptr + OLE_HEADER_SBAT_START, SBAT_START);
|
||||
GSF_LE_SET_GUINT32(data, ptr + OLE_HEADER_NUM_SBAT, NUM_SBAT);
|
||||
GSF_LE_SET_GUINT32(data, ptr + OLE_HEADER_METABAT_BLOCK, METABAT_BLOCK);
|
||||
GSF_LE_SET_GUINT32(data, ptr + OLE_HEADER_START_BAT, START_BAT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MS-OLE Directory Entry (.msi)
|
||||
/// </summary>
|
||||
internal class MSOleDirectoryEntry
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// Maximum length of the entry name including the null terminator, in bytes
|
||||
/// </summary>
|
||||
public const int DIRENT_MAX_NAME_SIZE = 0x40;
|
||||
|
||||
/// <summary>
|
||||
/// Size of the non-name parts of directory entry header, in bytes
|
||||
/// </summary>
|
||||
public const int DIRENT_DETAILS_SIZE = 0x40;
|
||||
|
||||
/// <summary>
|
||||
/// Total size of the directory entry header
|
||||
/// </summary>
|
||||
public const int DIRENT_SIZE = (DIRENT_MAX_NAME_SIZE + DIRENT_DETAILS_SIZE);
|
||||
|
||||
/// <summary>
|
||||
/// Magic number indicating the termination of a node
|
||||
/// </summary>
|
||||
public const uint DIRENT_MAGIC_END = 0xffffffff;
|
||||
|
||||
#region Offsets
|
||||
|
||||
private const int DIRENT_NAME = 0x00;
|
||||
|
||||
private const int DIRENT_NAME_LEN = 0x40;
|
||||
|
||||
private const int DIRENT_TYPE_FLAG = 0x42;
|
||||
|
||||
private const int DIRENT_COLOR = 0x43;
|
||||
|
||||
private const int DIRENT_PREV = 0x44;
|
||||
|
||||
private const int DIRENT_NEXT = 0x48;
|
||||
|
||||
private const int DIRENT_CHILD = 0x4c;
|
||||
|
||||
private const int DIRENT_CLSID = 0x50;
|
||||
|
||||
private const int DIRENT_USERFLAGS = 0x60;
|
||||
|
||||
private const int DIRENT_CREATE_TIME = 0x64;
|
||||
|
||||
private const int DIRENT_MODIFY_TIME = 0x6C;
|
||||
|
||||
private const int DIRENT_FIRSTBLOCK = 0x74;
|
||||
|
||||
private const int DIRENT_FILE_SIZE = 0x78;
|
||||
|
||||
private const int DIRENT_RESERVED = 0x7C;
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Name of the entry as a byte array
|
||||
/// </summary>
|
||||
/// <remarks>0x00</remarks>
|
||||
public byte[] NAME { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Length of the entry name including the null terminator, in bytes
|
||||
/// </summary>
|
||||
/// <remarks>0x40</remarks>
|
||||
public ushort NAME_LEN { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the type of directory entry
|
||||
/// </summary>
|
||||
/// <remarks>0x42</remarks>
|
||||
public DIRENT_TYPE TYPE_FLAG { get; set; }
|
||||
|
||||
/// <remarks>0x43</remarks>
|
||||
public byte COLOR { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Index of the previous item in the linked list, DIRENT_MAGIC_END if none
|
||||
/// </summary>
|
||||
/// <remarks>0x44</remarks>
|
||||
public uint PREV { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Index of the next item in the linked list, DIRENT_MAGIC_END if none
|
||||
/// </summary>
|
||||
/// <remarks>0x48</remarks>
|
||||
public uint NEXT { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Index of the first child directory entry, DIRENT_MAGIC_END if none
|
||||
/// </summary>
|
||||
/// <remarks>0x4C</remarks>
|
||||
public uint CHILD { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 16 byte GUID used by some apps; Only for dirs
|
||||
/// </summary>
|
||||
/// <remarks>0x50</remarks>
|
||||
public byte[] CLSID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Only for dirs
|
||||
/// </summary>
|
||||
/// <remarks>0x60</remarks>
|
||||
public uint USERFLAGS { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Entry creation time; For files
|
||||
/// </summary>
|
||||
/// <remarks>0x64</remarks>
|
||||
public ulong CREATE_TIME { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Entry modification time; For files
|
||||
/// </summary>
|
||||
/// <remarks>0x6C</remarks>
|
||||
public ulong MODIFY_TIME { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Index of the first block that contains this directory entry
|
||||
/// </summary>
|
||||
/// <remarks>0x74</remarks>
|
||||
public uint FIRSTBLOCK { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Size of the file contained within this directory entry
|
||||
/// </summary>
|
||||
/// <remarks>0x78</remarks>
|
||||
public uint FILE_SIZE { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 0x7c..0x7f reserved == 0
|
||||
/// </summary>
|
||||
/// <remarks>0x7C</remarks>
|
||||
public uint RESERVED { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Derived Properties
|
||||
|
||||
/// <summary>
|
||||
/// Directory Entry name as a UTF-8 encoded string
|
||||
/// </summary>
|
||||
public string NAME_STRING
|
||||
{
|
||||
get
|
||||
{
|
||||
if (NAME_LEN <= 0 || NAME_LEN > DIRENT_MAX_NAME_SIZE)
|
||||
return string.Empty;
|
||||
|
||||
// !#%!@$#^
|
||||
// Sometimes, rarely, people store the stream name as ascii
|
||||
// rather than utf16. Do a validation first just in case.
|
||||
int end;
|
||||
try { end = new UnicodeEncoding(false, true).GetCharCount(NAME); }
|
||||
catch { end = -1; }
|
||||
|
||||
if (end == -1)
|
||||
{
|
||||
byte[] direntNameBytes = Encoding.Convert(Encoding.ASCII, Encoding.UTF8, NAME);
|
||||
return Encoding.UTF8.GetString(direntNameBytes).TrimEnd('\0');
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] direntNameBytes = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, NAME);
|
||||
return Encoding.UTF8.GetString(direntNameBytes).TrimEnd('\0');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Last modified time as a nullable DateTime
|
||||
/// </summary>
|
||||
public DateTime? MODIFY_DATETIME => MODIFY_TIME == 0 ? (DateTime?)null : DateTime.FromFileTime((long)MODIFY_TIME);
|
||||
|
||||
/// <summary>
|
||||
/// Determine if this entry is a directory
|
||||
/// </summary>
|
||||
public bool IS_DIRECTORY => TYPE_FLAG != DIRENT_TYPE.DIRENT_TYPE_FILE;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private MSOleDirectoryEntry() { }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new default MSOleDirectoryEntry
|
||||
/// </summary>
|
||||
public static MSOleDirectoryEntry CreateDefault()
|
||||
{
|
||||
// TODO: Figure out if there are any sane defaults
|
||||
return new MSOleDirectoryEntry();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new MSOleDirectoryEntry from data
|
||||
/// </summary>
|
||||
public static MSOleDirectoryEntry Create(byte[] data, int ptr, ref Exception err)
|
||||
{
|
||||
if (data == null || data.Length < DIRENT_SIZE)
|
||||
return null;
|
||||
|
||||
MSOleDirectoryEntry directoryEntry = new MSOleDirectoryEntry();
|
||||
|
||||
directoryEntry.NAME = new byte[DIRENT_MAX_NAME_SIZE];
|
||||
Array.Copy(data, ptr + DIRENT_NAME, directoryEntry.NAME, 0, directoryEntry.NAME.Length);
|
||||
directoryEntry.NAME_LEN = GSF_LE_GET_GUINT16(data, ptr + DIRENT_NAME_LEN);
|
||||
directoryEntry.TYPE_FLAG = (DIRENT_TYPE)GSF_LE_GET_GUINT8(data, ptr + DIRENT_TYPE_FLAG);
|
||||
|
||||
if (directoryEntry.TYPE_FLAG != DIRENT_TYPE.DIRENT_TYPE_DIR
|
||||
&& directoryEntry.TYPE_FLAG != DIRENT_TYPE.DIRENT_TYPE_FILE
|
||||
&& directoryEntry.TYPE_FLAG != DIRENT_TYPE.DIRENT_TYPE_ROOTDIR)
|
||||
{
|
||||
err = new Exception($"Unknown stream type 0x{directoryEntry.TYPE_FLAG:x}");
|
||||
return null;
|
||||
}
|
||||
|
||||
directoryEntry.COLOR = GSF_LE_GET_GUINT8(data, ptr + DIRENT_COLOR);
|
||||
directoryEntry.PREV = GSF_LE_GET_GUINT32(data, ptr + DIRENT_PREV);
|
||||
directoryEntry.NEXT = GSF_LE_GET_GUINT32(data, ptr + DIRENT_NEXT);
|
||||
directoryEntry.CHILD = GSF_LE_GET_GUINT32(data, ptr + DIRENT_CHILD);
|
||||
directoryEntry.CLSID = new byte[0x10];
|
||||
Array.Copy(data, ptr + DIRENT_CLSID, directoryEntry.CLSID, 0, directoryEntry.CLSID.Length);
|
||||
directoryEntry.USERFLAGS = GSF_LE_GET_GUINT32(data, ptr + DIRENT_USERFLAGS);
|
||||
directoryEntry.CREATE_TIME = GSF_LE_GET_GUINT64(data, ptr + DIRENT_CREATE_TIME);
|
||||
directoryEntry.MODIFY_TIME = GSF_LE_GET_GUINT64(data, ptr + DIRENT_MODIFY_TIME);
|
||||
directoryEntry.FIRSTBLOCK = GSF_LE_GET_GUINT32(data, ptr + DIRENT_FIRSTBLOCK);
|
||||
directoryEntry.FILE_SIZE = GSF_LE_GET_GUINT32(data, ptr + DIRENT_FILE_SIZE);
|
||||
directoryEntry.RESERVED = GSF_LE_GET_GUINT32(data, ptr + DIRENT_RESERVED);
|
||||
|
||||
return directoryEntry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write to data from an existing MSOleHeader
|
||||
/// </summary>
|
||||
public bool Write(byte[] data, int ptr)
|
||||
{
|
||||
if (data == null || data.Length < DIRENT_SIZE)
|
||||
return false;
|
||||
|
||||
Array.Copy(NAME, 0, data, ptr + DIRENT_NAME, NAME.Length);
|
||||
GSF_LE_SET_GUINT16(data, ptr + DIRENT_NAME_LEN, NAME_LEN);
|
||||
GSF_LE_SET_GUINT8(data, ptr + DIRENT_TYPE_FLAG, (byte)TYPE_FLAG);
|
||||
GSF_LE_SET_GUINT8(data, ptr + DIRENT_COLOR, COLOR);
|
||||
GSF_LE_SET_GUINT32(data, ptr + DIRENT_PREV, PREV);
|
||||
GSF_LE_SET_GUINT32(data, ptr + DIRENT_NEXT, NEXT);
|
||||
GSF_LE_SET_GUINT32(data, ptr + DIRENT_CHILD, CHILD);
|
||||
Array.Copy(CLSID, 0, data, ptr + DIRENT_CLSID, CLSID.Length);
|
||||
GSF_LE_SET_GUINT32(data, ptr + DIRENT_USERFLAGS, USERFLAGS);
|
||||
GSF_LE_SET_GUINT64(data, ptr + DIRENT_CREATE_TIME, CREATE_TIME);
|
||||
GSF_LE_SET_GUINT64(data, ptr + DIRENT_MODIFY_TIME, MODIFY_TIME);
|
||||
GSF_LE_SET_GUINT32(data, ptr + DIRENT_FIRSTBLOCK, FIRSTBLOCK);
|
||||
GSF_LE_SET_GUINT32(data, ptr + DIRENT_FILE_SIZE, FILE_SIZE);
|
||||
GSF_LE_SET_GUINT32(data, ptr + DIRENT_RESERVED, RESERVED);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static class GsfMSOleImpl
|
||||
{
|
||||
public const int OLE_HEADER_CSECTDIR = 0x28;
|
||||
public const int OLE_HEADER_NUM_BAT = 0x2C;
|
||||
public const int OLE_HEADER_SBAT_START = 0x3C;
|
||||
public const int OLE_HEADER_START_BAT = 0x4C;
|
||||
public const int BAT_INDEX_SIZE = 4;
|
||||
public const int OLE_HEADER_METABAT_SIZE = ((MSOleHeader.OLE_HEADER_SIZE - OLE_HEADER_START_BAT) / BAT_INDEX_SIZE);
|
||||
|
||||
/* flags in the block allocation list to denote special blocks */
|
||||
public const uint BAT_MAGIC_UNUSED = 0xffffffff; /* -1 */
|
||||
public const uint BAT_MAGIC_END_OF_CHAIN = 0xfffffffe; /* -2 */
|
||||
public const uint BAT_MAGIC_BAT = 0xfffffffd; /* a bat block, -3 */
|
||||
public const uint BAT_MAGIC_METABAT = 0xfffffffc; /* a metabat block -4 */
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
2433
BurnOutSharp/External/libgsf/GsfMSOleUtils.cs
vendored
2433
BurnOutSharp/External/libgsf/GsfMSOleUtils.cs
vendored
File diff suppressed because it is too large
Load Diff
378
BurnOutSharp/External/libgsf/GsfMetaNames.cs
vendored
378
BurnOutSharp/External/libgsf/GsfMetaNames.cs
vendored
@@ -1,378 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-meta-names.h: a list of gsf-meta-names to "generically" represent
|
||||
* all diversly available implementation-specific
|
||||
* meta-names.
|
||||
*
|
||||
* Author: Veerapuram Varadhan (vvaradhan@novell.com)
|
||||
* Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* Copyright (C) 2004-2006 Novell, Inc
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
namespace LibGSF
|
||||
{
|
||||
/// <summary>
|
||||
/// The namespace follow this classification:
|
||||
///
|
||||
/// "dc:" - Dublin Core tags
|
||||
/// "gsf:" - Gnumeric only tags
|
||||
/// "meta:" - OpenDocument tags shared with Gnumeric
|
||||
/// "MSOLE:" - OLE tags
|
||||
/// </summary>
|
||||
public static class GsfMetaNames
|
||||
{
|
||||
#region Namespace - dc
|
||||
|
||||
/// <summary>
|
||||
/// (String) An entity primarily responsible for making the content of the
|
||||
/// resource typically a person, organization, or service.
|
||||
/// </summary>
|
||||
/// <remarks>1.14.0 Moved from "gsf" to "dc".</remarks>
|
||||
public const string GSF_META_NAME_CREATOR = "dc:creator";
|
||||
|
||||
/// <summary>
|
||||
/// (GsfTimestamp) The last time this document was saved.
|
||||
/// </summary>
|
||||
/// <remarks>1.14.0 Moved from dc:date-modified to dc:date.</remarks>
|
||||
public const string GSF_META_NAME_DATE_MODIFIED = "dc:date";
|
||||
|
||||
/// <summary>
|
||||
/// (String) An account of the content of the resource.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_DESCRIPTION = "dc:description";
|
||||
|
||||
/// <summary>
|
||||
/// (GsfDocPropVector of String) Searchable, indexable keywords. Similar to PDF
|
||||
/// keywords or HTML's meta block.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_KEYWORDS = "dc:keywords";
|
||||
|
||||
/// <summary>
|
||||
/// (String) The locale language of the intellectual content of the resource
|
||||
/// (basically xx_YY form for us).
|
||||
/// </summary>
|
||||
/// <remarks>1.14.0 Clarified that this is unique from _NAME_CODEPAGE in MSOLE</remarks>
|
||||
public const string GSF_META_NAME_LANGUAGE = "dc:language";
|
||||
|
||||
/// <summary>
|
||||
/// (UnsignedShort) The MS codepage to encode strings for metadata
|
||||
/// </summary>
|
||||
/// <remarks>1.14.0 Clarified that this is unique from _NAME_CODEPAGE in MSOLE</remarks>
|
||||
public const string GSF_META_NAME_CODEPAGE = "MSOLE:codepage";
|
||||
|
||||
/// <summary>
|
||||
/// (String) The topic of the content of the resource,
|
||||
/// <emphasis>typically</emphasis> including keywords.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_SUBJECT = "dc:subject";
|
||||
|
||||
/// <summary>
|
||||
/// (String) A formal name given to the resource.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_TITLE = "dc:title";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Namespace - gsf
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of bytes in the document.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_BYTE_COUNT = "gsf:byte-count";
|
||||
|
||||
/// <summary>
|
||||
/// (Unsigned Integer) Identifier representing the case-sensitiveness.
|
||||
/// </summary>
|
||||
/// <remarks>of what ?? why is it an integer ??</remarks>
|
||||
public const string GSF_META_NAME_CASE_SENSITIVE = "gsf:case-sensitivity";
|
||||
|
||||
/// <summary>
|
||||
/// (String) Category of the document.
|
||||
/// </summary>
|
||||
/// <remarks>example???</remarks>
|
||||
public const string GSF_META_NAME_CATEGORY = "gsf:category";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of cells in the spread-sheet document, if appropriate.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_CELL_COUNT = "gsf:cell-count";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of characters in the document.
|
||||
/// </summary>
|
||||
/// <remarks>TODO See how to sync this with ODF's document-statistic</remarks>
|
||||
public const string GSF_META_NAME_CHARACTER_COUNT = "gsf:character-count";
|
||||
|
||||
/// <summary>
|
||||
/// (None) Reserved name (PID) for Dictionary
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_DICTIONARY = "gsf:dictionary";
|
||||
|
||||
/// <summary>
|
||||
/// (Vector of strings) Names of the 'interesting' parts of the document. In
|
||||
/// spreadsheets this is a list of the sheet names, and the named expressions.
|
||||
/// </summary>
|
||||
/// <remarks>From MSOLE</remarks>
|
||||
public const string GSF_META_NAME_DOCUMENT_PARTS = "gsf:document-parts";
|
||||
|
||||
/// <summary>
|
||||
/// (Vector of string value pairs stored in alternating elements) Store the
|
||||
/// counts of objects in the document as names 'worksheet' and count '4'
|
||||
/// </summary>
|
||||
/// <remarks>From MSOLE</remarks>
|
||||
public const string GSF_META_NAME_HEADING_PAIRS = "gsf:heading-pairs";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of hidden-slides in the presentation document.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_HIDDEN_SLIDE_COUNT = "gsf:hidden-slide-count";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of images in the document, if appropriate.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_IMAGE_COUNT = "gsf:image-count";
|
||||
|
||||
/// <summary>
|
||||
/// (String) The entity that made the last change to the document, typically a
|
||||
/// person, organization, or service.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_LAST_SAVED_BY = "gsf:last-saved-by";
|
||||
|
||||
/// <summary>
|
||||
/// (Boolean) ???????
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_LINKS_DIRTY = "gsf:links-dirty";
|
||||
|
||||
/// <summary>
|
||||
/// (Unsigned Integer) Identifier representing the default system locale.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_LOCALE_SYSTEM_DEFAULT = "gsf:default-locale";
|
||||
|
||||
/// <summary>
|
||||
/// (String) Name of the manager of "CREATOR" entity.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_MANAGER = "gsf:manager";
|
||||
|
||||
/// <summary>
|
||||
/// (String) Type of presentation, like "On-screen Show", "SlideView" etc.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_PRESENTATION_FORMAT = "gsf:presentation-format";
|
||||
|
||||
/// <summary>
|
||||
/// (Boolean) ?????
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_SCALE = "gsf:scale";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Level of security.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <informaltable frame="none" role="params">
|
||||
/// <tgroup cols = "2" >
|
||||
/// <thead>
|
||||
/// <row><entry align="left">Level</entry><entry>Value</entry></row>
|
||||
/// </thead>
|
||||
/// <tbody>
|
||||
/// <row><entry>None</entry><entry>0</entry></row>
|
||||
/// <row><entry>Password protected</entry><entry>1</entry></row>
|
||||
/// <row><entry>Read-only recommended</entry><entry>2</entry></row>
|
||||
/// <row><entry>Read-only enforced</entry><entry>3</entry></row>
|
||||
/// <row><entry>Locked for annotations</entry><entry>4</entry></row>
|
||||
/// </tbody></tgroup></informaltable>
|
||||
/// </remarks>
|
||||
public const string GSF_META_NAME_SECURITY = "gsf:security";
|
||||
|
||||
/// <summary>
|
||||
/// (GsfClipData) Thumbnail data of the document, typically a
|
||||
/// preview image of the document.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_THUMBNAIL = "gsf:thumbnail";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of liness in the document.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_LINE_COUNT = "gsf:line-count";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of "multi-media" clips in the document.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_MM_CLIP_COUNT = "gsf:MM-clip-count";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of "notes" in the document.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_NOTE_COUNT = "gsf:note-count";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of objects (OLE and other graphics) in the document, if
|
||||
/// appropriate.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_OBJECT_COUNT = "gsf:object-count";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of pages in the document, if appropriate.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_PAGE_COUNT = "gsf:page-count";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of paragraphs in the document, if appropriate.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_PARAGRAPH_COUNT = "gsf:paragraph-count";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of slides in the presentation document.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_SLIDE_COUNT = "gsf:slide-count";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of pages in the document, if appropriate.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_SPREADSHEET_COUNT = "gsf:spreadsheet-count";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of tables in the document, if appropriate.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_TABLE_COUNT = "gsf:table-count";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of words in the document.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_WORD_COUNT = "gsf:word-count";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Namespace - MSOLE
|
||||
|
||||
/// <summary>
|
||||
/// (Unknown) User-defined name
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_MSOLE_UNKNOWN_17 = "MSOLE:unknown-doc-17";
|
||||
|
||||
/// <summary>
|
||||
/// (Unknown) User-defined name
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_MSOLE_UNKNOWN_18 = "MSOLE:unknown-doc-18";
|
||||
|
||||
/// <summary>
|
||||
/// (Boolean) User-defined name
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_MSOLE_UNKNOWN_19 = "MSOLE:unknown-doc-19";
|
||||
|
||||
/// <summary>
|
||||
/// (Unknown) User-defined name
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_MSOLE_UNKNOWN_20 = "MSOLE:unknown-doc-20";
|
||||
|
||||
/// <summary>
|
||||
/// (Unknown) User-defined name
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_MSOLE_UNKNOWN_21 = "MSOLE:unknown-doc-21";
|
||||
|
||||
/// <summary>
|
||||
/// (Boolean) User-defined name
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_MSOLE_UNKNOWN_22 = "MSOLE:unknown-doc-22";
|
||||
|
||||
/// <summary>
|
||||
/// (i4) User-defined name
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_MSOLE_UNKNOWN_23 = "MSOLE:unknown-doc-23";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Namespace - meta
|
||||
|
||||
/// <summary>
|
||||
/// (Date as ISO String) A date associated with an event in the life cycle of
|
||||
/// the resource (creation/publication date).
|
||||
/// Moved from gsf:date-created to meta:creation-date. This way can be used correctly
|
||||
/// by OpenDocument and Gnumeric.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_DATE_CREATED = "meta:creation-date";
|
||||
|
||||
/// <summary>
|
||||
/// (Date as ISO String) The total-time taken until the last modification.
|
||||
/// Moved from "gsf" to "meta". This way can be used correctly by OpenDocument
|
||||
/// and Gnumeric.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_EDITING_DURATION = "meta:editing-duration";
|
||||
|
||||
/// <summary>
|
||||
/// (String) The application that generated this document. AbiWord, Gnumeric,
|
||||
/// etc...
|
||||
/// </summary>
|
||||
/// <remarks>1.14.0 Moved from "gsf" to "meta".</remarks>
|
||||
public const string GSF_META_NAME_GENERATOR = "meta:generator";
|
||||
|
||||
/// <summary>
|
||||
/// (String) Searchable, indexable keywords. Similar to PDF keywords or HTML's
|
||||
/// meta block.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_KEYWORD = "meta:keyword";
|
||||
|
||||
/// <summary>
|
||||
/// (String) Specifies the name of the person who created the document
|
||||
/// initially.
|
||||
/// </summary>
|
||||
/// <remarks>1.14.0 Moved from "gsf" to "meta".</remarks>
|
||||
public const string GSF_META_NAME_INITIAL_CREATOR = "meta:initial-creator";
|
||||
|
||||
/// <summary>
|
||||
/// (String) Name of the company/organization that the "CREATOR" entity is
|
||||
/// associated with.
|
||||
/// </summary>
|
||||
/// <remarks>1.14.1 Moved from "gsf:company" to "dc:publisher".</remarks>
|
||||
public const string GSF_META_NAME_COMPANY = "dc:publisher";
|
||||
|
||||
/// <summary>
|
||||
/// (GsfTimestamp) Specifies the date and time when the document was last
|
||||
/// printed.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_PRINT_DATE = "meta:print-date";
|
||||
|
||||
/// <summary>
|
||||
/// (GSF_META_NAME_HEADING_PAIRS) The last time this document was printed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 1.14.0 Moved from "gsf" to "dc".
|
||||
/// 1.14.1 Moved back to "gsf" from "dc".
|
||||
/// </remarks>
|
||||
public const string GSF_META_NAME_LAST_PRINTED = "gsf:last-printed";
|
||||
|
||||
/// <summary>
|
||||
/// (String) Specifies the name of the last person who printed the document.
|
||||
/// </summary>
|
||||
/// <remarks>1.14.0 Moved from "gsf" to "meta".</remarks>
|
||||
public const string GSF_META_NAME_PRINTED_BY = "meta:printed-by";
|
||||
|
||||
/// <summary>
|
||||
/// (Integer) Count of revision on the document, if appropriate.
|
||||
/// Moved from gsf:revision-count to meta:editing-cycles. This way can be used
|
||||
/// correctly by OpenDocument and Gnumeric.
|
||||
/// </summary>
|
||||
public const string GSF_META_NAME_REVISION_COUNT = "meta:editing-cycles";
|
||||
|
||||
/// <summary>
|
||||
/// (String) The template file that is been used to generate this document.
|
||||
/// </summary>
|
||||
/// <remarks>1.14.0 Moved from "gsf" to "meta"</remarks>
|
||||
public const string GSF_META_NAME_TEMPLATE = "meta:template";
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
784
BurnOutSharp/External/libgsf/GsfOpenDocUtils.cs
vendored
784
BurnOutSharp/External/libgsf/GsfOpenDocUtils.cs
vendored
@@ -1,784 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-opendoc-utils.c: Handle the application neutral portions of OpenDocument
|
||||
*
|
||||
* Author: Luciano Wolf (luciano.wolf@indt.org.br)
|
||||
*
|
||||
* Copyright (C) 2006 Jody Goldberg (jody@gnome.org)
|
||||
* Copyright (C) 2005-2006 INdT - Instituto Nokia de Tecnologia
|
||||
* http://www.indt.org.br
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
using LibGSF.Input;
|
||||
using static LibGSF.GsfMetaNames;
|
||||
|
||||
namespace LibGSF
|
||||
{
|
||||
#region Enums
|
||||
|
||||
public enum OpenDocType
|
||||
{
|
||||
OO_NS_OFFICE,
|
||||
OO_NS_STYLE,
|
||||
OO_NS_TEXT,
|
||||
OO_NS_TABLE,
|
||||
OO_NS_DRAW,
|
||||
OO_NS_NUMBER,
|
||||
OO_NS_CHART,
|
||||
OO_NS_DR3D,
|
||||
OO_NS_FORM,
|
||||
OO_NS_SCRIPT,
|
||||
OO_NS_CONFIG,
|
||||
OO_NS_MATH,
|
||||
OO_NS_FO,
|
||||
OO_NS_DC,
|
||||
OO_NS_META,
|
||||
OO_NS_XLINK,
|
||||
OO_NS_SVG,
|
||||
|
||||
/* new in 2.0 */
|
||||
OO_NS_OOO,
|
||||
OO_NS_OOOW,
|
||||
OO_NS_OOOC,
|
||||
OO_NS_DOM,
|
||||
OO_NS_XFORMS,
|
||||
OO_NS_XSD,
|
||||
OO_NS_XSI,
|
||||
|
||||
OO_NS_PRESENT, /* added in gsf-1.14.8 */
|
||||
|
||||
/* new in 3.0 */
|
||||
OO_NS_RPT,
|
||||
OO_NS_OF,
|
||||
OO_NS_RDFA,
|
||||
OO_NS_FIELD,
|
||||
OO_NS_FORMX,
|
||||
|
||||
/* Other OpenDocument 1.1 */
|
||||
OO_NS_ANIM,
|
||||
OO_NS_DATASTYLE,
|
||||
OO_NS_MANIFEST,
|
||||
OO_NS_SMIL,
|
||||
|
||||
/* Symphony 1.3 */
|
||||
OO_LOTUS_NS_PRODTOOLS,
|
||||
|
||||
/* KOffice 1.6.3 */
|
||||
OO_KDE_NS_KOFFICE,
|
||||
|
||||
/*CleverAge ODF Add-in for Microsoft Office 3.0.5224.0 (11.0.8302)*/
|
||||
OO_CLEVERAGE_NS_DC,
|
||||
|
||||
/* Microsoft Excel Formulas */
|
||||
OO_MS_NS_MSOXL,
|
||||
|
||||
/* Gnumeric ODF extensions */
|
||||
OO_GNUM_NS_EXT,
|
||||
|
||||
/* New in ODF 3.2 */
|
||||
OO_NS_GRDDL,
|
||||
OO_NS_XHTML,
|
||||
OO_NS_TABLE_OOO,
|
||||
|
||||
/* New in ODF 3.3 */
|
||||
OO_NS_CHART_OOO,
|
||||
|
||||
/* New in LOCALC */
|
||||
OO_NS_LOCALC_EXT,
|
||||
OO_NS_LOCALC_EXT2
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Classes
|
||||
|
||||
public class GsfODFOut : GsfXMLOut
|
||||
{
|
||||
#region Internal Properties
|
||||
|
||||
public int OdfVersion { get; set; } = 100;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public string GetVersionString() => $"{OdfVersion / 100}.{OdfVersion % 100}";
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class GsfOOMetaIn
|
||||
{
|
||||
public GsfDocMetaData MetaData { get; set; }
|
||||
|
||||
public List<object> Keywords { get; set; }
|
||||
|
||||
public Exception Err { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public Type Type { get; set; }
|
||||
|
||||
public XmlDocument Doc { get; set; }
|
||||
}
|
||||
|
||||
public static class GsfOpenDocUtils
|
||||
{
|
||||
#region Constants
|
||||
|
||||
public const string OFFICE = "office:";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Gives the ODF version used by libgsf when writing Open Document files.
|
||||
/// </summary>
|
||||
/// <returns>The ODF version as a string: "1.2".</returns>
|
||||
public static string GetVersionString() => "1.2";
|
||||
|
||||
/// <summary>
|
||||
/// Gives the ODF version used by libgsf when writing Open Document files.
|
||||
/// </summary>
|
||||
/// <returns>The ODF version: 102.</returns>
|
||||
public static short GetVersion() => 102;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extension Functions
|
||||
|
||||
public static void od_meta_generator(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_GENERATOR, typeof(string));
|
||||
|
||||
public static void od_meta_title(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_TITLE, typeof(string));
|
||||
|
||||
public static void od_meta_description(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_DESCRIPTION, typeof(string));
|
||||
|
||||
public static void od_meta_subject(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_SUBJECT, typeof(string));
|
||||
|
||||
public static void od_meta_initial_creator(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_INITIAL_CREATOR, typeof(string));
|
||||
|
||||
/// <summary>
|
||||
/// OD considers this the last person to modify the doc, rather than
|
||||
/// the DC convention of the person primarilly responsible for its creation
|
||||
/// </summary>
|
||||
public static void od_meta_creator(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_CREATOR, typeof(string));
|
||||
|
||||
/// <summary>
|
||||
/// Last to print
|
||||
/// </summary>
|
||||
public static void od_meta_printed_by(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_PRINTED_BY, typeof(string));
|
||||
|
||||
public static void od_meta_date_created(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_DATE_CREATED, typeof(DateTime));
|
||||
|
||||
public static void od_meta_date_modified(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_DATE_MODIFIED, typeof(DateTime));
|
||||
|
||||
public static void od_meta_print_date(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_LAST_PRINTED, typeof(DateTime));
|
||||
|
||||
public static void od_meta_language(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_LANGUAGE, typeof(string));
|
||||
|
||||
public static void od_meta_editing_cycles(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_REVISION_COUNT, typeof(uint));
|
||||
|
||||
// FIXME FIXME FIXME should be durations using format 'PnYnMnDTnHnMnS'
|
||||
public static void od_meta_editing_duration(this GsfXMLIn xin, GsfXMLBlob blob) => xin.od_get_meta_prop(GSF_META_NAME_EDITING_DURATION, typeof(string));
|
||||
|
||||
/// <summary>
|
||||
/// OD allows multiple keywords, accumulate things and make it an array
|
||||
/// </summary>
|
||||
public static void od_meta_keyword(this GsfXMLIn xin, GsfXMLBlob blob)
|
||||
{
|
||||
GsfOOMetaIn mi = (GsfOOMetaIn)xin.UserState;
|
||||
|
||||
if (mi.Keywords == null)
|
||||
mi.Keywords = new List<object>();
|
||||
|
||||
mi.Keywords.Add(xin.Content);
|
||||
}
|
||||
|
||||
public static void od_meta_user_defined(this GsfXMLIn xin, string[] attrs)
|
||||
{
|
||||
GsfOOMetaIn mi = (GsfOOMetaIn)xin.UserState;
|
||||
mi.Type = null;
|
||||
mi.Name = null;
|
||||
|
||||
for (int i = 0; i < attrs.Length - 1 && attrs[i] != null && attrs[i + 1] != null; i += 2)
|
||||
{
|
||||
if (attrs[i] == "meta:name")
|
||||
{
|
||||
mi.Name = attrs[i + 1];
|
||||
}
|
||||
else if (attrs[i] == "meta:value-type" || attrs[i] == "meta:type")
|
||||
{
|
||||
// "meta:type" is a typo on the write
|
||||
// side that was fixed on 20110509.
|
||||
if (attrs[i + 1] == "boolean")
|
||||
{
|
||||
mi.Type = typeof(bool);
|
||||
}
|
||||
else if (attrs[i + 1] == "float")
|
||||
{
|
||||
mi.Type = typeof(double);
|
||||
}
|
||||
else if (attrs[i + 1] == "string")
|
||||
{
|
||||
mi.Type = typeof(string);
|
||||
}
|
||||
else if (attrs[i + 1] == "date" || attrs[i + 1] == "data")
|
||||
{
|
||||
// "data" is a typo on the write side that was
|
||||
// fixed on 20110311.
|
||||
mi.Type = typeof(DateTime);
|
||||
}
|
||||
else if (attrs[i + 1] == "time")
|
||||
{
|
||||
mi.Type = typeof(string);
|
||||
// We should be able to do better
|
||||
}
|
||||
else
|
||||
{
|
||||
// What?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This should not happen
|
||||
if (mi.Name == null)
|
||||
mi.Name = string.Empty;
|
||||
}
|
||||
|
||||
public static void od_meta_user_defined_end(this GsfXMLIn xin, GsfXMLBlob blob)
|
||||
{
|
||||
GsfOOMetaIn mi = (GsfOOMetaIn)xin.UserState;
|
||||
if (mi.Name != null)
|
||||
{
|
||||
object res = new object();
|
||||
Type t = mi.Type;
|
||||
|
||||
if (t == null)
|
||||
t = typeof(string);
|
||||
|
||||
if (!GsfLibXML.ValueFromString(ref res, t, xin.Content))
|
||||
{
|
||||
mi.Name = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mi.Name.StartsWith("GSF_DOCPROP_VECTOR:"))
|
||||
{
|
||||
int true_name = mi.Name.IndexOf(':', 19);
|
||||
if (true_name != -1 && mi.Name[++true_name] != 0)
|
||||
{
|
||||
GsfDocProp prop = mi.MetaData.Lookup(mi.Name.Substring(true_name));
|
||||
if (prop == null)
|
||||
{
|
||||
List<GsfDocProp> vector = new List<GsfDocProp>();
|
||||
vector.Add(prop);
|
||||
mi.MetaData.Insert(mi.Name.Substring(true_name), vector);
|
||||
}
|
||||
else
|
||||
{
|
||||
object old = prop.Value;
|
||||
if (old is List<GsfDocProp> oldList)
|
||||
{
|
||||
List<GsfDocProp> newObj = new List<GsfDocProp>();
|
||||
newObj.AddRange(oldList);
|
||||
newObj.Add(res as GsfDocProp);
|
||||
prop.Value = newObj;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error.WriteLine($"Property \"{mi.Name.Substring(true_name)}\" used for multiple types!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
mi.Name = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mi.MetaData.Insert(mi.Name, res);
|
||||
mi.Name = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void od_get_meta_prop(this GsfXMLIn xin, string prop_name, Type g_type)
|
||||
{
|
||||
object res = new object();
|
||||
if (GsfLibXML.ValueFromString(ref res, g_type, xin.Content))
|
||||
(xin.UserState as GsfOOMetaIn).MetaData.Insert(prop_name, res);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
/// <summary>
|
||||
/// Generated based on:
|
||||
/// http://www.oasis-open.org/committees/download.php/12572/OpenDocument-v1.0-os.pdf
|
||||
/// and OpenDocument-v1.1.pdf
|
||||
/// </summary>
|
||||
private static XmlNameTable CreateOpenDocumentNamespaces()
|
||||
{
|
||||
NameTable table = new NameTable();
|
||||
|
||||
// OOo 1.0.x & 1.1.x
|
||||
table.Add("http://openoffice.org/2000/office");
|
||||
table.Add("http://openoffice.org/2000/style");
|
||||
table.Add("http://openoffice.org/2000/text");
|
||||
table.Add("http://openoffice.org/2000/table");
|
||||
table.Add("http://openoffice.org/2000/drawing");
|
||||
table.Add("http://openoffice.org/2000/datastyle");
|
||||
table.Add("http://openoffice.org/2000/chart");
|
||||
table.Add("http://openoffice.org/2000/dr3d");
|
||||
table.Add("http://openoffice.org/2000/form");
|
||||
table.Add("http://openoffice.org/2000/script");
|
||||
table.Add("http://openoffice.org/2001/config");
|
||||
table.Add("http://www.w3.org/1998/Math/MathML"); // also in 2.0
|
||||
table.Add("http://www.w3.org/1999/XSL/Format");
|
||||
table.Add("http://www.w3.org/1999/xlink"); // also in 2.0
|
||||
table.Add("http://www.w3.org/2000/svg");
|
||||
|
||||
// OOo 1.9.x & 2.0.x
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:office:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:style:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:text:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:table:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:drawing:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:meta:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:chart:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:form:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:script:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:presentation:1.0");
|
||||
|
||||
table.Add("http://purl.org/dc/elements/1.1/");
|
||||
table.Add("http://openoffice.org/2004/office");
|
||||
table.Add("http://openoffice.org/2004/writer");
|
||||
table.Add("http://openoffice.org/2004/calc");
|
||||
table.Add("http://www.w3.org/2001/xml-events");
|
||||
table.Add("http://www.w3.org/2002/xforms");
|
||||
table.Add("http://www.w3.org/2001/XMLSchema");
|
||||
table.Add("http://www.w3.org/2001/XMLSchema-instance");
|
||||
|
||||
// OOo 3.0.x
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:of:1.2");
|
||||
table.Add("urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0");
|
||||
table.Add("urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:field:1.0");
|
||||
table.Add("urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0");
|
||||
|
||||
table.Add("http://openoffice.org/2005/report");
|
||||
table.Add("http://docs.oasis-open.org/opendocument/meta/rdfa#");
|
||||
|
||||
// OOo 3.2.x
|
||||
table.Add("http://www.w3.org/2003/g/data-view#");
|
||||
table.Add("http://www.w3.org/1999/xhtml");
|
||||
table.Add("http://openoffice.org/2009/table");
|
||||
|
||||
// OOo 3.3.x
|
||||
table.Add("http://openoffice.org/2010/chart");
|
||||
|
||||
// Other OpenDocument v 1.1
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:config:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:animation:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:data style:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0");
|
||||
table.Add("urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0");
|
||||
|
||||
// Symphony 1.3
|
||||
table.Add("http://www.ibm.com/xmlns/prodtools");
|
||||
|
||||
// CleverAge ODF Add-in for Microsoft Office 3.0.5224.0 (11.0.8302)
|
||||
table.Add("http://purl.org/dc/terms/");
|
||||
|
||||
// KOffice 1.6.3
|
||||
table.Add("http://www.koffice.org/2005/");
|
||||
|
||||
// Microsoft Excel Formulas in ODF
|
||||
table.Add("http://schemas.microsoft.com/office/excel/formula");
|
||||
|
||||
// Gnumeric ODF extensions
|
||||
table.Add("http://www.gnumeric.org/odf-extension/1.0");
|
||||
|
||||
// LOCALC
|
||||
table.Add("urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0");
|
||||
table.Add("urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0");
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
private static XmlDocumentType CreateOpenDocumentDTD(XmlDocument doc)
|
||||
{
|
||||
// Root node
|
||||
XmlDocumentType docType = doc.CreateDocumentType("meta", null, null, null);
|
||||
|
||||
// OpenDocument TAGS
|
||||
docType.AppendChild(doc.CreateElement("generator"));
|
||||
docType.AppendChild(doc.CreateElement("title"));
|
||||
docType.AppendChild(doc.CreateElement("description"));
|
||||
docType.AppendChild(doc.CreateElement("subject"));
|
||||
docType.AppendChild(doc.CreateElement("keyword"));
|
||||
docType.AppendChild(doc.CreateElement("initial-creator"));
|
||||
docType.AppendChild(doc.CreateElement("creator"));
|
||||
docType.AppendChild(doc.CreateElement("printed-by"));
|
||||
docType.AppendChild(doc.CreateElement("creation-date"));
|
||||
docType.AppendChild(doc.CreateElement("date"));
|
||||
docType.AppendChild(doc.CreateElement("print-date"));
|
||||
docType.AppendChild(doc.CreateElement("template"));
|
||||
docType.AppendChild(doc.CreateElement("auto-reload"));
|
||||
docType.AppendChild(doc.CreateElement("hyperlink-behaviour"));
|
||||
docType.AppendChild(doc.CreateElement("document-statistic"));
|
||||
docType.AppendChild(doc.CreateElement("language"));
|
||||
docType.AppendChild(doc.CreateElement("editing-duration"));
|
||||
docType.AppendChild(doc.CreateElement("user-defined"));
|
||||
|
||||
return docType;
|
||||
}
|
||||
|
||||
public static void gsf_opendoc_metadata_subtree_free(GsfXMLIn xin, object old_state)
|
||||
{
|
||||
GsfOOMetaIn state = old_state as GsfOOMetaIn;
|
||||
if (state.Keywords != null)
|
||||
state.MetaData.Insert(GSF_META_NAME_KEYWORDS, state.Keywords);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extend <paramref name="doc"/>> so that it can parse a subtree in OpenDoc metadata format
|
||||
/// </summary>
|
||||
public static void gsf_doc_meta_data_odf_subtree(GsfDocMetaData md, GsfXMLIn doc)
|
||||
{
|
||||
if (md == null)
|
||||
return;
|
||||
|
||||
XmlDocument document = new XmlDocument(CreateOpenDocumentNamespaces());
|
||||
XmlDocumentType docType = CreateOpenDocumentDTD(document);
|
||||
document.AppendChild(docType);
|
||||
|
||||
GsfOOMetaIn state = new GsfOOMetaIn
|
||||
{
|
||||
MetaData = md,
|
||||
Type = null,
|
||||
Doc = document,
|
||||
};
|
||||
|
||||
doc.PushState(state.Doc, state, gsf_opendoc_metadata_subtree_free, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extend <paramref name="xin"/> so that it can parse a subtree in OpenDoc metadata format
|
||||
/// The current user_state must be a GsfOOMetaIn!
|
||||
/// </summary>
|
||||
public static void gsf_opendoc_metadata_subtree_internal(GsfXMLIn xin, string[] attrs)
|
||||
{
|
||||
GsfOOMetaIn mi = (GsfOOMetaIn)xin.UserState;
|
||||
if (mi.Doc == null)
|
||||
{
|
||||
XmlDocument document = new XmlDocument(CreateOpenDocumentNamespaces());
|
||||
XmlDocumentType docType = CreateOpenDocumentDTD(document);
|
||||
document.AppendChild(docType);
|
||||
mi.Doc = document;
|
||||
}
|
||||
|
||||
xin.PushState(mi.Doc, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsf_doc_meta_data_read_from_odf:
|
||||
* @md: #GsfDocMetaData
|
||||
* @input: #GsfInput
|
||||
*
|
||||
* Read an OpenDocument metadata stream from @input and store the properties
|
||||
* into @md. Overwrite any existing properties with the same id.
|
||||
*
|
||||
* Since: 1.14.24
|
||||
*
|
||||
* Returns: (transfer full): a #Exception if there is a problem.
|
||||
**/
|
||||
public static Exception gsf_doc_meta_data_read_from_odf(GsfDocMetaData md, GsfInput input)
|
||||
{
|
||||
GsfOOMetaIn state = new GsfOOMetaIn
|
||||
{
|
||||
MetaData = md,
|
||||
Keywords = null,
|
||||
Err = null,
|
||||
Name = null,
|
||||
Doc = null,
|
||||
};
|
||||
|
||||
GsfXMLInParser parser = new GsfXMLInParser(input as GsfXMLIn);
|
||||
|
||||
state.Doc = new XmlDocument(CreateOpenDocumentNamespaces());
|
||||
XmlDocumentType docType = CreateOpenDocumentDTD(state.Doc);
|
||||
state.Doc.AppendChild(docType);
|
||||
|
||||
while (parser.Read()) { }
|
||||
|
||||
parser.Close();
|
||||
|
||||
if (state.Keywords != null)
|
||||
state.Keywords.Add(md);
|
||||
|
||||
return state.Err;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsf_opendoc_metadata_read: (skip)
|
||||
* @input: #GsfInput
|
||||
* @md: #GsfDocMetaData
|
||||
*
|
||||
* Read an OpenDocument metadata stream from @input and store the properties
|
||||
* into @md. Overwrite any existing properties with the same id.
|
||||
*
|
||||
* Deprecated: 1.14.24, use gsf_doc_meta_data_read_from_odf
|
||||
*
|
||||
* Returns: (transfer full): a #Exception if there is a problem.
|
||||
**/
|
||||
public static Exception gsf_opendoc_metadata_read(GsfInput input, GsfDocMetaData md) => gsf_doc_meta_data_read_from_odf(md, input);
|
||||
|
||||
// Shared by all instances and never freed
|
||||
private static readonly Dictionary<string, string> od_prop_name_map = new Dictionary<string, string>
|
||||
{
|
||||
{ GSF_META_NAME_GENERATOR, "meta:generator" },
|
||||
{ GSF_META_NAME_TITLE, "dc:title" },
|
||||
{ GSF_META_NAME_DESCRIPTION, "dc:description" },
|
||||
{ GSF_META_NAME_SUBJECT, "dc:subject" },
|
||||
{ GSF_META_NAME_INITIAL_CREATOR,"meta:initial-creator" },
|
||||
{ GSF_META_NAME_CREATOR, "dc:creator" },
|
||||
{ GSF_META_NAME_PRINTED_BY, "meta:printed-by" },
|
||||
{ GSF_META_NAME_DATE_CREATED, "meta:creation-date" },
|
||||
{ GSF_META_NAME_DATE_MODIFIED, "dc:date" },
|
||||
{ GSF_META_NAME_LAST_PRINTED, "meta:print-date" },
|
||||
{ GSF_META_NAME_LANGUAGE, "dc:language" },
|
||||
{ GSF_META_NAME_REVISION_COUNT, "meta:editing-cycles" },
|
||||
{ GSF_META_NAME_EDITING_DURATION, "meta:editing-duration" }
|
||||
};
|
||||
|
||||
public static string od_map_prop_name(string name)
|
||||
{
|
||||
if (!od_prop_name_map.ContainsKey(name))
|
||||
return null;
|
||||
|
||||
return od_prop_name_map[name];
|
||||
}
|
||||
|
||||
/*
|
||||
meta:page-count GSF_META_NAME_PAGE_COUNT
|
||||
meta:table-count GSF_META_NAME_TABLE_COUNT:
|
||||
meta:draw-count
|
||||
meta:image-count GSF_META_NAME_IMAGE_COUNT:
|
||||
meta:ole-object-count GSF_META_NAME_OBJECT_COUNT:
|
||||
meta:paragraph-count GSF_META_NAME_PARAGRAPH_COUNT:
|
||||
meta:word-count
|
||||
meta:character-count GSF_META_NAME_CHARACTER_COUNT
|
||||
meta:row-count GSF_META_NAME_LINE_COUNT:
|
||||
meta:frame-count
|
||||
meta:sentence-count
|
||||
meta:syllable-count
|
||||
meta:non-whitespace-character-count
|
||||
|
||||
meta:page-count
|
||||
GSF_META_NAME_SPREADSHEET_COUNT
|
||||
meta:table-count
|
||||
GSF_META_NAME_TABLE_COUNT:
|
||||
meta:image-count
|
||||
* GSF_META_NAME_IMAGE_COUNT:
|
||||
meta:cell-count
|
||||
GSF_META_NAME_CELL_COUNT
|
||||
meta:object-count
|
||||
GSF_META_NAME_OBJECT_COUNT:
|
||||
|
||||
meta:page-count
|
||||
GSF_META_NAME_SLIDE_COUNT:
|
||||
meta:image-count
|
||||
GSF_META_NAME_IMAGE_COUNT:
|
||||
meta:object-count
|
||||
GSF_META_NAME_OBJECT_COUNT:
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// ODF does not like "t" and "f" which we use normally
|
||||
/// </summary>
|
||||
public static void gsf_xml_out_add_gvalue_for_odf(this GsfXMLOut xout, string id, object val)
|
||||
{
|
||||
if (val is bool b)
|
||||
xout.AddString(id, b ? "true" : "false");
|
||||
else
|
||||
xout.AddValue(id, val);
|
||||
}
|
||||
|
||||
static void meta_write_props_user_defined(string prop_name, object val, GsfXMLOut output)
|
||||
{
|
||||
string type_name = null;
|
||||
|
||||
output.StartElement("meta:user-defined");
|
||||
output.AddStringUnchecked("meta:name", prop_name);
|
||||
|
||||
if (null == val)
|
||||
{
|
||||
output.EndElement();
|
||||
return;
|
||||
}
|
||||
|
||||
if (val.GetType() == typeof(char))
|
||||
type_name = "string";
|
||||
else if (val.GetType() == typeof(byte))
|
||||
type_name = "string";
|
||||
else if (val.GetType() == typeof(string))
|
||||
type_name = "string";
|
||||
else if (val.GetType().IsEnum)
|
||||
type_name = "string";
|
||||
else if (val.GetType() == typeof(bool))
|
||||
type_name = "boolean";
|
||||
else if (val.GetType() == typeof(int))
|
||||
type_name = "float";
|
||||
else if (val.GetType() == typeof(uint))
|
||||
type_name = "float";
|
||||
else if (val.GetType() == typeof(long))
|
||||
type_name = "float";
|
||||
else if (val.GetType() == typeof(ulong))
|
||||
type_name = "float";
|
||||
else if (val.GetType() == typeof(float))
|
||||
type_name = "float";
|
||||
else if (val.GetType() == typeof(double))
|
||||
type_name = "float";
|
||||
else if (val.GetType() == typeof(DateTime))
|
||||
type_name = "date";
|
||||
|
||||
if (type_name != null)
|
||||
output.AddStringUnchecked("meta:value-type", type_name);
|
||||
if (val != null)
|
||||
output.gsf_xml_out_add_gvalue_for_odf(null, val);
|
||||
|
||||
output.EndElement();
|
||||
}
|
||||
|
||||
static void meta_write_props(string prop_name, GsfDocProp prop, GsfXMLOut output)
|
||||
{
|
||||
string mapped_name;
|
||||
object val = prop.Value;
|
||||
|
||||
// Handle specially
|
||||
if (prop_name == GSF_META_NAME_KEYWORDS)
|
||||
{
|
||||
// OLE2 stores a single string, with no obvious
|
||||
// standard for seperator
|
||||
if (val.GetType() == typeof(string))
|
||||
{
|
||||
string str = (string)val;
|
||||
if (!string.IsNullOrEmpty(str))
|
||||
{
|
||||
output.StartElement("meta:keyword");
|
||||
output.AddString(null, str);
|
||||
output.EndElement();
|
||||
}
|
||||
}
|
||||
else if (val is List<GsfDocProp> list)
|
||||
{
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
string str = list[i].Name;
|
||||
output.StartElement("meta:keyword");
|
||||
output.AddString(null, str);
|
||||
output.EndElement();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ((mapped_name = od_map_prop_name(prop_name)) == null)
|
||||
{
|
||||
if (val is List<GsfDocProp> list)
|
||||
{
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
string new_name = $"GSF_DOCPROP_VECTOR:{i:4}:{prop_name}";
|
||||
meta_write_props_user_defined(new_name, list[i], output);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
meta_write_props_user_defined(prop_name, val, output);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Standardized ODF meta items
|
||||
output.StartElement(mapped_name);
|
||||
if (val != null)
|
||||
gsf_xml_out_add_gvalue_for_odf(output, null, val);
|
||||
|
||||
output.EndElement();
|
||||
}
|
||||
|
||||
/**
|
||||
* gsf_doc_meta_data_write_to_odf:
|
||||
* @md: #GsfDocMetaData
|
||||
* @output: (type GsfXMLOut): a pointer to a #GsfOutput.
|
||||
*
|
||||
* Since: 1.14.24
|
||||
*
|
||||
* Returns: %true if no error occured.
|
||||
**/
|
||||
static bool gsf_doc_meta_data_write_to_odf(GsfDocMetaData[] md, object output)
|
||||
{
|
||||
string ver_str;
|
||||
|
||||
if (output == null)
|
||||
return false;
|
||||
|
||||
// For compatibility we take a GsfXMLOut argument. It really
|
||||
// ought to be a GsfODFOut.
|
||||
GsfXMLOut xout = output as GsfXMLOut;
|
||||
GsfODFOut oout = (output is GsfODFOut) ? output as GsfODFOut : null;
|
||||
|
||||
ver_str = oout != null ? oout.GetVersionString() : GetVersionString();
|
||||
|
||||
xout.StartElement($"{OFFICE}document-meta");
|
||||
xout.AddStringUnchecked("xmlns:office", "urn:oasis:names:tc:opendocument:xmlns:office:1.0");
|
||||
xout.AddStringUnchecked("xmlns:xlink", "http://www.w3.org/1999/xlink");
|
||||
xout.AddStringUnchecked("xmlns:dc", "http://purl.org/dc/elements/1.1/");
|
||||
xout.AddStringUnchecked("xmlns:meta", "urn:oasis:names:tc:opendocument:xmlns:meta:1.0");
|
||||
xout.AddStringUnchecked("xmlns:ooo", "http://openoffice.org/2004/office");
|
||||
xout.AddStringUnchecked("office:version", ver_str);
|
||||
|
||||
xout.StartElement($"{OFFICE}meta");
|
||||
foreach (GsfDocMetaData data in md)
|
||||
{
|
||||
foreach (KeyValuePair<string, GsfDocProp> kvp in data.Table)
|
||||
{
|
||||
meta_write_props(kvp.Key, kvp.Value, xout);
|
||||
}
|
||||
}
|
||||
xout.EndElement();
|
||||
|
||||
xout.EndElement();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
723
BurnOutSharp/External/libgsf/GsfOpenPkgUtils.cs
vendored
723
BurnOutSharp/External/libgsf/GsfOpenPkgUtils.cs
vendored
@@ -1,723 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-open-pkg-utils.c: Utilities for handling Open Package zip files
|
||||
* from MS Office 2007 or XPS.
|
||||
*
|
||||
* Copyright (C) 2006-2007 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using LibGSF.Input;
|
||||
using LibGSF.Output;
|
||||
|
||||
namespace LibGSF
|
||||
{
|
||||
public class GsfOpenPkgRel
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public string Id { get; set; } = null;
|
||||
|
||||
public string Type { get; set; } = null;
|
||||
|
||||
public string Target { get; set; } = null;
|
||||
|
||||
public bool IsExtern { get; set; } = false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <returns>A new GsfInput which the called needs to unref, or null and sets <paramref name="err"/></returns>
|
||||
public static GsfInput OpenRelatedPackage(GsfInput opkg, GsfOpenPkgRel rel, ref Exception err)
|
||||
{
|
||||
if (opkg == null)
|
||||
return null;
|
||||
if (rel == null)
|
||||
return null;
|
||||
|
||||
GsfInput res = null;
|
||||
GsfInfile prev_parent;
|
||||
|
||||
// References from the root use children of opkg
|
||||
// References from a child are relative to siblings of opkg
|
||||
GsfInfile parent = opkg.Name != null ? opkg.Container : opkg as GsfInfile;
|
||||
|
||||
string target = rel.Target;
|
||||
if (target[0] == '/')
|
||||
{
|
||||
target = target.Substring(1);
|
||||
|
||||
// Handle absolute references by going as far up as we can.
|
||||
while (true)
|
||||
{
|
||||
GsfInfile next_parent = parent.Container;
|
||||
if (next_parent != null && next_parent.GetType() == parent.GetType())
|
||||
parent = next_parent;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
string[] elems = rel.Target.Split('/');
|
||||
for (int i = 0; i < elems.Length && elems[i] != null && parent != null; i++)
|
||||
{
|
||||
if (elems[i] == "." || elems[i][0] == '\0')
|
||||
continue; // Ignore '.' and empty
|
||||
|
||||
prev_parent = parent;
|
||||
if (elems[i] == "..")
|
||||
{
|
||||
parent = parent.Container;
|
||||
res = null; // Only return newly created children
|
||||
if (parent != null)
|
||||
{
|
||||
// Check for attempt to gain access outside the zip file
|
||||
if (parent.GetType() != prev_parent.GetType())
|
||||
{
|
||||
Console.Error.WriteLine("Broken file: relation access outside container");
|
||||
parent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
res = parent.ChildByName(elems[i], ref err);
|
||||
if (elems[i + 1] != null)
|
||||
{
|
||||
if (res == null || !(res is GsfInfile))
|
||||
return null;
|
||||
|
||||
parent = res as GsfInfile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds <paramref name="opkg"/>'s relation with @id
|
||||
/// </summary>
|
||||
/// <param name="id">Identifier.</param>
|
||||
/// <returns>A GsfOpenPkgRel or null</returns>
|
||||
/// <remarks>
|
||||
/// New in 1.14.6
|
||||
///
|
||||
/// Skipping because gsf_open_pkg_rel_get_type() does not return a GType.
|
||||
/// </remarks>
|
||||
public static GsfOpenPkgRel LookupRelationById(GsfInput opkg, string id)
|
||||
{
|
||||
GsfOpenPkgRels rels = GsfOpenPkgRels.LookupRelations(opkg);
|
||||
if (rels == null)
|
||||
return null;
|
||||
|
||||
if (!rels.RelationsById.ContainsKey(id))
|
||||
return null;
|
||||
|
||||
return rels.RelationsById[id];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds _a_ relation of <paramref name="opkg"/> with <paramref name="type"/> (no order is guaranteed)
|
||||
/// </summary>
|
||||
/// <param name="type">Target</param>
|
||||
/// <returns>A GsfOpenPkgRel or null</returns>
|
||||
/// <remarks>
|
||||
/// New in 1.14.6
|
||||
///
|
||||
/// Skipping because gsf_open_pkg_rel_get_type() does not return a GType.
|
||||
/// </remarks>
|
||||
public static GsfOpenPkgRel LookupRelationByType(GsfInput opkg, string type)
|
||||
{
|
||||
GsfOpenPkgRels rels = GsfOpenPkgRels.LookupRelations(opkg);
|
||||
if (rels == null)
|
||||
return null;
|
||||
|
||||
if (!rels.RelationsByType.ContainsKey(type))
|
||||
return null;
|
||||
|
||||
return rels.RelationsByType[type];
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class GsfOpenPkgRels
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// Generated based on:
|
||||
/// http://www.oasis-open.org/committees/download.php/12572/OpenDocument-v1.0-os.pdf
|
||||
/// and OpenDocument-v1.1.pdf
|
||||
/// </summary>
|
||||
private static XmlNameTable CREATE_NAMESPACES()
|
||||
{
|
||||
NameTable table = new NameTable();
|
||||
|
||||
table.Add("http://schemas.openxmlformats.org/package/2006/relationships");
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
private static XmlDocumentType CREATE_DTD(XmlDocument doc)
|
||||
{
|
||||
// Root node
|
||||
XmlDocumentType docType = doc.CreateDocumentType(null, null, null, null);
|
||||
|
||||
docType.AppendChild(doc.CreateElement("Relationships"));
|
||||
docType["Relationships"].AppendChild(doc.CreateElement("Relationship"));
|
||||
|
||||
return docType;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public Dictionary<string, GsfOpenPkgRel> RelationsById { get; set; } = new Dictionary<string, GsfOpenPkgRel>();
|
||||
|
||||
public Dictionary<string, GsfOpenPkgRel> RelationsByType { get; set; } = new Dictionary<string, GsfOpenPkgRel>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfOpenPkgRels() { }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public static GsfOpenPkgRels LookupRelations(GsfInput opkg)
|
||||
{
|
||||
if (opkg == null)
|
||||
return null;
|
||||
|
||||
GsfOpenPkgRels rels = opkg.Relations;
|
||||
if (rels == null)
|
||||
{
|
||||
string part_name = opkg.Name;
|
||||
|
||||
GsfInput rel_stream;
|
||||
if (part_name != null)
|
||||
{
|
||||
GsfInfile container = opkg.Container;
|
||||
if (container == null)
|
||||
return null;
|
||||
|
||||
string rel_name = $"{part_name}.rels";
|
||||
rel_stream = container.ChildByVariableName("_rels", rel_name);
|
||||
}
|
||||
else // The root
|
||||
{
|
||||
rel_stream = (opkg as GsfInfile).ChildByVariableName("_rels", ".rels");
|
||||
}
|
||||
|
||||
XmlDocument rel_doc;
|
||||
if (rel_stream != null)
|
||||
{
|
||||
rels = new GsfOpenPkgRels
|
||||
{
|
||||
RelationsById = new Dictionary<string, GsfOpenPkgRel>(),
|
||||
RelationsByType = new Dictionary<string, GsfOpenPkgRel>(),
|
||||
};
|
||||
|
||||
rel_doc = new XmlDocument(CREATE_NAMESPACES());
|
||||
XmlDocumentType docType = CREATE_DTD(rel_doc);
|
||||
rel_doc.AppendChild(docType);
|
||||
|
||||
// TODO: Enable parsing
|
||||
//rel_doc.Parse(rel_stream, rels);
|
||||
}
|
||||
|
||||
opkg.Relations = rels;
|
||||
}
|
||||
|
||||
return rels;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private static void OpenPkgRelBegin(GsfXMLIn xin, string[] attrs)
|
||||
{
|
||||
GsfOpenPkgRels rels = xin.UserState as GsfOpenPkgRels;
|
||||
string id = null;
|
||||
string type = null;
|
||||
string target = null;
|
||||
bool is_extern = false;
|
||||
|
||||
for (int i = 0; i < attrs.Length && attrs[i] != null && attrs[i + 1] != null; i += 2)
|
||||
{
|
||||
switch (attrs[i])
|
||||
{
|
||||
case "Id":
|
||||
id = attrs[i + 1];
|
||||
break;
|
||||
case "Type":
|
||||
type = attrs[i + 1];
|
||||
break;
|
||||
case "Target":
|
||||
target = attrs[i + 1];
|
||||
break;
|
||||
case "TargetMode":
|
||||
is_extern = attrs[i + 1] == "External";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (id == null)
|
||||
{
|
||||
Console.Error.WriteLine("Broken relation: missing id");
|
||||
id = "?";
|
||||
}
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
Console.Error.WriteLine("Broken relation: missing type");
|
||||
type = "?";
|
||||
}
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
Console.Error.WriteLine("Broken relation: missing target");
|
||||
target = "?";
|
||||
}
|
||||
|
||||
GsfOpenPkgRel rel = new GsfOpenPkgRel
|
||||
{
|
||||
Id = id,
|
||||
Type = type,
|
||||
Target = target,
|
||||
IsExtern = is_extern,
|
||||
};
|
||||
|
||||
// Make sure we don't point to a freed rel in the type hash.
|
||||
rels.RelationsByType[rel.Type] = rel;
|
||||
|
||||
// This will free a duplicate rel, so do this last.
|
||||
rels.RelationsById[rel.Id] = rel;
|
||||
}
|
||||
|
||||
#endregion
|
||||
};
|
||||
|
||||
public class GsfOutfileOpenPkg : GsfOutfile
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public GsfOutput Sink { get; set; } = null;
|
||||
|
||||
public bool IsDir { get; set; } = false;
|
||||
|
||||
public string ContentType { get; set; } = null;
|
||||
|
||||
public List<GsfOutfileOpenPkg> Children { get; set; } = null;
|
||||
|
||||
public List<GsfOpenPkgRel> Relations { get; set; } = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfOutfileOpenPkg() { }
|
||||
|
||||
/// <summary>
|
||||
/// Convenience routine to create a GsfOutfileOpenPkg inside <paramref name="sink"/>.
|
||||
/// </summary>
|
||||
/// <param name="sink"></param>
|
||||
/// <returns>A GsfOutfile that the caller is responsible for.</returns>
|
||||
public static GsfOutfileOpenPkg Create(GsfOutfile sink)
|
||||
{
|
||||
return new GsfOutfileOpenPkg
|
||||
{
|
||||
Sink = sink,
|
||||
IsDir = true,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfOutfileOpenPkg()
|
||||
{
|
||||
if (Sink != null)
|
||||
Sink = null;
|
||||
|
||||
ContentType = null;
|
||||
|
||||
Children.Clear();
|
||||
Children = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public new bool Close()
|
||||
{
|
||||
GsfOutput dir;
|
||||
bool res = false;
|
||||
string rels_name;
|
||||
|
||||
if (Sink == null || Sink.IsClosed)
|
||||
return true;
|
||||
|
||||
// Generate [Content_types].xml when we close the root dir
|
||||
if (Name == null)
|
||||
{
|
||||
GsfOutput output = (Sink as GsfOutfile).NewChild("[Content_Types].xml", false);
|
||||
GsfXMLOut xml = GsfXMLOut.Create(output);
|
||||
|
||||
xml.StartElement("Types");
|
||||
xml.AddStringUnchecked("xmlns", "http://schemas.openxmlformats.org/package/2006/content-types");
|
||||
xml.WriteContentDefault("rels", "application/vnd.openxmlformats-package.relationships+xml");
|
||||
xml.WriteContentDefault("xlbin", "application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings");
|
||||
xml.WriteContentDefault("xml", "application/xml");
|
||||
xml.WriteContentDefault("vml", "application/vnd.openxmlformats-officedocument.vmlDrawing");
|
||||
xml.WriteContentOverride(this, "/");
|
||||
xml.EndElement();
|
||||
|
||||
output.Close();
|
||||
|
||||
dir = Sink;
|
||||
rels_name = ".rels";
|
||||
}
|
||||
else
|
||||
{
|
||||
res = Sink.Close();
|
||||
dir = Sink.Container;
|
||||
rels_name = $"{Name}.rels";
|
||||
}
|
||||
|
||||
if (Relations != null)
|
||||
{
|
||||
dir = (dir as GsfOutfile).NewChild("_rels", true);
|
||||
GsfOutput rels = (dir as GsfOutfile).NewChild(rels_name, false);
|
||||
GsfXMLOut xml = GsfXMLOut.Create(rels);
|
||||
|
||||
xml.StartElement("Relationships");
|
||||
xml.AddStringUnchecked("xmlns", "http://schemas.openxmlformats.org/package/2006/relationships");
|
||||
|
||||
foreach (GsfOpenPkgRel rel in Relations)
|
||||
{
|
||||
xml.StartElement("Relationship");
|
||||
xml.AddString("Id", rel.Id);
|
||||
xml.AddString("Type", rel.Type);
|
||||
xml.AddString("Target", rel.Target);
|
||||
if (rel.IsExtern)
|
||||
xml.AddStringUnchecked("TargetMode", "External");
|
||||
|
||||
xml.EndElement();
|
||||
}
|
||||
|
||||
Relations.Clear();
|
||||
xml.EndElement();
|
||||
rels.Close();
|
||||
}
|
||||
|
||||
// Close the container
|
||||
if (Name == null)
|
||||
return Sink.Close();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public string CreateRelation(string target, string type, bool is_extern)
|
||||
{
|
||||
GsfOpenPkgRel rel = new GsfOpenPkgRel
|
||||
{
|
||||
Target = target,
|
||||
Type = type,
|
||||
Id = $"rID{Relations.Count + 1}",
|
||||
IsExtern = is_extern,
|
||||
};
|
||||
|
||||
Relations.Add(rel);
|
||||
return rel.Id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a relationship between child and <paramref name="parent"/> of <paramref name="type"/>.
|
||||
/// </summary>
|
||||
/// <param name="type">Target type</param>
|
||||
/// <returns>The relID which the caller does not own but will live as long as <paramref name="parent"/>.</returns>
|
||||
public string RelatePackages(GsfOutfileOpenPkg parent, string type)
|
||||
{
|
||||
int up = -1;
|
||||
GsfOutfile child_dir;
|
||||
|
||||
// Calculate the path from child to parent
|
||||
GsfOutfile parent_dir = parent.IsDir ? parent : parent.Container;
|
||||
do
|
||||
{
|
||||
up++;
|
||||
child_dir = this;
|
||||
while ((child_dir = child_dir.Container) != null)
|
||||
{
|
||||
if (child_dir == parent_dir)
|
||||
goto found; // Break out of both loops
|
||||
}
|
||||
} while ((parent_dir = parent_dir.Container) != null);
|
||||
|
||||
// TODO: Figure out how to best remove this goto
|
||||
found:
|
||||
// Yes prepend is slow, this will never be preformance critical
|
||||
string path = Name;
|
||||
child_dir = this;
|
||||
while ((child_dir = child_dir.Container) != null && child_dir.Name != null && child_dir != parent_dir)
|
||||
{
|
||||
path = $"{child_dir.Name}/{path}";
|
||||
}
|
||||
|
||||
while (up-- != 0)
|
||||
{
|
||||
path = $"../{path}";
|
||||
}
|
||||
|
||||
return parent.CreateRelation(path, type, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A convenience wrapper to create a child in <paramref name="dir"/> of <paramref name="content_type"/> then create
|
||||
/// a <paramref name="type"/> relation to <paramref name="parent"/>
|
||||
/// </summary>
|
||||
/// <param name="name">Target name</param>
|
||||
/// <param name="content_type">Non-null content type</param>
|
||||
/// <param name="type">Target type</param>
|
||||
/// <returns>The new part.</returns>
|
||||
public GsfOutput AddRelation(string name, string content_type, GsfOutfile parent, string type)
|
||||
{
|
||||
GsfOutfileOpenPkg part = NewChild(name, false) as GsfOutfileOpenPkg;
|
||||
if (part == null)
|
||||
return null;
|
||||
|
||||
part.ContentType = content_type;
|
||||
part.RelatePackages(parent as GsfOutfileOpenPkg, type);
|
||||
return part;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an external relation to parent.
|
||||
/// </summary>
|
||||
/// <param name="target">Target type</param>
|
||||
/// <param name="content_type">Target content</param>
|
||||
/// <returns>
|
||||
/// The id of the relation. The string is
|
||||
/// managed by the parent and should not be changed or freed by the
|
||||
/// caller.
|
||||
/// </returns>
|
||||
public string AddExternalRelation(string target, string content_type) => CreateRelation(target, content_type, true);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool WriteImpl(int num_bytes, byte[] data) => Sink.Write(num_bytes, data);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence) => Sink.Seek(offset, whence);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfOutput NewChild(string name, bool is_dir)
|
||||
{
|
||||
if (!is_dir)
|
||||
return null;
|
||||
|
||||
GsfOutfileOpenPkg child = new GsfOutfileOpenPkg
|
||||
{
|
||||
Name = name,
|
||||
Container = this,
|
||||
IsDir = is_dir,
|
||||
};
|
||||
|
||||
// Holding a ref here is not ideal. It means we won't release any of the
|
||||
// children until the package is closed.
|
||||
Children.Add(child);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static class GsfOpenPkgUtils
|
||||
{
|
||||
#region Delegates
|
||||
|
||||
public delegate void GsfOpenPkgIter(GsfInput opkg, GsfOpenPkgRel rel, object user_data);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Classes
|
||||
|
||||
private class pkg_iter_data
|
||||
{
|
||||
public GsfInput opkg { get; set; }
|
||||
|
||||
public GsfOpenPkgIter func { get; set; }
|
||||
|
||||
public object user_data { get; set; }
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Walks each relationship associated with <paramref name="opkg"/> and calls <paramref name="func"/> with <paramref name="user_data"/>.
|
||||
/// </summary>
|
||||
/// <remarks>New in 1.14.9</remarks>
|
||||
public static void ForeachRelation(this GsfInput opkg, GsfOpenPkgIter func, object user_data)
|
||||
{
|
||||
GsfOpenPkgRels rels = GsfOpenPkgRels.LookupRelations(opkg);
|
||||
if (rels == null)
|
||||
return;
|
||||
|
||||
pkg_iter_data dat = new pkg_iter_data
|
||||
{
|
||||
opkg = opkg,
|
||||
func = func,
|
||||
user_data = user_data,
|
||||
};
|
||||
|
||||
foreach (KeyValuePair<string, GsfOpenPkgRel> rel in rels.RelationsById)
|
||||
{
|
||||
ForeachRelationImpl(rel.Key, rel.Value, dat);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open @opkg's relation <paramref name="id"/>
|
||||
/// </summary>
|
||||
/// <param name="id">Target id</param>
|
||||
/// <returns>A new GsfInput or null, and sets <paramref name="err"/> if possible.</returns>
|
||||
/// <remarks>New in 1.14.7</remarks>
|
||||
public static GsfInput RelationById(this GsfInput opkg, string id, ref Exception err)
|
||||
{
|
||||
GsfOpenPkgRel rel = GsfOpenPkgRel.LookupRelationById(opkg, id);
|
||||
if (rel != null)
|
||||
return GsfOpenPkgRel.OpenRelatedPackage(opkg, rel, ref err);
|
||||
|
||||
err = new Exception($"Unable to find part id='{id}' for '{opkg.Name}'");
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open one of <paramref name="opkg"/>'s relationships with type=<paramref name="type"/>.
|
||||
/// </summary>
|
||||
/// <param name="type">Target type</param>
|
||||
/// <returns>A new GsfInput or null, and sets <paramref name="err"/> if possible.</returns>
|
||||
/// <remarks>New in 1.14.9</remarks>
|
||||
public static GsfInput RelationByType(this GsfInput opkg, string type, ref Exception err)
|
||||
{
|
||||
GsfOpenPkgRel rel = GsfOpenPkgRel.LookupRelationByType(opkg, type);
|
||||
if (rel != null)
|
||||
return GsfOpenPkgRel.OpenRelatedPackage(opkg, rel, ref err);
|
||||
|
||||
err = new Exception($"Unable to find part with type='{type}' for '{opkg.Name}'");
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convenience function to parse a related part.
|
||||
/// </summary>
|
||||
/// <param name="id">Target id</param>
|
||||
/// <returns>null on success or an Exception on failure.</returns>
|
||||
public static Exception ParseRelationById(this GsfXMLIn xin, string id, XmlDocumentType dtd, XmlNameTable ns)
|
||||
{
|
||||
if (xin == null)
|
||||
return null;
|
||||
|
||||
GsfInput cur_stream = xin.Input;
|
||||
if (id == null)
|
||||
return new Exception($"Missing id for part in '{cur_stream.Name}'");
|
||||
|
||||
Exception res = null;
|
||||
GsfInput part_stream = RelationById(cur_stream, id, ref res);
|
||||
if (part_stream != null)
|
||||
{
|
||||
XmlDocument doc = new XmlDocument(ns);
|
||||
doc.AppendChild(dtd);
|
||||
|
||||
// TODO: Enable parsing
|
||||
//if (!doc.Parse(part_stream, xin.UserState))
|
||||
// res = new Exception($"Part '{id}' in '{part_stream.Name}' from '{cur_stream.Name}' is corrupt!");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilties
|
||||
|
||||
private static void ForeachRelationImpl(object id, GsfOpenPkgRel rel, pkg_iter_data dat)
|
||||
{
|
||||
dat.func(dat.opkg, rel, dat.user_data);
|
||||
}
|
||||
|
||||
internal static void WriteContentDefault(this GsfXMLOut xml, string ext, string type)
|
||||
{
|
||||
xml.StartElement("Default");
|
||||
xml.AddString("Extension", ext);
|
||||
xml.AddString("ContentType", type);
|
||||
xml.EndElement();
|
||||
}
|
||||
|
||||
internal static void WriteContentOverride(this GsfXMLOut xml, GsfOutfileOpenPkg open_pkg, string baseName)
|
||||
{
|
||||
foreach (GsfOutfileOpenPkg child in open_pkg.Children)
|
||||
{
|
||||
string path;
|
||||
if (child.IsDir)
|
||||
{
|
||||
path = $"{child.Name}/";
|
||||
xml.WriteContentOverride(child, path);
|
||||
}
|
||||
else
|
||||
{
|
||||
path = $"{baseName}{child.Name}";
|
||||
|
||||
// Rels files do need content types, the defaults handle them
|
||||
if (child.ContentType != null)
|
||||
{
|
||||
xml.StartElement("Override");
|
||||
xml.AddString("PartName", path);
|
||||
xml.AddString("ContentType", child.ContentType);
|
||||
xml.EndElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dispose of children here to break link cycles.
|
||||
open_pkg.Children.Clear();
|
||||
open_pkg.Children = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
90
BurnOutSharp/External/libgsf/GsfSharedMemory.cs
vendored
90
BurnOutSharp/External/libgsf/GsfSharedMemory.cs
vendored
@@ -1,90 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-shared-memory.c:
|
||||
*
|
||||
* Copyright (C) 2002-2006 Morten Welinder (terra@diku.dk)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace LibGSF
|
||||
{
|
||||
public class GsfSharedMemory
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public byte[] Buf { get; set; } = null;
|
||||
|
||||
public long Size { get; set; }
|
||||
|
||||
public bool NeedsFree { get; set; }
|
||||
|
||||
public bool NeedsUnmap { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfSharedMemory() { }
|
||||
|
||||
public static GsfSharedMemory Create(byte[] buf, long size, bool needs_free)
|
||||
{
|
||||
return new GsfSharedMemory
|
||||
{
|
||||
Buf = buf,
|
||||
Size = size,
|
||||
NeedsFree = needs_free,
|
||||
NeedsUnmap = false,
|
||||
};
|
||||
}
|
||||
|
||||
public static GsfSharedMemory CreateMemoryMapped(byte[] buf, long size)
|
||||
{
|
||||
int msize = (int)size;
|
||||
if (msize != size)
|
||||
{
|
||||
Console.Error.WriteLine("Memory buffer size too large");
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
GsfSharedMemory mem = Create(buf, size, false);
|
||||
mem.NeedsUnmap = true;
|
||||
return mem;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfSharedMemory()
|
||||
{
|
||||
if (Buf != null)
|
||||
{
|
||||
if (NeedsFree)
|
||||
Buf = null;
|
||||
//else if (NeedsUnmap)
|
||||
//UnmapViewOfFile(mem.buf);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
266
BurnOutSharp/External/libgsf/GsfUtils.cs
vendored
266
BurnOutSharp/External/libgsf/GsfUtils.cs
vendored
@@ -1,266 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-utils.c:
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace LibGSF
|
||||
{
|
||||
/// <summary>
|
||||
/// Do this the ugly way so that we don't have to worry about alignment
|
||||
/// </summary>
|
||||
internal static class GsfUtils
|
||||
{
|
||||
#region GET
|
||||
|
||||
/// <summary>
|
||||
/// Interpret binary data as an unsigned 8-bit integer in little endian order.
|
||||
/// </summary>
|
||||
/// <param name="data">Storage</param>
|
||||
/// <param name="p">Pointer to storage</param>
|
||||
/// <returns>Interpreted data</returns>
|
||||
public static byte GSF_LE_GET_GUINT8(byte[] data, int p)
|
||||
=> data[p];
|
||||
|
||||
/// <summary>
|
||||
/// Interpret binary data as an unsigned 16-bit integer in little endian order.
|
||||
/// </summary>
|
||||
/// <param name="data">Storage</param>
|
||||
/// <param name="p">Pointer to storage</param>
|
||||
/// <returns>Interpreted data</returns>
|
||||
public static ushort GSF_LE_GET_GUINT16(byte[] data, int p)
|
||||
=> (ushort)((data[p] << 0)
|
||||
| (data[p + 1] << 8));
|
||||
|
||||
/// <summary>
|
||||
/// Interpret binary data as an unsigned 32-bit integer in little endian order.
|
||||
/// </summary>
|
||||
/// <param name="data">Storage</param>
|
||||
/// <param name="p">Pointer to storage</param>
|
||||
/// <returns>Interpreted data</returns>
|
||||
public static uint GSF_LE_GET_GUINT32(byte[] data, int p)
|
||||
=> (uint)((data[p] << 0)
|
||||
| (data[p + 1] << 8)
|
||||
| (data[p + 2] << 16)
|
||||
| (data[p + 3] << 24));
|
||||
|
||||
/// <summary>
|
||||
/// Interpret binary data as an unsigned 64-bit integer in little endian order.
|
||||
/// </summary>
|
||||
/// <param name="data">Storage</param>
|
||||
/// <param name="p">Pointer to storage</param>
|
||||
/// <returns>Interpreted data</returns>
|
||||
public static ulong GSF_LE_GET_GUINT64(byte[] data, int p)
|
||||
=> (uint)((data[p] << 0)
|
||||
| (data[p + 1] << 8)
|
||||
| (data[p + 2] << 16)
|
||||
| (data[p + 3] << 24)
|
||||
| (data[p + 4] << 32)
|
||||
| (data[p + 5] << 40)
|
||||
| (data[p + 6] << 48)
|
||||
| (data[p + 7] << 56));
|
||||
|
||||
/// <summary>
|
||||
/// Interpret binary data as a signed 8-bit integer in little endian order.
|
||||
/// </summary>
|
||||
/// <param name="data">Storage</param>
|
||||
/// <param name="p">Pointer to storage</param>
|
||||
/// <returns>Interpreted data</returns>
|
||||
public static sbyte GSF_LE_GET_GINT8(byte[] data, int p) => (sbyte)GSF_LE_GET_GINT8(data, p);
|
||||
|
||||
/// <summary>
|
||||
/// Interpret binary data as a signed 16-bit integer in little endian order.
|
||||
/// </summary>
|
||||
/// <param name="data">Storage</param>
|
||||
/// <param name="p">Pointer to storage</param>
|
||||
/// <returns>Interpreted data</returns>
|
||||
public static short GSF_LE_GET_GINT16(byte[] data, int p) => (short)GSF_LE_GET_GUINT16(data, p);
|
||||
|
||||
/// <summary>
|
||||
/// Interpret binary data as a signed 32-bit integer in little endian order.
|
||||
/// </summary>
|
||||
/// <param name="data">Storage</param>
|
||||
/// <param name="p">Pointer to storage</param>
|
||||
/// <returns>Interpreted data</returns>
|
||||
public static int GSF_LE_GET_GINT32(byte[] data, int p) => (int)GSF_LE_GET_GUINT32(data, p);
|
||||
|
||||
/// <summary>
|
||||
/// Interpret binary data as a signed 64-bit integer in little endian order.
|
||||
/// </summary>
|
||||
/// <param name="data">Storage</param>
|
||||
/// <param name="p">Pointer to storage</param>
|
||||
/// <returns>Interpreted data</returns>
|
||||
public static long GSF_LE_GET_GINT64(byte[] data, int p) => (long)GSF_LE_GET_GUINT64(data, p);
|
||||
|
||||
/// <summary>
|
||||
/// Interpret binary data as a float in little endian order.
|
||||
/// </summary>
|
||||
/// <param name="data">Storage</param>
|
||||
/// <param name="p">Pointer to storage</param>
|
||||
/// <returns>Interpreted data</returns>
|
||||
public static float GSF_LE_GET_FLOAT(byte[] data, int p)
|
||||
{
|
||||
byte[] temp = new ReadOnlySpan<byte>(data, p, 4).ToArray();
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Array.Reverse(temp);
|
||||
|
||||
return BitConverter.ToSingle(temp, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interpret binary data as a double in little endian order.
|
||||
/// </summary>
|
||||
/// <param name="data">Storage</param>
|
||||
/// <param name="p">Pointer to storage</param>
|
||||
/// <returns>Interpreted data</returns>
|
||||
public static double GSF_LE_GET_DOUBLE(byte[] data, int p)
|
||||
{
|
||||
byte[] temp = new ReadOnlySpan<byte>(data, p, 8).ToArray();
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Array.Reverse(temp);
|
||||
|
||||
return BitConverter.ToDouble(temp, 0);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region SET
|
||||
|
||||
/// <summary>
|
||||
/// Store <paramref name="dat"/> in little endian order in memory pointed to by <paramref name="p"/>.
|
||||
/// </summary>
|
||||
/// <param name="data">Storage</param>
|
||||
/// <param name="p">Pointer to storage</param>
|
||||
/// <param name="dat">8-bit unsigned integer</param>
|
||||
public static void GSF_LE_SET_GUINT8(byte[] data, int p, byte dat)
|
||||
{
|
||||
data[p] = (byte)(dat & 0xff);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Store <paramref name="dat"/> in little endian order in memory pointed to by <paramref name="p"/>.
|
||||
/// </summary>
|
||||
/// <param name="data">Storage</param>
|
||||
/// <param name="p">Pointer to storage</param>
|
||||
/// <param name="dat">16-bit unsigned integer</param>
|
||||
public static void GSF_LE_SET_GUINT16(byte[] data, int p, ushort dat)
|
||||
{
|
||||
data[p + 0] = (byte)(dat & 0xff);
|
||||
data[p + 1] = (byte)((dat >> 8) & 0xff);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Store <paramref name="dat"/> in little endian order in memory pointed to by <paramref name="p"/>.
|
||||
/// </summary>
|
||||
/// <param name="data">Storage</param>
|
||||
/// <param name="p">Pointer to storage</param>
|
||||
/// <param name="dat">32-bit unsigned integer</param>
|
||||
public static void GSF_LE_SET_GUINT32(byte[] data, int p, uint dat)
|
||||
{
|
||||
data[p + 0] = (byte)(dat & 0xff);
|
||||
data[p + 1] = (byte)((dat >> 8) & 0xff);
|
||||
data[p + 2] = (byte)((dat >> 16) & 0xff);
|
||||
data[p + 3] = (byte)((dat >> 24) & 0xff);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Store <paramref name="dat"/> in little endian order in memory pointed to by <paramref name="p"/>.
|
||||
/// </summary>
|
||||
/// <param name="data">Storage</param>
|
||||
/// <param name="p">Pointer to storage</param>
|
||||
/// <param name="dat">64-bit unsigned integer</param>
|
||||
public static void GSF_LE_SET_GUINT64(byte[] data, int p, ulong dat)
|
||||
{
|
||||
data[p + 0] = (byte)(dat & 0xff);
|
||||
data[p + 1] = (byte)((dat >> 8) & 0xff);
|
||||
data[p + 2] = (byte)((dat >> 16) & 0xff);
|
||||
data[p + 3] = (byte)((dat >> 24) & 0xff);
|
||||
data[p + 4] = (byte)((dat >> 32) & 0xff);
|
||||
data[p + 5] = (byte)((dat >> 40) & 0xff);
|
||||
data[p + 6] = (byte)((dat >> 48) & 0xff);
|
||||
data[p + 7] = (byte)((dat >> 56) & 0xff);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Store <paramref name="dat"/> in little endian order in memory pointed to by <paramref name="p"/>.
|
||||
/// </summary>
|
||||
/// <param name="data">Storage</param>
|
||||
/// <param name="p">Pointer to storage</param>
|
||||
/// <param name="dat">8-bit signed integer</param>
|
||||
public static void GSF_LE_SET_GINT8(byte[] data, int p, sbyte dat) => GSF_LE_SET_GUINT8(data, p, (byte)dat);
|
||||
|
||||
/// <summary>
|
||||
/// Store <paramref name="dat"/> in little endian order in memory pointed to by <paramref name="p"/>.
|
||||
/// </summary>
|
||||
/// <param name="data">Storage</param>
|
||||
/// <param name="p">Pointer to storage</param>
|
||||
/// <param name="dat">16-bit signed integer</param>
|
||||
public static void GSF_LE_SET_GINT16(byte[] data, int p, short dat) => GSF_LE_SET_GUINT16(data, p, (ushort)dat);
|
||||
|
||||
/// <summary>
|
||||
/// Store <paramref name="dat"/> in little endian order in memory pointed to by <paramref name="p"/>.
|
||||
/// </summary>
|
||||
/// <param name="data">Storage</param>
|
||||
/// <param name="p">Pointer to storage</param>
|
||||
/// <param name="dat">32-bit signed integer</param>
|
||||
public static void GSF_LE_SET_GINT32(byte[] data, int p, int dat) => GSF_LE_SET_GUINT32(data, p, (uint)dat);
|
||||
|
||||
/// <summary>
|
||||
/// Store <paramref name="dat"/> in little endian order in memory pointed to by <paramref name="p"/>.
|
||||
/// </summary>
|
||||
/// <param name="data">Storage</param>
|
||||
/// <param name="p">Pointer to storage</param>
|
||||
/// <param name="dat">64-bit signed integer</param>
|
||||
public static void GSF_LE_SET_GINT64(byte[] data, int p, long dat) => GSF_LE_SET_GUINT64(data, p, (ulong)dat);
|
||||
|
||||
/// <summary>
|
||||
/// Store <paramref name="dat"/> in little endian order in memory pointed to by <paramref name="p"/>.
|
||||
/// </summary>
|
||||
/// <param name="data">Storage</param>
|
||||
/// <param name="p">Pointer to storage</param>
|
||||
/// <param name="dat">Float to be stored</param>
|
||||
public static void GSF_LE_SET_FLOAT(byte[] data, int p, float dat)
|
||||
{
|
||||
byte[] temp = BitConverter.GetBytes(dat);
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Array.Reverse(temp);
|
||||
|
||||
Array.Copy(temp, 0, data, p, 4);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Store <paramref name="dat"/> in little endian order in memory pointed to by <paramref name="p"/>.
|
||||
/// </summary>
|
||||
/// <param name="data">Storage</param>
|
||||
/// <param name="p">Pointer to storage</param>
|
||||
/// <param name="dat">Double to be stored</param>
|
||||
public static void GSF_LE_SET_DOUBLE(byte[] data, int p, double dat)
|
||||
{
|
||||
byte[] temp = BitConverter.GetBytes(dat);
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
Array.Reverse(temp);
|
||||
|
||||
Array.Copy(temp, 0, data, p, 8);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
389
BurnOutSharp/External/libgsf/GsfZipImpl.cs
vendored
389
BurnOutSharp/External/libgsf/GsfZipImpl.cs
vendored
@@ -1,389 +0,0 @@
|
||||
/* THIS IS NOT INSTALLED */
|
||||
|
||||
/*
|
||||
* gsf-zip-impl.h:
|
||||
*
|
||||
* Copyright (C) 2002-2006 Tambet Ingo (tambet@ximian.com)
|
||||
* Copyright (C) 2014 Morten Welinder (terra@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LibGSF
|
||||
{
|
||||
#region Enums
|
||||
|
||||
/// <summary>
|
||||
/// A few well-defined extra-field tags.
|
||||
/// </summary>
|
||||
public enum ExtraFieldTags : ushort
|
||||
{
|
||||
ZIP_DIRENT_EXTRA_FIELD_ZIP64 = 0x0001,
|
||||
|
||||
/// <summary>
|
||||
/// "II" -- gsf defined
|
||||
/// </summary>
|
||||
ZIP_DIRENT_EXTRA_FIELD_IGNORE = 0x4949,
|
||||
|
||||
/// <summary>
|
||||
/// "UT"
|
||||
/// </summary>
|
||||
ZIP_DIRENT_EXTRA_FIELD_UNIXTIME = 0x5455,
|
||||
|
||||
/// <summary>
|
||||
/// "ux"
|
||||
/// </summary>
|
||||
ZIP_DIRENT_EXTRA_FIELD_UIDGID = 0x7875
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// OS codes. There are plenty, but this is all we need.
|
||||
/// </summary>
|
||||
public enum OSCodes
|
||||
{
|
||||
ZIP_OS_MSDOS = 0,
|
||||
ZIP_OS_UNIX = 3
|
||||
};
|
||||
|
||||
/// <remarks>From gsf-outfile-zip.h</remarks>
|
||||
public enum GsfZipCompressionMethod
|
||||
{
|
||||
/// <summary>
|
||||
/// Supported for export
|
||||
/// </summary>
|
||||
GSF_ZIP_STORED = 0,
|
||||
GSF_ZIP_SHRUNK = 1,
|
||||
GSF_ZIP_REDUCEDx1 = 2,
|
||||
GSF_ZIP_REDUCEDx2 = 3,
|
||||
GSF_ZIP_REDUCEDx3 = 4,
|
||||
GSF_ZIP_REDUCEDx4 = 5,
|
||||
GSF_ZIP_IMPLODED = 6,
|
||||
GSF_ZIP_TOKENIZED = 7,
|
||||
|
||||
/// <summary>
|
||||
/// Supported for export
|
||||
/// </summary>
|
||||
GSF_ZIP_DEFLATED = 8,
|
||||
GSF_ZIP_DEFLATED_BETTER = 9,
|
||||
GSF_ZIP_IMPLODED_BETTER = 10
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Classes
|
||||
|
||||
public class GsfZipDirectoryEntry
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public int Flags { get; set; }
|
||||
|
||||
public GsfZipCompressionMethod CompressionMethod { get; set; }
|
||||
|
||||
public uint CRC32 { get; set; }
|
||||
|
||||
public long CompressedSize { get; set; }
|
||||
|
||||
public long UncompressedSize { get; set; }
|
||||
|
||||
public long Offset { get; set; }
|
||||
|
||||
public long DataOffset { get; set; }
|
||||
|
||||
public uint DosTime { get; set; }
|
||||
|
||||
public DateTime? ModifiedTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// null = auto, FALSE, TRUE.
|
||||
/// </summary>
|
||||
public bool? Zip64 { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfZipDirectoryEntry() { }
|
||||
|
||||
/// <summary>
|
||||
/// Doesn't do much, but include for symmetry
|
||||
/// </summary>
|
||||
public static GsfZipDirectoryEntry Create() => new GsfZipDirectoryEntry();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public void Free()
|
||||
{
|
||||
Name = null;
|
||||
}
|
||||
|
||||
public GsfZipDirectoryEntry Copy()
|
||||
{
|
||||
return new GsfZipDirectoryEntry
|
||||
{
|
||||
Name = Name,
|
||||
Flags = Flags,
|
||||
CompressionMethod = CompressionMethod,
|
||||
CRC32 = CRC32,
|
||||
CompressedSize = CompressedSize,
|
||||
UncompressedSize = UncompressedSize,
|
||||
Offset = Offset,
|
||||
DataOffset = DataOffset,
|
||||
DosTime = DosTime,
|
||||
ModifiedTime = ModifiedTime,
|
||||
Zip64 = Zip64,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class GsfZipVDir
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public bool IsDirectory { get; set; }
|
||||
|
||||
public GsfZipDirectoryEntry DirectoryEntry { get; set; }
|
||||
|
||||
public List<GsfZipVDir> Children { get; set; }
|
||||
|
||||
//public GSList* last_child { get; set; } /* Unused */
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfZipVDir() { }
|
||||
|
||||
/// <returns>The newly created GsfZipVDir.</returns>
|
||||
/// <remarks>Since: 1.14.24</remarks>
|
||||
public static GsfZipVDir Create(string name, bool is_directory, GsfZipDirectoryEntry dirent)
|
||||
{
|
||||
return new GsfZipVDir
|
||||
{
|
||||
Name = name,
|
||||
IsDirectory = is_directory,
|
||||
DirectoryEntry = dirent,
|
||||
Children = new List<GsfZipVDir>(),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfZipVDir() => Free(true);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public void Free(bool free_dirent)
|
||||
{
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
GsfZipVDir c = Children[i];
|
||||
c.Free(free_dirent);
|
||||
}
|
||||
|
||||
Children.Clear();
|
||||
Name = null;
|
||||
if (free_dirent && DirectoryEntry != null)
|
||||
DirectoryEntry.Free();
|
||||
}
|
||||
|
||||
public GsfZipVDir Copy()
|
||||
{
|
||||
GsfZipVDir res = new GsfZipVDir();
|
||||
|
||||
// It is not possible to add a ref_count without breaking the API,
|
||||
// so we need to really copy everything
|
||||
if (Name != null)
|
||||
res.Name = Name;
|
||||
|
||||
res.IsDirectory = IsDirectory;
|
||||
if (DirectoryEntry != null)
|
||||
res.DirectoryEntry = DirectoryEntry.Copy();
|
||||
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
GsfZipVDir c = Children[i];
|
||||
res.AddChild(c.Copy());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public void AddChild(GsfZipVDir child)
|
||||
{
|
||||
Children.Add(child);
|
||||
}
|
||||
|
||||
public GsfZipVDir ChildByName(string name)
|
||||
{
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
GsfZipVDir child = Children[i];
|
||||
if (child.Name == name)
|
||||
return child;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public GsfZipVDir ChildByIndex(int target) => target < Children.Count ? Children[target] : null;
|
||||
|
||||
public void Insert(string name, GsfZipDirectoryEntry dirent)
|
||||
{
|
||||
int p = name.IndexOf(GsfZipImpl.ZIP_NAME_SEPARATOR);
|
||||
if (p != -1)
|
||||
{
|
||||
// A directory
|
||||
string dirname = name.Substring(0, p);
|
||||
GsfZipVDir child = ChildByName(dirname);
|
||||
if (child == null)
|
||||
{
|
||||
child = Create(dirname, true, null);
|
||||
AddChild(child);
|
||||
}
|
||||
|
||||
if (p + 1 < name.Length && name[p + 1] != '\0')
|
||||
{
|
||||
name = name.Substring(p + 1);
|
||||
child.Insert(name, dirent);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// A simple file name
|
||||
GsfZipVDir child = Create(name, false, dirent);
|
||||
AddChild(child);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static class GsfZipImpl
|
||||
{
|
||||
// Every member file is preceded by a header with this format.
|
||||
public const uint ZIP_HEADER_SIGNATURE = 0x04034b50;
|
||||
public const int ZIP_HEADER_SIZE = 30;
|
||||
public const int ZIP_HEADER_EXTRACT = 4;
|
||||
public const int ZIP_HEADER_FLAGS = 6;
|
||||
public const int ZIP_HEADER_COMP_METHOD = 8;
|
||||
public const int ZIP_HEADER_DOSTIME = 10;
|
||||
public const int ZIP_HEADER_CRC32 = 14;
|
||||
public const int ZIP_HEADER_CSIZE = 18;
|
||||
public const int ZIP_HEADER_USIZE = 22;
|
||||
public const int ZIP_HEADER_NAME_SIZE = 26;
|
||||
public const int ZIP_HEADER_EXTRAS_SIZE = 28;
|
||||
|
||||
// Members may have this record after the compressed data. It is meant
|
||||
// to be used only when it is not possible to seek back and patch the
|
||||
// right values into the header.
|
||||
public const uint ZIP_DDESC_SIGNATURE = 0x08074b50;
|
||||
public const int ZIP_DDESC_SIZE = 16;
|
||||
public const int ZIP_DDESC_CRC32 = 4;
|
||||
public const int ZIP_DDESC_CSIZE = 8;
|
||||
public const int ZIP_DDESC_USIZE = 12;
|
||||
|
||||
// 64-bit version of above. Used when the ZIP64 extra field is present
|
||||
// in the header.
|
||||
public const uint ZIP_DDESC64_SIGNATURE = ZIP_DDESC_SIGNATURE;
|
||||
public const int ZIP_DDESC64_SIZE = 24;
|
||||
public const int ZIP_DDESC64_CRC32 = 4;
|
||||
public const int ZIP_DDESC64_CSIZE = 8;
|
||||
public const int ZIP_DDESC64_USIZE = 16;
|
||||
|
||||
// The whole archive ends with a trailer.
|
||||
public const uint ZIP_TRAILER_SIGNATURE = 0x06054b50;
|
||||
public const int ZIP_TRAILER_SIZE = 22;
|
||||
public const int ZIP_TRAILER_DISK = 4;
|
||||
public const int ZIP_TRAILER_DIR_DISK = 6;
|
||||
public const int ZIP_TRAILER_ENTRIES = 8;
|
||||
public const int ZIP_TRAILER_TOTAL_ENTRIES = 10;
|
||||
public const int ZIP_TRAILER_DIR_SIZE = 12;
|
||||
public const int ZIP_TRAILER_DIR_POS = 16;
|
||||
public const int ZIP_TRAILER_COMMENT_SIZE = 20;
|
||||
|
||||
// A zip64 locator comes immediately before the trailer, if it is present.
|
||||
public const uint ZIP_ZIP64_LOCATOR_SIGNATURE = 0x07064b50;
|
||||
public const int ZIP_ZIP64_LOCATOR_SIZE = 20;
|
||||
public const int ZIP_ZIP64_LOCATOR_DISK = 4;
|
||||
public const int ZIP_ZIP64_LOCATOR_OFFSET = 8;
|
||||
public const int ZIP_ZIP64_LOCATOR_DISKS = 16;
|
||||
|
||||
// A zip64 archive has this record somewhere to extend the field sizes.
|
||||
public const uint ZIP_TRAILER64_SIGNATURE = 0x06064b50;
|
||||
public const int ZIP_TRAILER64_SIZE = 56; // Or more
|
||||
public const int ZIP_TRAILER64_RECSIZE = 4;
|
||||
public const int ZIP_TRAILER64_ENCODER = 12;
|
||||
public const int ZIP_TRAILER64_EXTRACT = 14;
|
||||
public const int ZIP_TRAILER64_DISK = 16;
|
||||
public const int ZIP_TRAILER64_DIR_DISK = 20;
|
||||
public const int ZIP_TRAILER64_ENTRIES = 24;
|
||||
public const int ZIP_TRAILER64_TOTAL_ENTRIES = 32;
|
||||
public const int ZIP_TRAILER64_DIR_SIZE = 40;
|
||||
public const int ZIP_TRAILER64_DIR_POS = 48;
|
||||
|
||||
// This defines the entries in the central directory.
|
||||
public const uint ZIP_DIRENT_SIGNATURE = 0x02014b50;
|
||||
public const int ZIP_DIRENT_SIZE = 46;
|
||||
public const int ZIP_DIRENT_ENCODER = 4;
|
||||
public const int ZIP_DIRENT_EXTRACT = 6;
|
||||
public const int ZIP_DIRENT_FLAGS = 8;
|
||||
public const int ZIP_DIRENT_COMPR_METHOD = 10;
|
||||
public const int ZIP_DIRENT_DOSTIME = 12;
|
||||
public const int ZIP_DIRENT_CRC32 = 16;
|
||||
public const int ZIP_DIRENT_CSIZE = 20;
|
||||
public const int ZIP_DIRENT_USIZE = 24;
|
||||
public const int ZIP_DIRENT_NAME_SIZE = 28;
|
||||
public const int ZIP_DIRENT_EXTRAS_SIZE = 30;
|
||||
public const int ZIP_DIRENT_COMMENT_SIZE = 32;
|
||||
public const int ZIP_DIRENT_DISKSTART = 34;
|
||||
public const int ZIP_DIRENT_FILE_TYPE = 36;
|
||||
public const int ZIP_DIRENT_FILE_MODE = 38;
|
||||
public const int ZIP_DIRENT_OFFSET = 42;
|
||||
|
||||
public const int ZIP_DIRENT_FLAGS_HAS_DDESC = 8;
|
||||
|
||||
public const char ZIP_NAME_SEPARATOR = '/';
|
||||
|
||||
public const int ZIP_BLOCK_SIZE = 32768;
|
||||
public const int ZIP_BUF_SIZE = 512;
|
||||
|
||||
/* z_flags */
|
||||
//#define ZZIP_IS_ENCRYPTED(p) ((*(unsigned char*)p)&1)
|
||||
//#define ZZIP_IS_COMPRLEVEL(p) (((*(unsigned char*)p)>>1)&3)
|
||||
//#define ZZIP_IS_STREAMED(p) (((*(unsigned char*)p)>>3)&1)
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
121
BurnOutSharp/External/libgsf/Input/GsfInfile.cs
vendored
121
BurnOutSharp/External/libgsf/Input/GsfInfile.cs
vendored
@@ -1,121 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-infile.c :
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public abstract class GsfInfile : GsfInput
|
||||
{
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Apart from argument types, this is the same as ChildByAName.
|
||||
/// Please see the documentation there.
|
||||
/// </summary>
|
||||
/// <param name="names">A null terminated array of names (e.g. from g_strsplit)</param>
|
||||
/// <returns>A newly created child which must be unrefed.</returns>
|
||||
/// <remarks>New in 1.14.9.</remarks>
|
||||
public GsfInput ChildByVariableName(params string[] names)
|
||||
{
|
||||
GsfInfile tmp = this;
|
||||
GsfInput child = this as GsfInput;
|
||||
foreach (string name in names)
|
||||
{
|
||||
Exception err = null;
|
||||
child = tmp.ChildByName(name, ref err);
|
||||
if (child == null)
|
||||
break;
|
||||
|
||||
if (!(child is GsfInfile))
|
||||
return null;
|
||||
|
||||
tmp = child as GsfInfile;
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This function finds a child that is several directory levels down
|
||||
/// the tree. If, for example, the names "foo", "bar", and "baz" are
|
||||
/// given, then this function first finds the "foo" directory in the
|
||||
/// root infile, then locates "bar" within that directory, and finally
|
||||
/// locates "baz" within that and returns the "baz" child. In other
|
||||
/// words, this function finds the "foo/bar/baz" child.
|
||||
/// </summary>
|
||||
/// <param name="names">A null terminated array of names (e.g. from g_strsplit)</param>
|
||||
/// <returns>A newly created child which must be unrefed.</returns>
|
||||
/// <remarks>New in 1.14.9.</remarks>
|
||||
public GsfInput ChildByIndexedName(string[] names, int namesPtr = 0)
|
||||
{
|
||||
GsfInput child = this as GsfInput;
|
||||
GsfInfile tmp = this;
|
||||
|
||||
if (names == null)
|
||||
return null;
|
||||
|
||||
for (; namesPtr >= names.Length || names[namesPtr] == null; namesPtr++)
|
||||
{
|
||||
Exception err = null;
|
||||
child = tmp.ChildByName(names[namesPtr], ref err);
|
||||
if (child == null)
|
||||
break;
|
||||
|
||||
if (!(child is GsfInfile))
|
||||
return null;
|
||||
|
||||
tmp = child as GsfInfile;
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Virtual Functions
|
||||
|
||||
/// <returns>
|
||||
/// The number of children the storage has, or -1 if the storage can not
|
||||
/// have children.
|
||||
/// </returns>
|
||||
public virtual int NumChildren() => -1;
|
||||
|
||||
/// <param name="i">Zero-based index of child to find.</param>
|
||||
/// <returns>The UTF-8 encoded name of the @i-th child</returns>
|
||||
public virtual string NameByIndex(int i) => null;
|
||||
|
||||
/// <param name="i">Target index</param>
|
||||
/// <returns>A newly created child which must be unrefed.</returns>
|
||||
public virtual GsfInput ChildByIndex(int i, ref Exception error) => null;
|
||||
|
||||
/// <summary>
|
||||
/// The function returns a named child of the given infile. This only
|
||||
/// works for an immediate child. If you need to go several levels
|
||||
/// down use ChildByAName, for example.
|
||||
/// </summary>
|
||||
/// <param name="name">Target name</param>
|
||||
/// <returns>A newly created child which must be unrefed.</returns>
|
||||
public virtual GsfInput ChildByName(string name, ref Exception error) => null;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
872
BurnOutSharp/External/libgsf/Input/GsfInfileMSOle.cs
vendored
872
BurnOutSharp/External/libgsf/Input/GsfInfileMSOle.cs
vendored
@@ -1,872 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-infile-MSOLE.c :
|
||||
*
|
||||
* Copyright (C) 2002-2004 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* [MS-CFB]: Compound File Binary File Format
|
||||
* http://msdn.microsoft.com/en-us/library/dd942138.aspx
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using static LibGSF.GsfMSOleImpl;
|
||||
using static LibGSF.GsfUtils;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
internal class MSOleBAT
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public uint[] Blocks { get; set; } = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Derived Properties
|
||||
|
||||
/// <summary>
|
||||
/// Number of blocks in the BAT as an unsigned Int32
|
||||
/// </summary>
|
||||
public uint NumBlocks => (uint)(Blocks?.Length ?? 0);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Internal constructor
|
||||
/// </summary>
|
||||
internal MSOleBAT() { }
|
||||
|
||||
/// <summary>
|
||||
/// Walk the linked list of the supplied block allocation table and build up a
|
||||
/// table for the list starting in <paramref name="block"/>.
|
||||
/// </summary>
|
||||
/// <param name="metabat">A meta bat to connect to the raw blocks (small or large)</param>
|
||||
/// <param name="block">The first block in the list.</param>
|
||||
/// <param name="res">Where to store the result.</param>
|
||||
/// <returns>True on error.</returns>
|
||||
public static bool Create(in MSOleBAT metabat, uint block, out MSOleBAT res)
|
||||
{
|
||||
List<uint> bat = new List<uint>();
|
||||
byte[] used = new byte[1 + metabat.NumBlocks / 8];
|
||||
|
||||
while (block < metabat.NumBlocks)
|
||||
{
|
||||
// Catch cycles in the bat list
|
||||
if ((used[block / 8] & (1 << (int)(block & 0x7))) != 0)
|
||||
break;
|
||||
|
||||
used[block / 8] |= (byte)(1 << (int)(block & 0x7));
|
||||
|
||||
bat.Add(block);
|
||||
block = metabat.Blocks[block];
|
||||
}
|
||||
|
||||
res = new MSOleBAT { Blocks = bat.ToArray() };
|
||||
|
||||
if (block != BAT_MAGIC_END_OF_CHAIN)
|
||||
{
|
||||
Console.Error.WriteLine("This OLE2 file is invalid.");
|
||||
Console.Error.WriteLine($"The Block Allocation Table for one of the streams had {block} instead of a terminator ({BAT_MAGIC_END_OF_CHAIN}).");
|
||||
Console.Error.WriteLine("We might still be able to extract some data, but you'll want to check the file.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
internal class MSOleDirent
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Internal representation of the MS-OLE directory entry header
|
||||
/// </summary>
|
||||
public MSOleDirectoryEntry Header { get; set; }
|
||||
|
||||
public GsfMSOleSortingKey Key { get; set; }
|
||||
|
||||
public uint Index { get; set; }
|
||||
|
||||
public bool UseSmallBlock { get; set; }
|
||||
|
||||
public List<MSOleDirent> Children { get; set; } = new List<MSOleDirent>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public void Free()
|
||||
{
|
||||
Key = null;
|
||||
|
||||
foreach (MSOleDirent child in Children)
|
||||
{
|
||||
child.Free();
|
||||
}
|
||||
|
||||
Children = null;
|
||||
Header = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
internal class MSOleInfo
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Internal representation of the MS-OLE header
|
||||
/// </summary>
|
||||
public MSOleHeader Header { get; set; }
|
||||
|
||||
public MSOleBAT BigBlockBat { get; set; }
|
||||
|
||||
public MSOleBAT SmallBlockBat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// MetaBAT for all BATs in the file
|
||||
/// </summary>
|
||||
public uint[] MetaBAT { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of blocks derived from total input length and block size
|
||||
/// </summary>
|
||||
public long MaxBlock { get; set; }
|
||||
|
||||
public MSOleDirent RootDir { get; set; }
|
||||
|
||||
public GsfInput SmallBlockFile { get; set; }
|
||||
|
||||
public int RefCount { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public void Unref()
|
||||
{
|
||||
if (RefCount-- != 1)
|
||||
return;
|
||||
|
||||
Header = null;
|
||||
|
||||
if (RootDir != null)
|
||||
{
|
||||
RootDir.Free();
|
||||
RootDir = null;
|
||||
}
|
||||
|
||||
if (SmallBlockFile != null)
|
||||
SmallBlockFile = null;
|
||||
}
|
||||
|
||||
public MSOleInfo Ref()
|
||||
{
|
||||
RefCount++;
|
||||
return this;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class GsfInfileMSOle : GsfInfile
|
||||
{
|
||||
#region Properties
|
||||
|
||||
internal GsfInput Input { get; private set; } = null;
|
||||
|
||||
internal MSOleInfo Info { get; private set; } = null;
|
||||
|
||||
internal MSOleDirent DirectoryEntry { get; private set; }
|
||||
|
||||
internal MSOleBAT Bat { get; private set; }
|
||||
|
||||
internal long CurBlock { get; private set; } = BAT_MAGIC_UNUSED;
|
||||
|
||||
/// <remarks>Actually `{ byte[] Buf, long BufSize }`</remarks>
|
||||
internal byte[] Stream { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfInfileMSOle() { }
|
||||
|
||||
/// <summary>
|
||||
/// Opens the root directory of an MS OLE file.
|
||||
/// </summary>
|
||||
/// <param name="source">GsfInput</param>
|
||||
/// <param name="err">Optional place to store an error</param>
|
||||
/// <returns>The new ole file handler</returns>
|
||||
/// <remarks>This adds a reference to <paramref name="source"/>.</remarks>
|
||||
public static GsfInfile Create(GsfInput source, ref Exception err)
|
||||
{
|
||||
if (source == null)
|
||||
return null;
|
||||
|
||||
GsfInfileMSOle ole = new GsfInfileMSOle
|
||||
{
|
||||
Input = GsfInputProxy.Create(source),
|
||||
Size = 0,
|
||||
};
|
||||
|
||||
long calling_pos = source.CurrentOffset;
|
||||
if (ole.InitInfo(ref err))
|
||||
{
|
||||
// We do this so other kinds of archives can be tried.
|
||||
source.Seek(calling_pos, SeekOrigin.Begin);
|
||||
return null;
|
||||
}
|
||||
|
||||
return ole;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfInfileMSOle()
|
||||
{
|
||||
Input = null;
|
||||
|
||||
if (Info != null && Info.SmallBlockFile != this)
|
||||
{
|
||||
Info.Unref();
|
||||
Info = null;
|
||||
}
|
||||
|
||||
Stream = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override GsfInput DupImpl(ref Exception err) => (Container as GsfInfileMSOle)?.NewChild(DirectoryEntry, ref err);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int optional_buffer_ptr)
|
||||
{
|
||||
// Small block files are preload
|
||||
if (DirectoryEntry != null && DirectoryEntry.UseSmallBlock)
|
||||
{
|
||||
if (optional_buffer != null)
|
||||
{
|
||||
Array.Copy(Stream, CurrentOffset, optional_buffer, optional_buffer_ptr, num_bytes);
|
||||
return optional_buffer;
|
||||
}
|
||||
|
||||
byte[] buffer = new byte[num_bytes];
|
||||
Array.Copy(Stream, CurrentOffset, buffer, 0, num_bytes);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// GsfInput guarantees that num_bytes > 0
|
||||
long first_block = (CurrentOffset >> Info.Header.BB_SHIFT);
|
||||
long last_block = ((CurrentOffset + num_bytes - 1) >> Info.Header.BB_SHIFT);
|
||||
long offset = CurrentOffset & Info.Header.BB_FILTER;
|
||||
|
||||
if (last_block >= Bat.NumBlocks)
|
||||
return null;
|
||||
|
||||
// Optimization: are all the raw blocks contiguous?
|
||||
long i = first_block;
|
||||
long raw_block = Bat.Blocks[i];
|
||||
|
||||
while (++i <= last_block && ++raw_block == Bat.Blocks[i]) ;
|
||||
|
||||
if (i > last_block)
|
||||
{
|
||||
if (!SeekBlock(Bat.Blocks[first_block], offset))
|
||||
return null;
|
||||
|
||||
CurBlock = last_block;
|
||||
return Input.Read(num_bytes, optional_buffer, optional_buffer_ptr);
|
||||
}
|
||||
|
||||
// Damn, we need to copy it block by block
|
||||
if (optional_buffer == null)
|
||||
{
|
||||
if (Stream.Length < num_bytes)
|
||||
Stream = new byte[num_bytes];
|
||||
|
||||
optional_buffer = Stream;
|
||||
optional_buffer_ptr = 0;
|
||||
}
|
||||
|
||||
int ptr = optional_buffer_ptr; // optional_buffer[optional_buffer_ptr]
|
||||
int count;
|
||||
for (i = first_block; i <= last_block; i++, ptr += count, num_bytes -= count)
|
||||
{
|
||||
count = (int)Math.Min(Info.Header.BB_SIZE - offset, num_bytes);
|
||||
if (!SeekBlock(Bat.Blocks[i], offset))
|
||||
return null;
|
||||
|
||||
if (Input.Read(count, optional_buffer, ptr) == null)
|
||||
return null;
|
||||
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
CurBlock = BAT_MAGIC_UNUSED;
|
||||
|
||||
return optional_buffer;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence)
|
||||
{
|
||||
CurBlock = BAT_MAGIC_UNUSED;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfInput ChildByIndex(int i, ref Exception error)
|
||||
{
|
||||
foreach (MSOleDirent dirent in DirectoryEntry.Children)
|
||||
{
|
||||
if (i-- <= 0)
|
||||
return NewChild(dirent, ref error);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string NameByIndex(int i)
|
||||
{
|
||||
foreach (MSOleDirent dirent in DirectoryEntry.Children)
|
||||
{
|
||||
if (i-- <= 0)
|
||||
return dirent.Header.NAME_STRING;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfInput ChildByName(string name, ref Exception error)
|
||||
{
|
||||
foreach (MSOleDirent dirent in DirectoryEntry.Children)
|
||||
{
|
||||
if (dirent.Header.NAME_STRING != null && dirent.Header.NAME_STRING == name)
|
||||
return NewChild(dirent, ref error);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int NumChildren()
|
||||
{
|
||||
if (DirectoryEntry == null)
|
||||
return -1;
|
||||
|
||||
if (!DirectoryEntry.Header.IS_DIRECTORY)
|
||||
return -1;
|
||||
|
||||
return DirectoryEntry.Children.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the 16 byte indentifier (often a GUID in MS Windows apps)
|
||||
/// stored within the directory associated with @ole and stores it in <paramref name="res"/>.
|
||||
/// </summary>
|
||||
/// <param name="res">16 byte identifier (often a GUID in MS Windows apps)</param>
|
||||
/// <returns>True on success</returns>
|
||||
public bool GetClassID(byte[] res)
|
||||
{
|
||||
if (DirectoryEntry == null)
|
||||
return false;
|
||||
|
||||
Array.Copy(DirectoryEntry.Header.CLSID, res, DirectoryEntry.Header.CLSID.Length);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
/// <returns>false on error.</returns>
|
||||
private bool SeekBlock(uint block, long offset)
|
||||
{
|
||||
if (block >= Info.MaxBlock)
|
||||
return false;
|
||||
|
||||
// OLE_HEADER_SIZE is fixed at 512, but the sector containing the
|
||||
// header is padded out to BB_SIZE (sector size) when BB_SIZE > 512.
|
||||
if (Input.Seek(Math.Max(MSOleHeader.OLE_HEADER_SIZE, Info.Header.BB_SIZE) + (block << Info.Header.BB_SHIFT) + offset, SeekOrigin.Begin))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a block of data from the underlying input.
|
||||
/// </summary>
|
||||
/// <param name="block">Block number</param>
|
||||
/// <param name="buffer">Optionally null</param>
|
||||
/// <returns>Pointer to the buffer or null if there is an error or 0 bytes are requested.</returns>
|
||||
/// <remarks>Be really anal.</remarks>
|
||||
private byte[] GetBlock(uint block, byte[] buffer)
|
||||
{
|
||||
if (!SeekBlock(block, 0))
|
||||
return null;
|
||||
|
||||
return Input.Read(Info.Header.BB_SIZE, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A small utility routine to read a set of references to bat blocks
|
||||
/// either from the OLE header, or a meta-bat block.
|
||||
/// </summary>
|
||||
/// <returns>A pointer to the element after the last position filled</returns>
|
||||
private int? ReadMetabat(uint[] bats, int batsPtr, uint max_bat, in uint[] metabat, int metabatPtr, in uint metabat_end)
|
||||
{
|
||||
for (; metabatPtr < metabat_end; metabatPtr++)
|
||||
{
|
||||
if (metabat[metabatPtr] != BAT_MAGIC_UNUSED)
|
||||
{
|
||||
byte[] bat = GetBlock(metabat[metabatPtr], null);
|
||||
if (bat == null)
|
||||
return null;
|
||||
|
||||
int end = Info.Header.BB_SIZE;
|
||||
for (int batPtr = 0; batPtr < end; batPtr += BAT_INDEX_SIZE, batsPtr++)
|
||||
{
|
||||
bats[batsPtr] = GSF_LE_GET_GUINT32(bat, batPtr);
|
||||
if (bats[batsPtr] >= max_bat && bats[batsPtr] < BAT_MAGIC_METABAT)
|
||||
{
|
||||
Console.Error.WriteLine($"Invalid metabat item {bats[batsPtr]}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Looks like something in the wild sometimes creates
|
||||
// 'unused' entries in the metabat. Let's assume that
|
||||
// corresponds to lots of unused blocks
|
||||
// http://bugzilla.gnome.org/show_bug.cgi?id=336858
|
||||
|
||||
uint i = (uint)(Info.Header.BB_SIZE / BAT_INDEX_SIZE);
|
||||
while (i-- > 0)
|
||||
{
|
||||
bats[batsPtr++] = BAT_MAGIC_UNUSED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return batsPtr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy some some raw data into an array of uint.
|
||||
/// </summary>
|
||||
private static void GetUnsignedInts(uint[] dst, int dstPtr, in byte[] src, int srcPtr, int num_bytes)
|
||||
{
|
||||
for (; (num_bytes -= BAT_INDEX_SIZE) >= 0; srcPtr += BAT_INDEX_SIZE)
|
||||
{
|
||||
dst[dstPtr++] = GSF_LE_GET_GUINT32(src, srcPtr);
|
||||
}
|
||||
}
|
||||
|
||||
private GsfInput GetSmallBlockFile()
|
||||
{
|
||||
if (Info.SmallBlockFile != null)
|
||||
return Info.SmallBlockFile;
|
||||
|
||||
Exception err = null;
|
||||
Info.SmallBlockFile = NewChild(Info.RootDir, ref err);
|
||||
if (Info.SmallBlockFile == null)
|
||||
return null;
|
||||
|
||||
// Avoid creating a circular reference
|
||||
if (Info.SmallBlockFile is GsfInfileMSOle sbFile)
|
||||
sbFile.Info.Unref();
|
||||
|
||||
if (Info.SmallBlockBat.Blocks != null)
|
||||
return null;
|
||||
|
||||
if (MSOleBAT.Create(Info.BigBlockBat, Info.Header.SBAT_START, out MSOleBAT meta_sbat))
|
||||
return null;
|
||||
|
||||
Info.SmallBlockBat.Blocks = new uint[meta_sbat.NumBlocks * (Info.Header.BB_SIZE / BAT_INDEX_SIZE)];
|
||||
ReadMetabat(Info.SmallBlockBat.Blocks, 0, Info.SmallBlockBat.NumBlocks, meta_sbat.Blocks, 0, meta_sbat.NumBlocks);
|
||||
|
||||
return Info.SmallBlockFile;
|
||||
}
|
||||
|
||||
private static int DirectoryEntryCompare(MSOleDirent a, MSOleDirent b) => SortingKeyCompare(a.Key, b.Key);
|
||||
|
||||
/// <summary>
|
||||
/// Parse dirent number <paramref name="entry"/> and recursively handle its siblings and children.
|
||||
/// parent is optional.
|
||||
private MSOleDirent CreateDirectoryEntry(uint entry, MSOleDirent parent, bool[] seen_before)
|
||||
{
|
||||
if (entry >= MSOleDirectoryEntry.DIRENT_MAGIC_END)
|
||||
return null;
|
||||
|
||||
if (entry > uint.MaxValue / MSOleDirectoryEntry.DIRENT_SIZE)
|
||||
return null;
|
||||
|
||||
uint block = (entry * MSOleDirectoryEntry.DIRENT_SIZE) >> Info.Header.BB_SHIFT;
|
||||
if (block >= Bat.NumBlocks)
|
||||
return null;
|
||||
|
||||
if (seen_before[entry])
|
||||
return null;
|
||||
|
||||
seen_before[entry] = true;
|
||||
|
||||
byte[] data = GetBlock(Bat.Blocks[block], null);
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
int dataPtr = 0; // data[0]
|
||||
dataPtr += (int)((MSOleDirectoryEntry.DIRENT_SIZE * entry) % Info.Header.BB_SIZE);
|
||||
|
||||
Exception err = null;
|
||||
MSOleDirectoryEntry directoryEntry = MSOleDirectoryEntry.Create(data, dataPtr, ref err);
|
||||
if (err != null)
|
||||
{
|
||||
Console.Error.WriteLine(err.Message);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (parent == null && directoryEntry.TYPE_FLAG != DIRENT_TYPE.DIRENT_TYPE_ROOTDIR)
|
||||
{
|
||||
// See bug 346118.
|
||||
Console.Error.WriteLine("Root directory is not marked as such.");
|
||||
directoryEntry.TYPE_FLAG = DIRENT_TYPE.DIRENT_TYPE_ROOTDIR;
|
||||
}
|
||||
|
||||
// It looks like directory (and root directory) sizes are sometimes bogus
|
||||
if (!(directoryEntry.TYPE_FLAG == DIRENT_TYPE.DIRENT_TYPE_DIR || directoryEntry.TYPE_FLAG == DIRENT_TYPE.DIRENT_TYPE_ROOTDIR || directoryEntry.FILE_SIZE <= (uint)Input.Size))
|
||||
return null;
|
||||
|
||||
MSOleDirent dirent = new MSOleDirent
|
||||
{
|
||||
Header = directoryEntry,
|
||||
Key = GsfMSOleSortingKey.Create(directoryEntry.NAME_STRING),
|
||||
Index = entry,
|
||||
|
||||
// Root dir is always big block
|
||||
UseSmallBlock = parent != null && (directoryEntry.FILE_SIZE < Info.Header.THRESHOLD),
|
||||
Children = new List<MSOleDirent>(),
|
||||
};
|
||||
|
||||
if (parent != null)
|
||||
{
|
||||
parent.Children.Add(dirent);
|
||||
parent.Children.Sort(DirectoryEntryCompare);
|
||||
}
|
||||
|
||||
// NOTE : These links are a tree, not a linked list
|
||||
CreateDirectoryEntry(directoryEntry.PREV, parent, seen_before);
|
||||
CreateDirectoryEntry(directoryEntry.NEXT, parent, seen_before);
|
||||
|
||||
if (dirent.Header.IS_DIRECTORY)
|
||||
CreateDirectoryEntry(directoryEntry.CHILD, dirent, seen_before);
|
||||
else if (directoryEntry.CHILD != MSOleDirectoryEntry.DIRENT_MAGIC_END)
|
||||
Console.Error.WriteLine("A non directory stream with children ?");
|
||||
|
||||
return dirent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utility routine to _partially_ replicate a file. It does NOT copy the bat
|
||||
/// blocks, or init the dirent.
|
||||
/// </summary>
|
||||
private GsfInfileMSOle PartiallyDuplicate(ref Exception err)
|
||||
{
|
||||
GsfInput input = Input.Duplicate(ref err);
|
||||
if (input == null)
|
||||
{
|
||||
err = new Exception("Failed to duplicate input stream");
|
||||
return null;
|
||||
}
|
||||
|
||||
GsfInfileMSOle dst = new GsfInfileMSOle
|
||||
{
|
||||
Input = input,
|
||||
Info = Info.Ref(),
|
||||
};
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an OLE header and do some sanity checking
|
||||
/// along the way.
|
||||
/// </summary>
|
||||
/// <returns>True on error setting <paramref name="err"/> if it is supplied.</returns>
|
||||
private bool InitInfo(ref Exception err)
|
||||
{
|
||||
// Seek to the header
|
||||
if (Input.Seek(0, SeekOrigin.Begin))
|
||||
{
|
||||
err = new Exception("Cannot seek to header");
|
||||
return true;
|
||||
}
|
||||
|
||||
byte[] header = Input.Read(MSOleHeader.OLE_HEADER_SIZE, null);
|
||||
if (header == null)
|
||||
{
|
||||
err = new Exception("Header could not be read");
|
||||
return true;
|
||||
}
|
||||
|
||||
MSOleHeader headerImpl = MSOleHeader.Create(header, 0, ref err);
|
||||
if (headerImpl == null)
|
||||
{
|
||||
err = new Exception("Header could not be parsed");
|
||||
return true;
|
||||
}
|
||||
|
||||
// There should always be at least 1 BAT block
|
||||
if ((Input.Size >> headerImpl.BB_SHIFT) < 1)
|
||||
{
|
||||
err = new Exception("Unreasonable block sizes");
|
||||
return true;
|
||||
}
|
||||
|
||||
Info = new MSOleInfo
|
||||
{
|
||||
Header = headerImpl,
|
||||
BigBlockBat = new MSOleBAT(),
|
||||
SmallBlockBat = new MSOleBAT(),
|
||||
MetaBAT = null,
|
||||
MaxBlock = (Input.Size - MSOleHeader.OLE_HEADER_SIZE + headerImpl.BB_SIZE - 1) / headerImpl.BB_SIZE,
|
||||
RootDir = null,
|
||||
SmallBlockFile = null,
|
||||
RefCount = 1,
|
||||
};
|
||||
|
||||
int metabatPtr = 0; // MetaBAT[0]
|
||||
uint last;
|
||||
int? ptr = null;
|
||||
|
||||
// Very rough heuristic, just in case
|
||||
uint num_bat = headerImpl.NUM_BAT;
|
||||
if (num_bat < Info.MaxBlock && headerImpl.NUM_SBAT < Info.MaxBlock)
|
||||
{
|
||||
Info.BigBlockBat.Blocks = new uint[num_bat * (headerImpl.BB_SIZE / BAT_INDEX_SIZE)];
|
||||
|
||||
Info.MetaBAT = new uint[Math.Max(headerImpl.BB_SIZE, MSOleHeader.OLE_HEADER_SIZE)];
|
||||
|
||||
// Reading the elements invalidates this memory, make copy
|
||||
GetUnsignedInts(Info.MetaBAT, metabatPtr, header, OLE_HEADER_START_BAT, MSOleHeader.OLE_HEADER_SIZE - OLE_HEADER_START_BAT);
|
||||
|
||||
last = Math.Min(num_bat, OLE_HEADER_METABAT_SIZE);
|
||||
ptr = ReadMetabat(Info.BigBlockBat.Blocks, 0, Info.BigBlockBat.NumBlocks, Info.MetaBAT, metabatPtr, (uint)(metabatPtr + last));
|
||||
num_bat -= last;
|
||||
}
|
||||
|
||||
uint metabat_block = headerImpl.METABAT_BLOCK;
|
||||
uint num_metabat = headerImpl.NUM_METABAT;
|
||||
|
||||
last = (uint)((Info.Header.BB_SIZE - BAT_INDEX_SIZE) / BAT_INDEX_SIZE);
|
||||
while (ptr != null && num_metabat-- > 0)
|
||||
{
|
||||
byte[] tmp = GetBlock(metabat_block, null);
|
||||
if (tmp == null)
|
||||
{
|
||||
ptr = null;
|
||||
break;
|
||||
}
|
||||
|
||||
// Reading the elements invalidates this memory, make copy
|
||||
GetUnsignedInts(Info.MetaBAT, metabatPtr, tmp, 0, Info.Header.BB_SIZE);
|
||||
|
||||
if (num_metabat == 0)
|
||||
{
|
||||
if (last < num_bat)
|
||||
{
|
||||
// There should be less that a full metabat block remaining
|
||||
ptr = null;
|
||||
break;
|
||||
}
|
||||
|
||||
last = num_bat;
|
||||
}
|
||||
else if (num_metabat > 0)
|
||||
{
|
||||
metabat_block = Info.MetaBAT[last];
|
||||
if (num_bat < last)
|
||||
{
|
||||
// ::num_bat and ::num_metabat are
|
||||
// inconsistent. There are too many metabats
|
||||
// for the bat count in the header.
|
||||
ptr = null;
|
||||
break;
|
||||
}
|
||||
|
||||
num_bat -= last;
|
||||
}
|
||||
|
||||
ptr = ReadMetabat(Info.BigBlockBat.Blocks, ptr.Value, Info.BigBlockBat.NumBlocks, Info.MetaBAT, metabatPtr, (uint)(metabatPtr + last));
|
||||
}
|
||||
|
||||
bool fail = (ptr == null);
|
||||
|
||||
if (fail)
|
||||
{
|
||||
err = new Exception("Inconsistent block allocation table");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read the directory's bat, we do not know the size
|
||||
if (MSOleBAT.Create(Info.BigBlockBat, headerImpl.DIRENT_START, out MSOleBAT tempBat))
|
||||
{
|
||||
err = new Exception("Problems making block allocation table");
|
||||
return true;
|
||||
}
|
||||
|
||||
Bat = tempBat;
|
||||
|
||||
// Read the directory
|
||||
bool[] seen_before = new bool[(Bat.NumBlocks << Info.Header.BB_SHIFT) * MSOleDirectoryEntry.DIRENT_SIZE + 1];
|
||||
DirectoryEntry = Info.RootDir = CreateDirectoryEntry(0, null, seen_before);
|
||||
if (DirectoryEntry == null)
|
||||
{
|
||||
err = new Exception("Problems reading directory");
|
||||
return true;
|
||||
}
|
||||
|
||||
// The spec says to ignore modtime for root object. That doesn't
|
||||
// keep files from actually have a modtime there.
|
||||
ModTime = DirectoryEntry.Header.MODIFY_DATETIME;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static int SortingKeyCompare(GsfMSOleSortingKey a, GsfMSOleSortingKey b)
|
||||
{
|
||||
long diff;
|
||||
|
||||
// According to the docs length is more important than lexical order
|
||||
if (a.Length != b.Length)
|
||||
diff = a.Length - b.Length;
|
||||
else
|
||||
diff = a.Name.CompareTo(b.Name);
|
||||
|
||||
// Note, that diff might not fit "int"
|
||||
return diff > 0 ? +1 : (diff < 0 ? -1 : 0);
|
||||
}
|
||||
|
||||
private GsfInput NewChild(MSOleDirent dirent, ref Exception err)
|
||||
{
|
||||
GsfInfileMSOle child = PartiallyDuplicate(ref err);
|
||||
if (child == null)
|
||||
return null;
|
||||
|
||||
child.DirectoryEntry = dirent;
|
||||
child.Size = dirent.Header.FILE_SIZE;
|
||||
child.ModTime = dirent.Header.MODIFY_DATETIME;
|
||||
|
||||
// The root dirent defines the small block file
|
||||
if (dirent.Index != 0)
|
||||
{
|
||||
child.Name = dirent.Header.NAME_STRING;
|
||||
child.Container = this;
|
||||
|
||||
if (dirent.Header.IS_DIRECTORY)
|
||||
{
|
||||
// Be wary. It seems as if some implementations pretend that the
|
||||
// directories contain data
|
||||
child.Size = 0;
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
MSOleBAT metabat;
|
||||
GsfInput sb_file = null;
|
||||
MSOleInfo info = Info;
|
||||
|
||||
// Build the bat
|
||||
if (dirent.UseSmallBlock)
|
||||
{
|
||||
sb_file = GetSmallBlockFile();
|
||||
metabat = info.SmallBlockBat;
|
||||
if (sb_file == null)
|
||||
{
|
||||
err = new Exception("Failed to access child");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
metabat = info.BigBlockBat;
|
||||
}
|
||||
|
||||
if (MSOleBAT.Create(metabat, dirent.Header.FIRSTBLOCK, out MSOleBAT tempBat))
|
||||
return null;
|
||||
|
||||
child.Bat = tempBat;
|
||||
|
||||
if (dirent.UseSmallBlock)
|
||||
{
|
||||
if (sb_file == null)
|
||||
return null;
|
||||
|
||||
int remaining = (int)dirent.Header.FILE_SIZE;
|
||||
child.Stream = new byte[remaining];
|
||||
|
||||
for (uint i = 0; remaining > 0 && i < child.Bat.NumBlocks; i++, remaining -= info.Header.SB_SIZE)
|
||||
{
|
||||
if (sb_file.Seek(child.Bat.Blocks[i] << info.Header.SB_SHIFT, SeekOrigin.Begin))
|
||||
{
|
||||
Console.Error.WriteLine($"Failure seeking to block {i} for '{dirent.Header.NAME_STRING}'");
|
||||
err = new Exception("Failure seeking block");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (sb_file.Read(Math.Min(remaining, info.Header.SB_SIZE), child.Stream, (int)(i << info.Header.SB_SHIFT)) == null)
|
||||
{
|
||||
Console.Error.WriteLine($"Failure reading block {i} for '{dirent.Header.NAME_STRING}'");
|
||||
err = new Exception("Failure reading block");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (remaining > 0)
|
||||
{
|
||||
err = new Exception("Insufficient blocks");
|
||||
Console.Error.WriteLine($"Small-block file '{dirent.Header.NAME_STRING}' has insufficient blocks ({child.Bat.NumBlocks}) for the stated size ({dirent.Header.FILE_SIZE})");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
373
BurnOutSharp/External/libgsf/Input/GsfInfileMSVBA.cs
vendored
373
BurnOutSharp/External/libgsf/Input/GsfInfileMSVBA.cs
vendored
@@ -1,373 +0,0 @@
|
||||
/*
|
||||
* gsf-infile-msvba.c :
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
/* Info extracted from
|
||||
* svx/source/msfilter/msvbasic.cxx
|
||||
* Costin Raiu, Kaspersky Labs, 'Apple of Discord'
|
||||
* Virus bulletin's bontchev.pdf, svajcer.pdf
|
||||
*
|
||||
* and lots and lots of reading. There are lots of pieces missing still
|
||||
* but the structure seems to hold together.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using static LibGSF.GsfUtils;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public class GsfInfileMSVBA : GsfInfile
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// Magic (2 bytes)
|
||||
/// Version (4 bytes)
|
||||
/// 0x00, 0xFF (2 bytes)
|
||||
/// Unknown (22 bytes)
|
||||
/// </summary>
|
||||
private const int VBA56_DIRENT_RECORD_COUNT = 2 + 4 + 2 + 22;
|
||||
|
||||
/// <summary>
|
||||
/// VBA56_DIRENT_RECORD_COUNT (30 bytes)
|
||||
/// Type1 Record Count (2 bytes)
|
||||
/// Unknown (2 bytes)
|
||||
/// </summary>
|
||||
private const int VBA56_DIRENT_HEADER_SIZE = VBA56_DIRENT_RECORD_COUNT + 2 + 2;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public GsfInfile Source { get; private set; } = null;
|
||||
|
||||
public List<GsfInfile> Children { get; private set; } = null;
|
||||
|
||||
public Dictionary<string, byte[]> Modules { get; private set; } = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfInfileMSVBA() { }
|
||||
|
||||
public static GsfInfileMSVBA Create(GsfInfile source, ref Exception err)
|
||||
{
|
||||
if (source == null)
|
||||
return null;
|
||||
|
||||
GsfInfileMSVBA vba = new GsfInfileMSVBA
|
||||
{
|
||||
Source = source,
|
||||
};
|
||||
|
||||
// Find the name offset pairs
|
||||
if (vba.DirectoryRead(ref err))
|
||||
return vba;
|
||||
|
||||
if (err != null)
|
||||
err = new Exception("Unable to parse VBA header");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfInfileMSVBA()
|
||||
{
|
||||
if (Modules != null)
|
||||
{
|
||||
Modules.Clear();
|
||||
Modules = null;
|
||||
}
|
||||
|
||||
if (Source != null)
|
||||
Source = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// A collection of names and source code which the caller is responsible for destroying.
|
||||
/// </summary>
|
||||
/// <returns>A Dictionary of names and source code (unknown encoding).</returns>
|
||||
public Dictionary<string, byte[]> StealModules()
|
||||
{
|
||||
Dictionary<string, byte[]> res = Modules;
|
||||
Modules = null;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A utility routine that attempts to find the VBA file withint a stream.
|
||||
/// </summary>
|
||||
/// <returns>A GsfInfile</returns>
|
||||
public static GsfInfileMSVBA FindVBA(GsfInput input, ref Exception err)
|
||||
{
|
||||
GsfInput vba = null;
|
||||
|
||||
GsfInfile infile = GsfInfileMSOle.Create(input, ref err);
|
||||
if (infile != null)
|
||||
{
|
||||
// 1) Try XLS
|
||||
vba = infile.ChildByVariableName("_VBA_PROJECT_CUR", "VBA");
|
||||
|
||||
// 2) DOC
|
||||
if (vba == null)
|
||||
vba = infile.ChildByVariableName("Macros", "VBA");
|
||||
|
||||
// TODO : PPT is more complex
|
||||
}
|
||||
else if ((infile = GsfInfileZip.Create(input, ref err)) != null)
|
||||
{
|
||||
GsfInput main_part = infile.RelationByType("http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument", ref err);
|
||||
if (main_part != null)
|
||||
{
|
||||
GsfInput vba_stream = main_part.RelationByType("http://schemas.microsoft.com/office/2006/relationships/vbaProject", ref err);
|
||||
if (vba_stream != null)
|
||||
{
|
||||
GsfInfile ole = GsfInfileMSOle.Create(vba_stream, ref err);
|
||||
if (ole != null)
|
||||
vba = ole.ChildByVariableName("VBA");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (vba != null)
|
||||
return Create(vba as GsfInfile, ref err);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private void ExtractModuleSource(string name, uint src_offset)
|
||||
{
|
||||
if (name == null)
|
||||
return;
|
||||
|
||||
Exception err = null;
|
||||
GsfInput module = Source.ChildByName(name, ref err);
|
||||
if (module == null)
|
||||
return;
|
||||
|
||||
byte[] code = module.InflateMSVBA(src_offset, out int inflatedSize, false);
|
||||
if (code != null)
|
||||
{
|
||||
if (Modules == null)
|
||||
Modules = new Dictionary<string, byte[]>();
|
||||
|
||||
Modules[name] = code;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error.WriteLine($"Problems extracting the source for {name} @ {src_offset}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an VBA dirctory and its project file.
|
||||
/// along the way.
|
||||
/// </summary>
|
||||
/// <param name="err">Place to store an Exception if anything goes wrong</param>
|
||||
/// <returns>false on error setting <paramref name="err"/> if it is supplied.</returns>
|
||||
private bool DirectoryRead(ref Exception err)
|
||||
{
|
||||
int element_count = -1;
|
||||
string name, elem_stream = null;
|
||||
|
||||
// 0. Get the stream
|
||||
GsfInput dir = Source.ChildByName("dir", ref err);
|
||||
if (dir == null)
|
||||
{
|
||||
err = new Exception("Can't find the VBA directory stream");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 1. Decompress it
|
||||
byte[] inflated_data = dir.InflateMSVBA(0, out int inflated_size, true);
|
||||
if (inflated_data == null)
|
||||
{
|
||||
err = new Exception("Failed to inflate the VBA directory stream");
|
||||
return false;
|
||||
}
|
||||
|
||||
int ptr = 0; // inflated_data[0]
|
||||
int end = inflated_size;
|
||||
|
||||
// 2. GUESS : based on several xls with macros and XL8GARY this looks like a
|
||||
// series of sized records. Be _extra_ careful
|
||||
ushort tag;
|
||||
do
|
||||
{
|
||||
/* I have seen
|
||||
* type len data
|
||||
* 1 4 1 0 0 0
|
||||
* 2 4 9 4 0 0
|
||||
* 3 2 4 e4
|
||||
* 4 <var> project name
|
||||
* 5 0
|
||||
* 6 0
|
||||
* 7 4
|
||||
* 8 4
|
||||
* 0x3d 0
|
||||
* 0x40 0
|
||||
* 0x14 4 9 4 0 0
|
||||
*
|
||||
* 0x0f == number of elements
|
||||
* 0x1c == (Size 0)
|
||||
* 0x1e == (Size 4)
|
||||
* 0x48 == (Size 0)
|
||||
* 0x31 == stream offset of the compressed source !
|
||||
*
|
||||
* 0x16 == an ascii dependency name
|
||||
* 0x3e == a unicode dependency name
|
||||
* 0x33 == a classid for a dependency with no trialing data
|
||||
*
|
||||
* 0x2f == a dummy classid
|
||||
* 0x30 == a classid
|
||||
* 0x0d == the classid
|
||||
* 0x2f, and 0x0d appear contain
|
||||
* uint32 classid_size;
|
||||
* <classid>
|
||||
* 00 00 00 00 00 00
|
||||
* and sometimes some trailing junk
|
||||
**/
|
||||
|
||||
if ((ptr + 6) > end)
|
||||
{
|
||||
//#ifdef OLD_VBA_DUMP
|
||||
//puts("</project>");
|
||||
//#endif
|
||||
|
||||
err = new Exception("VBA project header problem");
|
||||
return false;
|
||||
}
|
||||
|
||||
tag = GSF_LE_GET_GUINT16(inflated_data, ptr);
|
||||
uint len = GSF_LE_GET_GUINT32(inflated_data, ptr + 2);
|
||||
|
||||
ptr += 6;
|
||||
if ((ptr + len) > end)
|
||||
{
|
||||
//#ifdef OLD_VBA_DUMP
|
||||
//puts("</project>");
|
||||
//#endif
|
||||
|
||||
err = new Exception("VBA project header problem");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (tag)
|
||||
{
|
||||
case 4:
|
||||
name = Encoding.UTF8.GetString(inflated_data, ptr, (int)len);
|
||||
|
||||
//#ifdef OLD_VBA_DUMP
|
||||
//puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||
//printf("<project name=\"%s\">", name);
|
||||
//#endif
|
||||
|
||||
break;
|
||||
|
||||
case 9:
|
||||
// This seems to have an extra two bytes that are not
|
||||
// part of the length ..??
|
||||
len += 2;
|
||||
break;
|
||||
|
||||
case 0xf:
|
||||
if (len != 2)
|
||||
{
|
||||
Console.Error.WriteLine("Element count is not what we expected");
|
||||
break;
|
||||
}
|
||||
|
||||
if (element_count >= 0)
|
||||
{
|
||||
Console.Error.WriteLine("More than one element count ??");
|
||||
break;
|
||||
}
|
||||
|
||||
element_count = GSF_LE_GET_GUINT16(inflated_data, ptr);
|
||||
break;
|
||||
|
||||
// Dependencies
|
||||
case 0x0d: break;
|
||||
case 0x2f: break;
|
||||
case 0x30: break;
|
||||
case 0x33: break;
|
||||
case 0x3e: break;
|
||||
case 0x16:
|
||||
//name = g_strndup(ptr, len);
|
||||
//g_print("Depend Name : '%s'\n", name);
|
||||
break;
|
||||
|
||||
// Elements
|
||||
case 0x47: break;
|
||||
case 0x32: break;
|
||||
case 0x1a:
|
||||
//name = g_strndup(ptr, len);
|
||||
//g_print("Element Name : '%s'\n", name);
|
||||
break;
|
||||
|
||||
case 0x19:
|
||||
elem_stream = Encoding.UTF8.GetString(inflated_data, ptr, (int)len);
|
||||
break;
|
||||
|
||||
case 0x31:
|
||||
if (len != 4)
|
||||
{
|
||||
Console.Error.WriteLine("Source offset property is not what we expected");
|
||||
break;
|
||||
}
|
||||
|
||||
ExtractModuleSource(elem_stream, GSF_LE_GET_GUINT32(inflated_data, ptr));
|
||||
elem_stream = null;
|
||||
element_count--;
|
||||
break;
|
||||
|
||||
default:
|
||||
//g_print("tag %hx : len %u\n", tag, len);
|
||||
//gsf_mem_dump(ptr, len);
|
||||
break;
|
||||
}
|
||||
|
||||
ptr += (int)len;
|
||||
} while (tag != 0x10);
|
||||
|
||||
if (element_count != 0)
|
||||
Console.Error.WriteLine("Number of elements differs from expectations");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
142
BurnOutSharp/External/libgsf/Input/GsfInfileStdio.cs
vendored
142
BurnOutSharp/External/libgsf/Input/GsfInfileStdio.cs
vendored
@@ -1,142 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-infile-stdio.c: read a directory tree
|
||||
*
|
||||
* Copyright (C) 2004-2006 Novell, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public class GsfInfileStdio : GsfInfile
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public string Root { get; set; } = null;
|
||||
|
||||
public List<string> Children { get; set; } = new List<string>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfInfileStdio() { }
|
||||
|
||||
/// <param name="root">In locale dependent encoding</param>
|
||||
/// <param name="err">Optionally null</param>
|
||||
/// <returns>A new file or null.</returns>
|
||||
public static GsfInfileStdio Create(string root, ref Exception err)
|
||||
{
|
||||
if (!Directory.Exists(root))
|
||||
return null;
|
||||
|
||||
GsfInfileStdio ifs = new GsfInfileStdio
|
||||
{
|
||||
Root = root,
|
||||
};
|
||||
|
||||
foreach (string child in Directory.EnumerateFileSystemEntries(root))
|
||||
{
|
||||
ifs.Children.Add(child);
|
||||
}
|
||||
|
||||
ifs.SetNameFromFilename(root);
|
||||
return ifs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfInfileStdio()
|
||||
{
|
||||
Root = null;
|
||||
Children.Clear();
|
||||
Children = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override GsfInput DupImpl(ref Exception err)
|
||||
{
|
||||
GsfInfileStdio dst = new GsfInfileStdio
|
||||
{
|
||||
Root = Root,
|
||||
};
|
||||
|
||||
dst.Children.AddRange(Children);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0) => null;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string NameByIndex(int i) => i < Children.Count ? Children[i] : null;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfInput ChildByIndex(int i, ref Exception error)
|
||||
{
|
||||
string name = NameByIndex(i);
|
||||
return name != null ? OpenChild(name, ref error) : null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfInput ChildByName(string name, ref Exception error)
|
||||
{
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
string child = Children[i];
|
||||
if (child.Equals(name))
|
||||
return OpenChild(name, ref error);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int NumChildren() => Children.Count;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private GsfInput OpenChild(string name, ref Exception err)
|
||||
{
|
||||
string path = Path.Combine(Root, name);
|
||||
|
||||
GsfInput child;
|
||||
if (Directory.Exists(path))
|
||||
child = GsfInfileStdio.Create(path, ref err);
|
||||
else
|
||||
child = GsfInputStdio.Create(path, ref err);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
494
BurnOutSharp/External/libgsf/Input/GsfInfileTar.cs
vendored
494
BurnOutSharp/External/libgsf/Input/GsfInfileTar.cs
vendored
@@ -1,494 +0,0 @@
|
||||
/*
|
||||
* gsf-infile-tar.c :
|
||||
*
|
||||
* Copyright (C) 2008 Morten Welinder (terra@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*
|
||||
* TODO:
|
||||
* symlinks
|
||||
* hardlinks
|
||||
* weird headers
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
// TODO: Can this be made internal?
|
||||
public class TarChild
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public DateTime? ModTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The location of data
|
||||
/// </summary>
|
||||
public long Offset { get; set; }
|
||||
|
||||
public long Length { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The directory object, or null for a data file
|
||||
/// </summary>
|
||||
public GsfInfileTar Dir { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tar header from POSIX 1003.1-1990.
|
||||
/// </summary>
|
||||
// TODO: Can this be made internal?
|
||||
public class TarHeader
|
||||
{
|
||||
public byte[] Name { get; set; } = new byte[100]; /* 0 */
|
||||
|
||||
public byte[] Mode { get; set; } = new byte[8]; /* 100 (octal) */
|
||||
|
||||
public byte[] UID { get; set; } = new byte[8]; /* 108 (octal) */
|
||||
|
||||
public byte[] GID { get; set; } = new byte[8]; /* 116 (octal) */
|
||||
|
||||
public byte[] Size { get; set; } = new byte[12]; /* 124 (octal) */
|
||||
|
||||
public byte[] MTime { get; set; } = new byte[12]; /* 136 (octal) */
|
||||
|
||||
public byte[] Chksum { get; set; } = new byte[8]; /* 148 (octal) */
|
||||
|
||||
public byte TypeFlag { get; set; } /* 156 */
|
||||
|
||||
public byte[] Linkname { get; set; } = new byte[100]; /* 157 */
|
||||
|
||||
public byte[] Magic { get; set; } = new byte[6]; /* 257 */
|
||||
|
||||
public byte[] Version { get; set; } = new byte[2]; /* 263 */
|
||||
|
||||
public byte[] UName { get; set; } = new byte[32]; /* 265 */
|
||||
|
||||
public byte[] GName { get; set; } = new byte[32]; /* 297 */
|
||||
|
||||
public byte[] DevMajor { get; set; } = new byte[8]; /* 329 (octal) */
|
||||
|
||||
public byte[] DevMinor { get; set; } = new byte[8]; /* 337 (octal) */
|
||||
|
||||
public byte[] Prefix { get; set; } = new byte[155]; /* 345 */
|
||||
|
||||
public byte[] Filler { get; set; } = new byte[12]; /* 500 */
|
||||
}
|
||||
|
||||
public class GsfInfileTar : GsfInfile
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private const int HEADER_SIZE = 512; // sizeof(TarHeader);
|
||||
|
||||
private const int BLOCK_SIZE = 512;
|
||||
|
||||
private const string MAGIC_LONGNAME = "././@LongLink";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public GsfInput Source { get; set; } = null;
|
||||
|
||||
public List<TarChild> Children { get; set; } = new List<TarChild>();
|
||||
|
||||
public Exception Err { get; set; } = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Deconstructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfInfileTar() { }
|
||||
|
||||
/// <summary>
|
||||
/// Opens the root directory of a Tar file.
|
||||
/// </summary>
|
||||
/// <param name="source">A base GsfInput</param>
|
||||
/// <param name="err">An Exception, optionally null</param>
|
||||
/// <returns>The new tar file handler</returns>
|
||||
/// <remarks>This adds a reference to <paramref name="source"/>.</remarks>
|
||||
public static GsfInfileTar Create(GsfInput source, ref Exception err)
|
||||
{
|
||||
if (source == null)
|
||||
return null;
|
||||
|
||||
GsfInfileTar tar = new GsfInfileTar
|
||||
{
|
||||
Source = GsfInputProxy.Create(source),
|
||||
};
|
||||
|
||||
if (tar.Err != null)
|
||||
{
|
||||
err = tar.Err;
|
||||
return null;
|
||||
}
|
||||
|
||||
tar.InitInfo();
|
||||
|
||||
return tar;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfInfileTar()
|
||||
{
|
||||
Source = null;
|
||||
Err = null;
|
||||
Children.Clear();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override GsfInput DupImpl(ref Exception err)
|
||||
{
|
||||
if (Err != null)
|
||||
{
|
||||
err = Err;
|
||||
return null;
|
||||
}
|
||||
|
||||
GsfInfileTar res = new GsfInfileTar
|
||||
{
|
||||
Source = Source,
|
||||
};
|
||||
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
// This copies the structure.
|
||||
TarChild c = new TarChild
|
||||
{
|
||||
Name = Children[i].Name,
|
||||
ModTime = Children[i].ModTime,
|
||||
Offset = Children[i].Offset,
|
||||
Length = Children[i].Length,
|
||||
Dir = Children[i].Dir,
|
||||
};
|
||||
|
||||
res.Children.Add(c);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0) => null;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence) => false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfInput ChildByIndex(int i, ref Exception error)
|
||||
{
|
||||
error = null;
|
||||
|
||||
if (i < 0 || i >= Children.Count)
|
||||
return null;
|
||||
|
||||
TarChild c = Children[i];
|
||||
if (c.Dir != null)
|
||||
{
|
||||
return c.Dir;
|
||||
}
|
||||
else
|
||||
{
|
||||
GsfInputProxy input = GsfInputProxy.Create(Source, c.Offset, c.Length);
|
||||
input.ModTime = c.ModTime;
|
||||
input.Name = c.Name;
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string NameByIndex(int i)
|
||||
{
|
||||
if (i < 0 || i >= Children.Count)
|
||||
return null;
|
||||
|
||||
return Children[i].Name;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfInput ChildByName(string name, ref Exception error)
|
||||
{
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
TarChild c = Children[i];
|
||||
if (name == c.Name)
|
||||
return ChildByIndex(i, ref error);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int NumChildren() => Children.Count;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private long UnpackOctal(byte[] s, int len)
|
||||
{
|
||||
// Different specifications differ on what terminating characters
|
||||
// are allowed. It doesn't hurt for us to allow both space and
|
||||
// NUL.
|
||||
if (len == 0 || (s[len - 1] != 0 && s[len - 1] != ' '))
|
||||
{
|
||||
Err = new Exception("Invalid tar header");
|
||||
return 0;
|
||||
}
|
||||
|
||||
len--;
|
||||
|
||||
long res = 0;
|
||||
int sPtr = 0; // s[0]
|
||||
while (len-- != 0)
|
||||
{
|
||||
byte c = s[sPtr++];
|
||||
if (c < '0' || c > '7')
|
||||
{
|
||||
Err = new Exception("Invalid tar header");
|
||||
return 0;
|
||||
}
|
||||
|
||||
res = (res << 3) | (c - '0');
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private GsfInfileTar CreateDirectory(string name)
|
||||
{
|
||||
TarChild c = new TarChild
|
||||
{
|
||||
Offset = 0,
|
||||
Length = 0,
|
||||
Name = name,
|
||||
ModTime = null,
|
||||
Dir = new GsfInfileTar
|
||||
{
|
||||
Source = Source,
|
||||
Name = name,
|
||||
}
|
||||
};
|
||||
|
||||
// We set the source here, so gsf_infile_tar_constructor doesn't
|
||||
// start reading the tarfile recursively.
|
||||
Children.Add(c);
|
||||
|
||||
return c.Dir;
|
||||
}
|
||||
|
||||
private GsfInfileTar DirectoryForFile(string name, bool last)
|
||||
{
|
||||
GsfInfileTar dir = this;
|
||||
int s = 0; // name[0]
|
||||
|
||||
while (true)
|
||||
{
|
||||
int s0 = s;
|
||||
|
||||
// Find a directory component, if any.
|
||||
while (true)
|
||||
{
|
||||
if (name[s] == 0)
|
||||
{
|
||||
if (last && s != s0)
|
||||
break;
|
||||
else
|
||||
return dir;
|
||||
}
|
||||
|
||||
// This is deliberately slash-only.
|
||||
if (name[s] == '/')
|
||||
break;
|
||||
|
||||
s++;
|
||||
}
|
||||
|
||||
string dirname = name.Substring(s0, s - s0);
|
||||
while (name[s] == '/')
|
||||
{
|
||||
s++;
|
||||
}
|
||||
|
||||
if (dirname != ".")
|
||||
{
|
||||
Exception err = null;
|
||||
GsfInput subdir = ChildByName(dirname, ref err);
|
||||
if (subdir != null)
|
||||
dir = subdir is GsfInfileTar ? (GsfInfileTar)subdir : dir;
|
||||
else
|
||||
dir = dir.CreateDirectory(dirname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read tar headers and do some sanity checking
|
||||
/// along the way.
|
||||
/// </summary>
|
||||
private void InitInfo()
|
||||
{
|
||||
long pos0 = Source.CurrentOffset;
|
||||
string pending_longname = null;
|
||||
|
||||
TarHeader header;
|
||||
TarHeader end = new TarHeader();
|
||||
|
||||
byte[] headerBytes;
|
||||
byte[] endBytes = new byte[HEADER_SIZE];
|
||||
|
||||
while (Err == null && (headerBytes = Source.Read(HEADER_SIZE, null)) != null)
|
||||
{
|
||||
header = new TarHeader();
|
||||
Array.Copy(headerBytes, 0, header.Name, 0, 100);
|
||||
Array.Copy(headerBytes, 100, header.Mode, 0, 8);
|
||||
Array.Copy(headerBytes, 108, header.UID, 0, 8);
|
||||
Array.Copy(headerBytes, 116, header.GID, 0, 8);
|
||||
Array.Copy(headerBytes, 124, header.Size, 0, 12);
|
||||
Array.Copy(headerBytes, 136, header.MTime, 0, 12);
|
||||
Array.Copy(headerBytes, 148, header.Chksum, 0, 8);
|
||||
header.TypeFlag = headerBytes[156];
|
||||
Array.Copy(headerBytes, 157, header.Linkname, 0, 100);
|
||||
Array.Copy(headerBytes, 257, header.Magic, 0, 6);
|
||||
Array.Copy(headerBytes, 263, header.Version, 0, 2);
|
||||
Array.Copy(headerBytes, 265, header.UName, 0, 32);
|
||||
Array.Copy(headerBytes, 297, header.GName, 0, 32);
|
||||
Array.Copy(headerBytes, 329, header.DevMajor, 0, 8);
|
||||
Array.Copy(headerBytes, 337, header.DevMinor, 0, 8);
|
||||
Array.Copy(headerBytes, 345, header.Prefix, 0, 155);
|
||||
Array.Copy(headerBytes, 500, header.Filler, 0, 12);
|
||||
|
||||
if (header.Filler.Length != end.Filler.Length || !header.Filler.SequenceEqual(end.Filler))
|
||||
{
|
||||
Err = new Exception("Invalid tar header");
|
||||
break;
|
||||
}
|
||||
|
||||
if (headerBytes.SequenceEqual(endBytes))
|
||||
break;
|
||||
|
||||
string name;
|
||||
if (pending_longname != null)
|
||||
{
|
||||
name = pending_longname;
|
||||
pending_longname = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
name = Encoding.UTF8.GetString(header.Name);
|
||||
}
|
||||
|
||||
long length = UnpackOctal(header.Size, header.Size.Length);
|
||||
long offset = Source.CurrentOffset;
|
||||
|
||||
long mtime = UnpackOctal(header.MTime, header.MTime.Length);
|
||||
|
||||
switch (header.TypeFlag)
|
||||
{
|
||||
case (byte)'0':
|
||||
case 0:
|
||||
{
|
||||
// Regular file.
|
||||
int n = 0, s; // name[0]
|
||||
|
||||
// This is deliberately slash-only.
|
||||
while ((s = name.IndexOf('/', n)) != -1)
|
||||
{
|
||||
n = s + 1;
|
||||
}
|
||||
|
||||
TarChild c = new TarChild
|
||||
{
|
||||
Name = name.Substring(n),
|
||||
ModTime = mtime > 0 ? DateTimeOffset.FromUnixTimeSeconds(mtime).UtcDateTime : (DateTime?)null,
|
||||
Offset = offset,
|
||||
Length = length,
|
||||
Dir = null,
|
||||
};
|
||||
|
||||
GsfInfileTar dir = DirectoryForFile(name, false);
|
||||
dir.Children.Add(c);
|
||||
break;
|
||||
}
|
||||
|
||||
case (byte)'5':
|
||||
{
|
||||
// Directory
|
||||
DirectoryForFile(name, true);
|
||||
break;
|
||||
}
|
||||
|
||||
case (byte)'L':
|
||||
{
|
||||
if (pending_longname != null || name != MAGIC_LONGNAME)
|
||||
{
|
||||
Err = new Exception("Invalid longname header");
|
||||
break;
|
||||
}
|
||||
|
||||
byte[] n = Source.Read((int)length, null);
|
||||
if (n == null)
|
||||
{
|
||||
Err = new Exception("Failed to read longname"); ;
|
||||
break;
|
||||
}
|
||||
|
||||
pending_longname = Encoding.UTF8.GetString(n);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// Other -- ignore
|
||||
break;
|
||||
}
|
||||
|
||||
// Round up to block size
|
||||
length = (length + (BLOCK_SIZE - 1)) / BLOCK_SIZE * BLOCK_SIZE;
|
||||
|
||||
if (Err == null && Source.Seek(offset + length, SeekOrigin.Begin))
|
||||
{
|
||||
Err = new Exception("Seek failed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pending_longname != null)
|
||||
{
|
||||
if (Err == null)
|
||||
Err = new Exception("Truncated archive");
|
||||
}
|
||||
|
||||
if (Err != null)
|
||||
Source.Seek(pos0, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
797
BurnOutSharp/External/libgsf/Input/GsfInfileZip.cs
vendored
797
BurnOutSharp/External/libgsf/Input/GsfInfileZip.cs
vendored
@@ -1,797 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-infile-zip.c :
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
|
||||
* Tambet Ingo (tambet@ximian.com)
|
||||
* Copyright (C) 2014 Morten Welinder (terra@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using ComponentAce.Compression.Libs.zlib;
|
||||
using static ComponentAce.Compression.Libs.zlib.zlibConst;
|
||||
using static LibGSF.GsfUtils;
|
||||
using static LibGSF.GsfZipImpl;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
// TODO: Can this be made internal?
|
||||
public class ZipInfo
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public uint Entries { get; set; }
|
||||
|
||||
public long DirPos { get; set; }
|
||||
|
||||
public List<GsfZipDirectoryEntry> DirectoryEntries { get; set; }
|
||||
|
||||
public GsfZipVDir VDir { get; set; }
|
||||
|
||||
public int RefCount { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public ZipInfo Ref()
|
||||
{
|
||||
RefCount++;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Unref()
|
||||
{
|
||||
if (RefCount-- != 1)
|
||||
return;
|
||||
|
||||
VDir.Free(false);
|
||||
for (int i = 0; i < DirectoryEntries.Count; i++)
|
||||
{
|
||||
DirectoryEntries[i].Free();
|
||||
}
|
||||
|
||||
DirectoryEntries.Clear();
|
||||
DirectoryEntries = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class GsfInfileZip : GsfInfile
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public GsfInput Source { get; set; } = null;
|
||||
|
||||
public ZipInfo Info { get; set; } = null;
|
||||
|
||||
public bool Zip64 { get; set; } = false;
|
||||
|
||||
public GsfZipVDir VDir { get; set; } = null;
|
||||
|
||||
public ZStream Stream { get; set; } = null;
|
||||
|
||||
public long RestLen { get; set; } = 0;
|
||||
|
||||
public long CRestLen { get; set; } = 0;
|
||||
|
||||
public byte[] Buf { get; set; } = null;
|
||||
|
||||
public int BufSize { get; set; } = 0;
|
||||
|
||||
public long SeekSkipped { get; set; } = 0;
|
||||
|
||||
public Exception Err { get; set; } = null;
|
||||
|
||||
public GsfInfileZip DupParent { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfInfileZip(GsfInfileZip dupParent = null)
|
||||
{
|
||||
DupParent = dupParent;
|
||||
if (DupParent != null)
|
||||
{
|
||||
// Special call from PartiallyDuplicate.
|
||||
Exception err = null;
|
||||
Source = DupParent.Source.Duplicate(ref err);
|
||||
Err = err;
|
||||
|
||||
Info = DupParent.Info.Ref();
|
||||
Zip64 = DupParent.Zip64;
|
||||
DupParent = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!InitInfo())
|
||||
VDir = Info.VDir;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the root directory of a Zip file.
|
||||
/// </summary>
|
||||
/// <param name="source">A base GsfInput</param>
|
||||
/// <param name="err">Place to store an Exception if anything goes wrong</param>
|
||||
/// <returns>The new zip file handler</returns>
|
||||
/// <remarks>This adds a reference to <paramref name="source"/>.</remarks>
|
||||
public static GsfInfileZip Create(GsfInput source, ref Exception err)
|
||||
{
|
||||
if (source == null)
|
||||
return null;
|
||||
|
||||
GsfInfileZip zip = new GsfInfileZip
|
||||
{
|
||||
Source = source,
|
||||
};
|
||||
|
||||
if (zip.Err != null)
|
||||
{
|
||||
err = zip.Err;
|
||||
return null;
|
||||
}
|
||||
|
||||
return zip;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfInfileZip()
|
||||
{
|
||||
if (Info != null)
|
||||
{
|
||||
Info.Unref();
|
||||
Info = null;
|
||||
}
|
||||
|
||||
if (Stream != null)
|
||||
{
|
||||
Stream.inflateEnd();
|
||||
Stream = null;
|
||||
}
|
||||
|
||||
Buf = null;
|
||||
|
||||
SetSource(null);
|
||||
Err = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override GsfInput DupImpl(ref Exception err)
|
||||
{
|
||||
GsfInfileZip dst = PartiallyDuplicate(ref err);
|
||||
if (dst == null)
|
||||
return null;
|
||||
|
||||
dst.VDir = VDir;
|
||||
|
||||
if (dst.VDir.DirectoryEntry != null && dst.ChildInit(ref err))
|
||||
return null;
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0)
|
||||
{
|
||||
GsfZipVDir vdir = VDir;
|
||||
long pos;
|
||||
|
||||
if (RestLen < num_bytes)
|
||||
return null;
|
||||
|
||||
switch (vdir.DirectoryEntry.CompressionMethod)
|
||||
{
|
||||
case GsfZipCompressionMethod.GSF_ZIP_STORED:
|
||||
RestLen -= num_bytes;
|
||||
pos = VDir.DirectoryEntry.DataOffset + CurrentOffset;
|
||||
if (Source.Seek(pos, SeekOrigin.Begin))
|
||||
return null;
|
||||
|
||||
return Source.Read(num_bytes, optional_buffer);
|
||||
|
||||
case GsfZipCompressionMethod.GSF_ZIP_DEFLATED:
|
||||
if (optional_buffer == null)
|
||||
{
|
||||
if (BufSize < num_bytes)
|
||||
{
|
||||
BufSize = Math.Max(num_bytes, 256);
|
||||
Buf = new byte[BufSize];
|
||||
}
|
||||
|
||||
optional_buffer = Buf;
|
||||
}
|
||||
|
||||
Stream.avail_out = num_bytes;
|
||||
Stream.next_out = optional_buffer;
|
||||
|
||||
do
|
||||
{
|
||||
int err;
|
||||
long startlen;
|
||||
|
||||
if (CRestLen > 0 && Stream.avail_in == 0)
|
||||
if (!UpdateStreamInput())
|
||||
break;
|
||||
|
||||
startlen = Stream.total_out;
|
||||
err = Stream.inflate(Z_NO_FLUSH);
|
||||
|
||||
if (err == Z_STREAM_END)
|
||||
RestLen = 0;
|
||||
else if (err == Z_OK)
|
||||
RestLen -= (Stream.total_out - startlen);
|
||||
else
|
||||
return null; // Error, probably corrupted
|
||||
|
||||
} while (RestLen != 0 && Stream.avail_out != 0);
|
||||
|
||||
return optional_buffer;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Global flag -- we don't want one per stream.
|
||||
/// </summary>
|
||||
private static bool warned = false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence)
|
||||
{
|
||||
long pos = offset;
|
||||
|
||||
// Note, that pos has already been sanity checked.
|
||||
switch (whence)
|
||||
{
|
||||
case SeekOrigin.Begin: break;
|
||||
case SeekOrigin.Current: pos += CurrentOffset; break;
|
||||
case SeekOrigin.End: pos += Size; break;
|
||||
default: return true;
|
||||
}
|
||||
|
||||
if (Stream != null)
|
||||
{
|
||||
Stream.inflateEnd();
|
||||
Stream = new ZStream();
|
||||
}
|
||||
|
||||
Exception err = null;
|
||||
if (ChildInit(ref err))
|
||||
{
|
||||
Console.Error.WriteLine("Failure initializing zip child");
|
||||
return true;
|
||||
}
|
||||
|
||||
CurrentOffset = 0;
|
||||
if (SeekEmulate(pos))
|
||||
return true;
|
||||
|
||||
SeekSkipped += pos;
|
||||
if (!warned && SeekSkipped != pos && SeekSkipped >= 1000000)
|
||||
{
|
||||
warned = true;
|
||||
Console.Error.WriteLine("Seeking in zip child streams is awfully slow.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfInput ChildByIndex(int i, ref Exception error)
|
||||
{
|
||||
GsfZipVDir child_vdir = VDir.ChildByIndex(i);
|
||||
if (child_vdir != null)
|
||||
return NewChild(child_vdir, ref error);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string NameByIndex(int i)
|
||||
{
|
||||
GsfZipVDir child_vdir = VDir.ChildByIndex(i);
|
||||
if (child_vdir != null)
|
||||
return child_vdir.Name;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfInput ChildByName(string name, ref Exception error)
|
||||
{
|
||||
GsfZipVDir child_vdir = VDir.ChildByName(name);
|
||||
if (child_vdir != null)
|
||||
return NewChild(child_vdir, ref error);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int NumChildren()
|
||||
{
|
||||
if (VDir == null)
|
||||
return -1;
|
||||
|
||||
if (!VDir.IsDirectory)
|
||||
return -1;
|
||||
|
||||
return VDir.Children.Count;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private static DateTime? MakeModTime(uint dostime)
|
||||
{
|
||||
if (dostime == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
int year = (int)(dostime >> 25) + 1980;
|
||||
int month = (int)(dostime >> 21) & 0x0f;
|
||||
int day = (int)(dostime >> 16) & 0x1f;
|
||||
int hour = (int)(dostime >> 11) & 0x0f;
|
||||
int minute = (int)(dostime >> 5) & 0x3f;
|
||||
int second = (int)(dostime & 0x1f) * 2;
|
||||
|
||||
DateTime modtime = new DateTime(year, month, day, hour, minute, second, DateTimeKind.Utc);
|
||||
|
||||
return modtime;
|
||||
}
|
||||
}
|
||||
|
||||
private long FindTrailer(uint sig, int size)
|
||||
{
|
||||
byte sig1 = (byte)(sig & 0xff);
|
||||
|
||||
long filesize = Source.Size;
|
||||
if (filesize < size)
|
||||
return -1;
|
||||
|
||||
long trailer_offset = filesize;
|
||||
long maplen = filesize & (ZIP_BUF_SIZE - 1);
|
||||
if (maplen == 0)
|
||||
maplen = ZIP_BUF_SIZE;
|
||||
|
||||
long offset = filesize - maplen; // Offset is now BUFSIZ aligned
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (Source.Seek(offset, SeekOrigin.Begin))
|
||||
return -1;
|
||||
|
||||
byte[] data = Source.Read((int)maplen, null);
|
||||
if (data == null)
|
||||
return -1;
|
||||
|
||||
int p = 0; // data[0]
|
||||
|
||||
for (int s = (int)(p + maplen - 1); (s >= p); s--, trailer_offset--)
|
||||
{
|
||||
if (data[s] == sig1 && p + maplen - 1 - s > size - 2 && GSF_LE_GET_GUINT32(data, s) == sig)
|
||||
return --trailer_offset;
|
||||
}
|
||||
|
||||
// Not found in currently mapped block, so update it if
|
||||
// there is some room in before. The requirements are..
|
||||
// (a) mappings should overlap so that trailer can cross BUFSIZ-boundary
|
||||
// (b) trailer cannot be farther away than 64K from fileend
|
||||
|
||||
// Outer loop cond
|
||||
if (offset <= 0)
|
||||
return -1;
|
||||
|
||||
// Outer loop step
|
||||
offset -= ZIP_BUF_SIZE / 2;
|
||||
maplen = Math.Min(filesize - offset, ZIP_BUF_SIZE);
|
||||
trailer_offset = offset + maplen;
|
||||
|
||||
if (filesize - offset > 64 * 1024)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] DirectoryEntryExtraField(byte[] extra, int extraPtr, int elen, ExtraFieldTags typ, out uint pflen)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (elen == 0)
|
||||
{
|
||||
pflen = 0;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (elen < 4)
|
||||
{
|
||||
pflen = 0;
|
||||
return null;
|
||||
}
|
||||
|
||||
ExtraFieldTags ftyp = (ExtraFieldTags)GSF_LE_GET_GUINT16(extra, extraPtr);
|
||||
uint flen = GSF_LE_GET_GUINT16(extra, extraPtr + 2);
|
||||
if (flen > elen - 4)
|
||||
{
|
||||
pflen = 0;
|
||||
return null;
|
||||
}
|
||||
|
||||
extraPtr += 4;
|
||||
elen -= 4;
|
||||
if (ftyp == typ)
|
||||
{
|
||||
// Found the extended data.
|
||||
pflen = flen;
|
||||
return extra;
|
||||
}
|
||||
|
||||
extraPtr += (int)flen;
|
||||
elen -= (int)flen;
|
||||
}
|
||||
}
|
||||
|
||||
private GsfZipDirectoryEntry NewDirectoryEntry(ref long offset)
|
||||
{
|
||||
byte[] header = new byte[ZIP_DIRENT_SIZE];
|
||||
|
||||
// Read fixed-length part of data and check the header
|
||||
byte[] data = header;
|
||||
if (Source.Seek(offset, SeekOrigin.Begin)
|
||||
|| Source.Read(ZIP_DIRENT_SIZE, header) != null
|
||||
|| GSF_LE_GET_GUINT32(data, 0) != ZIP_DIRENT_SIGNATURE)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ushort name_len = GSF_LE_GET_GUINT16(header, ZIP_DIRENT_NAME_SIZE);
|
||||
ushort extras_len = GSF_LE_GET_GUINT16(header, ZIP_DIRENT_EXTRAS_SIZE);
|
||||
ushort comment_len = GSF_LE_GET_GUINT16(header, ZIP_DIRENT_COMMENT_SIZE);
|
||||
int vlen = name_len + extras_len + comment_len;
|
||||
|
||||
// Read variable part
|
||||
byte[] variable = Source.Read(vlen, null);
|
||||
if (variable == null && vlen > 0)
|
||||
return null;
|
||||
|
||||
byte[] extra = DirectoryEntryExtraField(variable, name_len, extras_len, ExtraFieldTags.ZIP_DIRENT_EXTRA_FIELD_ZIP64, out uint elen);
|
||||
int extraPtr = 0; // extra[0];
|
||||
bool zip64 = (extra != null);
|
||||
|
||||
uint flags = GSF_LE_GET_GUINT32(header, ZIP_DIRENT_FLAGS);
|
||||
GsfZipCompressionMethod compression_method = (GsfZipCompressionMethod)GSF_LE_GET_GUINT16(header, ZIP_DIRENT_COMPR_METHOD);
|
||||
uint dostime = GSF_LE_GET_GUINT32(header, ZIP_DIRENT_DOSTIME);
|
||||
uint crc32 = GSF_LE_GET_GUINT32(header, ZIP_DIRENT_CRC32);
|
||||
ulong csize = GSF_LE_GET_GUINT32(header, ZIP_DIRENT_CSIZE);
|
||||
ulong usize = GSF_LE_GET_GUINT32(header, ZIP_DIRENT_USIZE);
|
||||
ulong off = GSF_LE_GET_GUINT32(header, ZIP_DIRENT_OFFSET);
|
||||
uint disk_start = GSF_LE_GET_GUINT16(header, ZIP_DIRENT_DISKSTART);
|
||||
|
||||
if (usize == 0xffffffffu && elen >= 8)
|
||||
{
|
||||
usize = GSF_LE_GET_GUINT64(extra, extraPtr);
|
||||
extraPtr += 8;
|
||||
elen -= 8;
|
||||
}
|
||||
if (csize == 0xffffffffu && elen >= 8)
|
||||
{
|
||||
csize = GSF_LE_GET_GUINT64(extra, extraPtr);
|
||||
extraPtr += 8;
|
||||
elen -= 8;
|
||||
}
|
||||
if (off == 0xffffffffu && elen >= 8)
|
||||
{
|
||||
off = GSF_LE_GET_GUINT64(extra, extraPtr);
|
||||
extraPtr += 8;
|
||||
elen -= 8;
|
||||
}
|
||||
if (disk_start == 0xffffu && elen >= 4)
|
||||
{
|
||||
disk_start = GSF_LE_GET_GUINT32(extra, extraPtr);
|
||||
extraPtr += 4;
|
||||
elen -= 4;
|
||||
}
|
||||
|
||||
byte[] name = new byte[name_len + 1];
|
||||
Array.Copy(variable, name, name_len);
|
||||
name[name_len] = 0x00;
|
||||
|
||||
GsfZipDirectoryEntry dirent = GsfZipDirectoryEntry.Create();
|
||||
dirent.Name = Encoding.UTF8.GetString(name);
|
||||
|
||||
dirent.Flags = (int)flags;
|
||||
dirent.CompressionMethod = compression_method;
|
||||
dirent.CRC32 = crc32;
|
||||
dirent.CompressedSize = (long)csize;
|
||||
dirent.UncompressedSize = (long)usize;
|
||||
dirent.Offset = (long)off;
|
||||
dirent.DosTime = dostime;
|
||||
dirent.Zip64 = zip64;
|
||||
|
||||
//g_print("%s = 0x%x @ %" GSF_OFF_T_FORMAT "\n", name, off, *offset);
|
||||
|
||||
offset += ZIP_DIRENT_SIZE + vlen;
|
||||
|
||||
return dirent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a partial duplicate.
|
||||
/// </summary>
|
||||
private GsfInfileZip PartiallyDuplicate(ref Exception err)
|
||||
{
|
||||
GsfInfileZip dst = new GsfInfileZip(this);
|
||||
|
||||
if (dst.Err != null)
|
||||
{
|
||||
err = dst.Err;
|
||||
return null;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read zip headers and do some sanity checking
|
||||
/// along the way.
|
||||
/// </summary>
|
||||
/// <returns>True on error setting Err.</returns>
|
||||
private bool ReadDirectoryEntries()
|
||||
{
|
||||
// Find and check the trailing header
|
||||
long offset = FindTrailer(ZIP_TRAILER_SIGNATURE, ZIP_TRAILER_SIZE);
|
||||
if (offset < ZIP_ZIP64_LOCATOR_SIZE || Source.Seek(offset - ZIP_ZIP64_LOCATOR_SIZE, SeekOrigin.Begin))
|
||||
{
|
||||
Err = new Exception("Broken zip file structure");
|
||||
return true;
|
||||
}
|
||||
|
||||
byte[] locator = Source.Read(ZIP_TRAILER_SIZE + ZIP_ZIP64_LOCATOR_SIZE, null);
|
||||
if (locator == null)
|
||||
{
|
||||
Err = new Exception("Broken zip file structure");
|
||||
return true;
|
||||
}
|
||||
|
||||
int data = ZIP_ZIP64_LOCATOR_SIZE; // locator[0] + ZIP_ZIP64_LOCATOR_SIZE
|
||||
|
||||
ulong entries = GSF_LE_GET_GUINT16(locator, data + ZIP_TRAILER_ENTRIES);
|
||||
ulong dir_pos = GSF_LE_GET_GUINT32(locator, data + ZIP_TRAILER_DIR_POS);
|
||||
|
||||
if (GSF_LE_GET_GUINT32(locator, 0) == ZIP_ZIP64_LOCATOR_SIGNATURE)
|
||||
{
|
||||
Zip64 = true;
|
||||
|
||||
data = 0; // locator[0]
|
||||
uint disk = GSF_LE_GET_GUINT32(locator, data + ZIP_ZIP64_LOCATOR_DISK);
|
||||
long zip64_eod_offset = (long)GSF_LE_GET_GUINT64(locator, data + ZIP_ZIP64_LOCATOR_OFFSET);
|
||||
uint disks = GSF_LE_GET_GUINT32(locator, data + ZIP_ZIP64_LOCATOR_DISKS);
|
||||
|
||||
if (disk != 0 || disks != 1)
|
||||
{
|
||||
Err = new Exception("Broken zip file structure");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Source.Seek((int)zip64_eod_offset, SeekOrigin.Begin))
|
||||
{
|
||||
Err = new Exception("Broken zip file structure");
|
||||
return true;
|
||||
}
|
||||
|
||||
locator = Source.Read(ZIP_TRAILER64_SIZE, null);
|
||||
if (locator == null || GSF_LE_GET_GUINT32(locator, data) != ZIP_TRAILER64_SIGNATURE)
|
||||
{
|
||||
Err = new Exception("Broken zip file structure");
|
||||
return true;
|
||||
}
|
||||
|
||||
entries = GSF_LE_GET_GUINT64(locator, data + ZIP_TRAILER64_ENTRIES);
|
||||
dir_pos = GSF_LE_GET_GUINT64(locator, data + ZIP_TRAILER64_DIR_POS);
|
||||
}
|
||||
|
||||
Info = new ZipInfo()
|
||||
{
|
||||
DirectoryEntries = new List<GsfZipDirectoryEntry>(),
|
||||
RefCount = 1,
|
||||
Entries = (uint)entries,
|
||||
DirPos = (long)dir_pos,
|
||||
};
|
||||
|
||||
// Read the directory
|
||||
uint i = 0;
|
||||
for (offset = (long)dir_pos; i < entries; i++)
|
||||
{
|
||||
GsfZipDirectoryEntry d = NewDirectoryEntry(ref offset);
|
||||
if (d == null)
|
||||
{
|
||||
Err = new Exception("Error reading zip dirent");
|
||||
return true;
|
||||
}
|
||||
|
||||
Info.DirectoryEntries.Add(d);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void BuildVirtualDirectories()
|
||||
{
|
||||
Info.VDir = GsfZipVDir.Create(string.Empty, true, null);
|
||||
for (int i = 0; i < Info.DirectoryEntries.Count; i++)
|
||||
{
|
||||
GsfZipDirectoryEntry dirent = Info.DirectoryEntries[i];
|
||||
Info.VDir.Insert(dirent.Name, dirent);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read zip headers and do some sanity checking
|
||||
/// along the way.
|
||||
/// </summary>
|
||||
/// <returns>True on error setting Err.</returns>
|
||||
private bool InitInfo()
|
||||
{
|
||||
bool ret = ReadDirectoryEntries();
|
||||
if (ret != false)
|
||||
return ret;
|
||||
|
||||
BuildVirtualDirectories();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <returns>Returns true on error</returns>
|
||||
private bool ChildInit(ref Exception errmsg)
|
||||
{
|
||||
byte[] data = null;
|
||||
|
||||
GsfZipDirectoryEntry dirent = VDir.DirectoryEntry;
|
||||
|
||||
// Skip local header
|
||||
// Should test tons of other info, but trust that those are correct
|
||||
|
||||
string err = null;
|
||||
if (Source.Seek(dirent.Offset, SeekOrigin.Begin))
|
||||
{
|
||||
err = "Error seeking to zip header";
|
||||
}
|
||||
else if ((data = Source.Read(ZIP_HEADER_SIZE, null)) == null)
|
||||
{
|
||||
err = "Error reading zip header";
|
||||
}
|
||||
else if (GSF_LE_GET_GUINT32(data, 0) != ZIP_HEADER_SIGNATURE)
|
||||
{
|
||||
err = "Error incorrect zip header";
|
||||
Console.Error.WriteLine($"Header is 0x{GSF_LE_GET_GUINT32(data, 0):x}");
|
||||
Console.Error.WriteLine($"Expected 0x{ZIP_HEADER_SIGNATURE:x}");
|
||||
}
|
||||
|
||||
if (err != null)
|
||||
{
|
||||
errmsg = new Exception(err);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint name_len = GSF_LE_GET_GUINT16(data, ZIP_HEADER_NAME_SIZE);
|
||||
uint extras_len = GSF_LE_GET_GUINT16(data, ZIP_HEADER_EXTRAS_SIZE);
|
||||
|
||||
dirent.DataOffset = dirent.Offset + ZIP_HEADER_SIZE + name_len + extras_len;
|
||||
RestLen = dirent.UncompressedSize;
|
||||
CRestLen = dirent.CompressedSize;
|
||||
|
||||
if (dirent.CompressionMethod != GsfZipCompressionMethod.GSF_ZIP_STORED)
|
||||
{
|
||||
if (Stream == null)
|
||||
Stream = new ZStream();
|
||||
|
||||
int errno = Stream.inflateInit(-15);
|
||||
if (errno != Z_OK)
|
||||
{
|
||||
errmsg = new Exception("Problem uncompressing stream");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool UpdateStreamInput()
|
||||
{
|
||||
if (CRestLen == 0)
|
||||
return false;
|
||||
|
||||
uint read_now = (uint)Math.Min(CRestLen, ZIP_BLOCK_SIZE);
|
||||
|
||||
long pos = VDir.DirectoryEntry.DataOffset + Stream.total_in;
|
||||
if (Source.Seek(pos, SeekOrigin.Begin))
|
||||
return false;
|
||||
|
||||
byte[] data = Source.Read((int)read_now, null);
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
CRestLen -= read_now;
|
||||
Stream.next_in = data; // next input byte
|
||||
Stream.avail_in = (int)read_now; // number of bytes available at next_in
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private GsfInput NewChild(GsfZipVDir vdir, ref Exception err)
|
||||
{
|
||||
GsfZipDirectoryEntry dirent = vdir.DirectoryEntry;
|
||||
GsfInfileZip child = PartiallyDuplicate(ref err);
|
||||
|
||||
if (child == null)
|
||||
return null;
|
||||
|
||||
child.Name = vdir.Name;
|
||||
child.Container = this;
|
||||
child.VDir = vdir;
|
||||
|
||||
if (dirent != null)
|
||||
{
|
||||
child.Size = dirent.UncompressedSize;
|
||||
if (dirent.DosTime != 0)
|
||||
{
|
||||
DateTime? modtime = MakeModTime(dirent.DosTime);
|
||||
child.ModTime = modtime;
|
||||
}
|
||||
|
||||
if (child.ChildInit(ref err) != false)
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
child.Size = 0;
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
private void SetSource(GsfInput src)
|
||||
{
|
||||
if (src != null)
|
||||
src = GsfInputProxy.Create(src);
|
||||
|
||||
Source = src;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
531
BurnOutSharp/External/libgsf/Input/GsfInput.cs
vendored
531
BurnOutSharp/External/libgsf/Input/GsfInput.cs
vendored
@@ -1,531 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-input.c: interface for used by the ole layer to read raw data
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using LibGSF.Output;
|
||||
using static LibGSF.GsfMSOleUtils;
|
||||
using static LibGSF.GsfUtils;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public abstract class GsfInput : IDisposable
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private const int GSF_READ_BUFSIZE = 1024 * 4;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public long Size { get; protected internal set; }
|
||||
|
||||
public long CurrentOffset { get; protected internal set; }
|
||||
|
||||
public string Name { get; protected internal set; }
|
||||
|
||||
public GsfInfile Container { get; protected internal set; }
|
||||
|
||||
public DateTime? ModTime { get; protected internal set; }
|
||||
|
||||
public GsfOpenPkgRels Relations { get; protected internal set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Container = null;
|
||||
Name = null;
|
||||
ModTime = default;
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
Size = 0;
|
||||
CurrentOffset = 0;
|
||||
Name = null;
|
||||
Container = null;
|
||||
}
|
||||
|
||||
/// <param name="err">Place to store an Exception if anything goes wrong</param>
|
||||
/// <returns>The duplicate</returns>
|
||||
public GsfInput Duplicate(ref Exception err)
|
||||
{
|
||||
GsfInput dst = DupImpl(ref err);
|
||||
if (dst != null)
|
||||
{
|
||||
if (dst.Size != Size)
|
||||
{
|
||||
err = new Exception("Duplicate size mismatch");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (dst.Seek(CurrentOffset, SeekOrigin.Begin))
|
||||
{
|
||||
err = new Exception("Seek failed");
|
||||
return null;
|
||||
}
|
||||
|
||||
dst.Name = Name;
|
||||
dst.Container = Container;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to open a 'sibling' of input. The caller is responsible for
|
||||
/// managing the resulting object.
|
||||
/// </summary>
|
||||
/// <param name="name">Name.</param>
|
||||
/// <param name="err">Place to store an Exception if anything goes wrong</param>
|
||||
/// <returns>A related GsfInput</returns>
|
||||
/// <remarks>
|
||||
/// UNIMPLEMENTED BY ANY BACKEND
|
||||
/// and it is probably unnecessary. Container provides
|
||||
/// enough power to do what is necessary.
|
||||
/// </remarks>
|
||||
public GsfInput OpenSibling(string name, ref Exception err) => OpenSiblingImpl(name, ref err);
|
||||
|
||||
/// <summary>
|
||||
/// Are we at the end of the file?
|
||||
/// </summary>
|
||||
/// <returns>True if the input is at the eof.</returns>
|
||||
public bool EOF() => CurrentOffset >= Size;
|
||||
|
||||
/// <summary>
|
||||
/// Read at least <paramref name="num_bytes"/>. Does not change the current position if there
|
||||
/// is an error. Will only read if the entire amount can be read. Invalidates
|
||||
/// the buffer associated with previous calls to gsf_input_read.
|
||||
/// </summary>
|
||||
/// <param name="num_bytes">Number of bytes to read</param>
|
||||
/// <param name="optional_buffer">Pointer to destination memory area</param>
|
||||
/// <returns>
|
||||
/// Pointer to the buffer or null if there is
|
||||
/// an error or 0 bytes are requested.
|
||||
/// </returns>
|
||||
public byte[] Read(int num_bytes, byte[] optional_buffer, int bufferPtr = 0)
|
||||
{
|
||||
long newpos = CurrentOffset + num_bytes;
|
||||
|
||||
if (newpos <= CurrentOffset || newpos > Size)
|
||||
return null;
|
||||
|
||||
byte[] res = ReadImpl(num_bytes, optional_buffer, bufferPtr);
|
||||
if (res == null)
|
||||
return null;
|
||||
|
||||
CurrentOffset = newpos;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read <paramref name="num_bytes"/>. Does not change the current position if there
|
||||
/// is an error. Will only read if the entire amount can be read.
|
||||
/// </summary>
|
||||
/// <param name="num_bytes">Number of bytes to read</param>
|
||||
/// <param name="bytes_read">Copy of <paramref name="num_bytes"/></param>
|
||||
/// <returns>The data read.</returns>
|
||||
public byte[] Read0(int num_bytes, out int bytes_read)
|
||||
{
|
||||
bytes_read = num_bytes;
|
||||
|
||||
if (num_bytes < 0 || (long)num_bytes > Remaining())
|
||||
return null;
|
||||
|
||||
byte[] res = new byte[num_bytes];
|
||||
if (Read(num_bytes, res) != null)
|
||||
return res;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <returns>The number of bytes left in the file.</returns>
|
||||
public long Remaining() => Size - CurrentOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Move the current location in the input stream.
|
||||
/// </summary>
|
||||
/// <param name="offset">Target offset</param>
|
||||
/// <param name="whence">
|
||||
/// Determines whether the offset is relative to the beginning or
|
||||
/// the end of the stream, or to the current location.
|
||||
/// </param>
|
||||
/// <returns>True on error.</returns>
|
||||
public bool Seek(long offset, SeekOrigin whence)
|
||||
{
|
||||
long pos = offset;
|
||||
|
||||
switch (whence)
|
||||
{
|
||||
case SeekOrigin.Begin: break;
|
||||
case SeekOrigin.Current: pos += CurrentOffset; break;
|
||||
case SeekOrigin.End: pos += Size; break;
|
||||
default: return true;
|
||||
}
|
||||
|
||||
if (pos < 0 || pos > Size)
|
||||
return true;
|
||||
|
||||
// If we go nowhere, just return. This in particular handles null
|
||||
// seeks for streams with no seek method.
|
||||
if (pos == CurrentOffset)
|
||||
return false;
|
||||
|
||||
if (SeekImpl(offset, whence))
|
||||
return true;
|
||||
|
||||
CurrentOffset = pos;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <param name="filename">The (fs-sys encoded) filename</param>
|
||||
/// <returns>True if the assignment was ok.</returns>
|
||||
public bool SetNameFromFilename(string filename)
|
||||
{
|
||||
string name = null;
|
||||
if (filename != null)
|
||||
{
|
||||
byte[] filenameBytes = Encoding.Unicode.GetBytes(filename);
|
||||
filenameBytes = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, filenameBytes);
|
||||
name = Encoding.UTF8.GetString(filenameBytes);
|
||||
}
|
||||
|
||||
Name = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emulate forward seeks by reading.
|
||||
/// </summary>
|
||||
/// <param name="pos">Absolute position to seek to</param>
|
||||
/// <returns>True if the emulation failed.</returns>
|
||||
public bool SeekEmulate(long pos)
|
||||
{
|
||||
if (pos < CurrentOffset)
|
||||
return true;
|
||||
|
||||
while (pos > CurrentOffset)
|
||||
{
|
||||
long readcount = Math.Min(pos - CurrentOffset, 8192);
|
||||
if (Read((int)readcount, null) == null)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy the contents from input to <paramref name="output"/> from their respective
|
||||
/// current positions. So if you want to be sure to copy *everything*,
|
||||
/// make sure to call input.Seek(0, SeekOrigin.Begin) and
|
||||
/// output.Seek(0, SeekOrigin.Begin) first, if applicable.
|
||||
/// </summary>
|
||||
/// <param name="output">A non-null GsfOutput</param>
|
||||
/// <returns>True on success</returns>
|
||||
public bool Copy(GsfOutput output)
|
||||
{
|
||||
if (output == null)
|
||||
return false;
|
||||
|
||||
bool success = true;
|
||||
long remaining;
|
||||
while (success && (remaining = Remaining()) > 0)
|
||||
{
|
||||
long toread = Math.Min(remaining, GSF_READ_BUFSIZE);
|
||||
byte[] buffer = Read((int)toread, null);
|
||||
if (buffer != null && buffer.Length != 0)
|
||||
success = output.Write((int)toread, buffer);
|
||||
else
|
||||
success = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This functions takes ownership of the incoming reference and yields a
|
||||
/// new one as its output.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A stream equivalent to the source stream,
|
||||
/// but uncompressed if the source was compressed.
|
||||
/// </returns>
|
||||
public GsfInput Uncompress()
|
||||
{
|
||||
long cur_offset = CurrentOffset;
|
||||
byte[] header = new byte[4];
|
||||
|
||||
if (Seek(0, SeekOrigin.Begin))
|
||||
goto error;
|
||||
|
||||
// Read header up front, so we avoid extra seeks in tests.
|
||||
if (Read(4, header) == null)
|
||||
goto error;
|
||||
|
||||
// Let's try gzip.
|
||||
{
|
||||
byte[] gzip_sig = new byte[2] { 0x1f, 0x8b };
|
||||
if (header.Take(2).SequenceEqual(gzip_sig))
|
||||
{
|
||||
Exception err = null;
|
||||
GsfInput res = GsfInputGZip.Create(null, ref err);
|
||||
if (res != null)
|
||||
return res.Uncompress();
|
||||
}
|
||||
}
|
||||
|
||||
// Let's try bzip.
|
||||
{
|
||||
byte[] bzip_sig = new byte[3] { (byte)'B', (byte)'Z', (byte)'h' };
|
||||
if (header.Take(3).SequenceEqual(bzip_sig))
|
||||
{
|
||||
Exception err = null;
|
||||
GsfInput res = GsfInputMemory.CreateFromBzip(null, ref err);
|
||||
if (res != null)
|
||||
return res.Uncompress();
|
||||
}
|
||||
}
|
||||
|
||||
// Other methods go here.
|
||||
|
||||
error:
|
||||
Seek(cur_offset, SeekOrigin.Begin);
|
||||
return this;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GIO
|
||||
|
||||
internal void SetNameFromFile(string file)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return;
|
||||
|
||||
FileInfo info = new FileInfo(file);
|
||||
SetNameFromFilename(info.Name);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MS-OLE
|
||||
|
||||
/// <summary>
|
||||
/// Decompresses an LZ compressed stream.
|
||||
/// </summary>
|
||||
/// <param name="offset">Offset into it for start byte of compresse stream</param>
|
||||
/// <returns>A GByteArray that the caller is responsible for freeing</returns>
|
||||
internal byte[] InflateMSOLE(long offset)
|
||||
{
|
||||
uint pos = 0;
|
||||
uint shift;
|
||||
byte[] flag = new byte[1];
|
||||
byte[] buffer = new byte[VBA_COMPRESSION_WINDOW];
|
||||
byte[] tmp = new byte[2];
|
||||
bool clean = true;
|
||||
|
||||
if (Seek(offset, SeekOrigin.Begin))
|
||||
return null;
|
||||
|
||||
byte[] res = new byte[0];
|
||||
|
||||
// Explaination from libole2/ms-ole-vba.c
|
||||
|
||||
// The first byte is a flag byte. Each bit in this byte
|
||||
// determines what the next byte is. If the bit is zero,
|
||||
// the next byte is a character. Otherwise the next two
|
||||
// bytes contain the number of characters to copy from the
|
||||
// umcompresed buffer and where to copy them from (offset,
|
||||
// length).
|
||||
|
||||
while (Read(1, flag) != null)
|
||||
{
|
||||
for (uint mask = 1; mask < 0x100; mask <<= 1)
|
||||
{
|
||||
if ((flag[0] & mask) != 0)
|
||||
{
|
||||
if ((tmp = Read(2, null)) == null)
|
||||
break;
|
||||
|
||||
uint win_pos = pos % VBA_COMPRESSION_WINDOW;
|
||||
if (win_pos <= 0x80)
|
||||
{
|
||||
if (win_pos <= 0x20)
|
||||
shift = (uint)((win_pos <= 0x10) ? 12 : 11);
|
||||
else
|
||||
shift = (uint)((win_pos <= 0x40) ? 10 : 9);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (win_pos <= 0x200)
|
||||
shift = (uint)((win_pos <= 0x100) ? 8 : 7);
|
||||
else if (win_pos <= 0x800)
|
||||
shift = (uint)((win_pos <= 0x400) ? 6 : 5);
|
||||
else
|
||||
shift = 4;
|
||||
}
|
||||
|
||||
ushort token = GSF_LE_GET_GUINT16(tmp, 0);
|
||||
ushort len = (ushort)((token & ((1 << (int)shift) - 1)) + 3);
|
||||
uint distance = (uint)(token >> (int)shift);
|
||||
clean = true;
|
||||
|
||||
if (distance >= pos)
|
||||
{
|
||||
Console.Error.WriteLine("Corrupted compressed stream");
|
||||
break;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < len; i++)
|
||||
{
|
||||
uint srcpos = (pos - distance - 1) % VBA_COMPRESSION_WINDOW;
|
||||
byte c = buffer[srcpos];
|
||||
buffer[pos++ % VBA_COMPRESSION_WINDOW] = c;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((pos != 0) && ((pos % VBA_COMPRESSION_WINDOW) == 0) && clean)
|
||||
{
|
||||
Read(2, null);
|
||||
clean = false;
|
||||
|
||||
List<byte> temp = new List<byte>(res);
|
||||
temp.AddRange(buffer);
|
||||
res = temp.ToArray();
|
||||
break;
|
||||
}
|
||||
|
||||
if (Read(1, buffer, (int)(pos % VBA_COMPRESSION_WINDOW)) != null)
|
||||
pos++;
|
||||
|
||||
clean = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((pos % VBA_COMPRESSION_WINDOW) != 0)
|
||||
{
|
||||
List<byte> temp = new List<byte>(res);
|
||||
temp.AddRange(buffer.Skip((int)(pos % VBA_COMPRESSION_WINDOW)));
|
||||
res = temp.ToArray();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MS-VBA
|
||||
|
||||
/// <summary>
|
||||
/// Decompresses VBA stream.
|
||||
/// </summary>
|
||||
/// <param name="offset">Offset into it for start byte of compressed stream</param>
|
||||
/// <param name="size">Size of the returned array</param>
|
||||
/// <param name="add_null_terminator">Whenever add or not null at the end of array</param>
|
||||
/// <returns>A pointer to byte array</returns>
|
||||
internal byte[] InflateMSVBA(long offset, out int size, bool add_null_terminator)
|
||||
{
|
||||
size = 0;
|
||||
|
||||
byte[] sig = new byte[1];
|
||||
Read(1, sig);
|
||||
if (sig[0] != 1) // Should start with 0x01
|
||||
return null;
|
||||
|
||||
offset++;
|
||||
|
||||
List<byte> res = new List<byte>();
|
||||
|
||||
long length = Size;
|
||||
while (offset < length)
|
||||
{
|
||||
byte[] tmp = Read(2, null);
|
||||
if (tmp == null)
|
||||
break;
|
||||
|
||||
ushort chunk_hdr = GSF_LE_GET_GUINT16(tmp, 0);
|
||||
offset += 2;
|
||||
|
||||
GsfInput chunk;
|
||||
if (0xB000 == (chunk_hdr & 0xF000) && (chunk_hdr & 0xFFF) > 0 && (length - offset < 4094))
|
||||
{
|
||||
if (length < offset + (chunk_hdr & 0xFFF))
|
||||
break;
|
||||
|
||||
chunk = GsfInputProxy.Create(this, offset, (long)(chunk_hdr & 0xFFF) + 1);
|
||||
offset += (chunk_hdr & 0xFFF) + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (length < offset + 4094)
|
||||
{
|
||||
chunk = GsfInputProxy.Create(this, offset, length - offset);
|
||||
offset = length;
|
||||
}
|
||||
else
|
||||
{
|
||||
chunk = GsfInputProxy.Create(this, offset, 4094);
|
||||
offset += 4094;
|
||||
}
|
||||
}
|
||||
|
||||
if (chunk != null)
|
||||
{
|
||||
byte[] tmpres = chunk.InflateMSOLE(0);
|
||||
Seek(offset, SeekOrigin.Current);
|
||||
res.AddRange(tmpres);
|
||||
}
|
||||
}
|
||||
|
||||
if (res == null)
|
||||
return null;
|
||||
if (add_null_terminator)
|
||||
res.Add(0x00);
|
||||
|
||||
size = res.Count;
|
||||
|
||||
return res.ToArray();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Virtual Functions
|
||||
|
||||
protected virtual GsfInput DupImpl(ref Exception err) => null;
|
||||
|
||||
protected virtual GsfInput OpenSiblingImpl(string name, ref Exception err) => null;
|
||||
|
||||
protected virtual byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0) => null;
|
||||
|
||||
protected virtual bool SeekImpl(long offset, SeekOrigin whence) => false;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
140
BurnOutSharp/External/libgsf/Input/GsfInputBzip.cs
vendored
140
BurnOutSharp/External/libgsf/Input/GsfInputBzip.cs
vendored
@@ -1,140 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-input-iochannel.c: wrapper for glib's GIOChannel
|
||||
*
|
||||
* Copyright (C) 2003-2006 Dom Lachowicz (cinamod@hotmail.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public partial class GsfInputMemory
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private const int BZ_BUFSIZ = 1024;
|
||||
|
||||
#region bzlib.h
|
||||
|
||||
private const int BZ_RUN = 0;
|
||||
private const int BZ_FLUSH = 1;
|
||||
private const int BZ_FINISH = 2;
|
||||
|
||||
private const int BZ_OK = 0;
|
||||
private const int BZ_RUN_OK = 1;
|
||||
private const int BZ_FLUSH_OK = 2;
|
||||
private const int BZ_FINISH_OK = 3;
|
||||
private const int BZ_STREAM_END = 4;
|
||||
private const int BZ_SEQUENCE_ERROR = (-1);
|
||||
private const int BZ_PARAM_ERROR = (-2);
|
||||
private const int BZ_MEM_ERROR = (-3);
|
||||
private const int BZ_DATA_ERROR = (-4);
|
||||
private const int BZ_DATA_ERROR_MAGIC = (-5);
|
||||
private const int BZ_IO_ERROR = (-6);
|
||||
private const int BZ_UNEXPECTED_EOF = (-7);
|
||||
private const int BZ_OUTBUFF_FULL = (-8);
|
||||
private const int BZ_CONFIG_ERROR = (-9);
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: Implement BZIP reading
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <param name="err">Place to store an Exception if anything goes wrong</param>
|
||||
/// <returns>A new GsfInputMemory or null.</returns>
|
||||
public static GsfInputMemory CreateFromBzip(GsfInput source, ref Exception err)
|
||||
{
|
||||
#if BZIP2
|
||||
bz_stream bzstm;
|
||||
GsfInput* mem = NULL;
|
||||
GsfOutput* sink = NULL;
|
||||
guint8 out_buf[BZ_BUFSIZ];
|
||||
int bzerr = BZ_OK;
|
||||
|
||||
g_return_val_if_fail(source != NULL, NULL);
|
||||
|
||||
memset(&bzstm, 0, sizeof(bzstm));
|
||||
if (BZ_OK != BZ2_bzDecompressInit(&bzstm, 0, 0))
|
||||
{
|
||||
if (err)
|
||||
*err = g_error_new(gsf_input_error_id(), 0,
|
||||
_("BZ2 decompress init failed"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sink = gsf_output_memory_new();
|
||||
|
||||
for (; ; )
|
||||
{
|
||||
bzstm.next_out = (char*)out_buf;
|
||||
bzstm.avail_out = (unsigned int)sizeof(out_buf);
|
||||
|
||||
if (bzstm.avail_in == 0)
|
||||
{
|
||||
bzstm.avail_in = (unsigned int)MIN(gsf_input_remaining(source), BZ_BUFSIZ);
|
||||
bzstm.next_in = (char*)gsf_input_read(source, bzstm.avail_in, NULL);
|
||||
}
|
||||
|
||||
bzerr = BZ2_bzDecompress(&bzstm);
|
||||
if (bzerr != BZ_OK && bzerr != BZ_STREAM_END)
|
||||
{
|
||||
if (err)
|
||||
*err = g_error_new(gsf_input_error_id(), 0,
|
||||
_("BZ2 decompress failed"));
|
||||
BZ2_bzDecompressEnd(&bzstm);
|
||||
gsf_output_close(sink);
|
||||
g_object_unref(sink);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gsf_output_write(sink, BZ_BUFSIZ - bzstm.avail_out, out_buf);
|
||||
if (bzerr == BZ_STREAM_END)
|
||||
break;
|
||||
}
|
||||
|
||||
gsf_output_close(sink);
|
||||
|
||||
if (BZ_OK != BZ2_bzDecompressEnd(&bzstm)) {
|
||||
if (err)
|
||||
* err = g_error_new(gsf_input_error_id(), 0,
|
||||
_("BZ2 decompress end failed"));
|
||||
g_object_unref(sink);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mem = gsf_input_memory_new_clone(
|
||||
gsf_output_memory_get_bytes (GSF_OUTPUT_MEMORY (sink)),
|
||||
gsf_output_size(sink));
|
||||
|
||||
if (mem != NULL)
|
||||
gsf_input_set_name(mem, gsf_input_name (source));
|
||||
|
||||
g_object_unref(sink);
|
||||
return mem;
|
||||
#else
|
||||
err = new Exception("BZ2 support not enabled");
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
444
BurnOutSharp/External/libgsf/Input/GsfInputGZip.cs
vendored
444
BurnOutSharp/External/libgsf/Input/GsfInputGZip.cs
vendored
@@ -1,444 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-input-gzip.c: wrapper to uncompress gzipped input
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
|
||||
* Copyright (C) 2005-2006 Morten Welinder (terra@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using ComponentAce.Compression.Libs.zlib;
|
||||
using static ComponentAce.Compression.Libs.zlib.zlibConst;
|
||||
using static LibGSF.GsfUtils;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public class GsfInputGZip : GsfInput
|
||||
{
|
||||
#region Enums
|
||||
|
||||
[Flags]
|
||||
private enum GZIP_HEADER_FLAGS : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// File contains text ?
|
||||
/// </summary>
|
||||
GZIP_IS_ASCII = 0x01,
|
||||
|
||||
/// <summary>
|
||||
/// There is a CRC in the header
|
||||
/// </summary>
|
||||
GZIP_HEADER_CRC = 0x02,
|
||||
|
||||
/// <summary>
|
||||
/// There is an 'extra' field
|
||||
/// </summary>
|
||||
GZIP_EXTRA_FIELD = 0x04,
|
||||
|
||||
/// <summary>
|
||||
/// The original is stored
|
||||
/// </summary>
|
||||
GZIP_ORIGINAL_NAME = 0x08,
|
||||
|
||||
/// <summary>
|
||||
/// There is a comment in the header
|
||||
/// </summary>
|
||||
GZIP_HAS_COMMENT = 0x10,
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Compressed data
|
||||
/// </summary>
|
||||
public GsfInput Source { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// No header and no trailer.
|
||||
/// </summary>
|
||||
public bool Raw { get; set; } = false;
|
||||
|
||||
public Exception Err { get; set; } = null;
|
||||
|
||||
public long UncompressedSize { get; set; } = -1;
|
||||
|
||||
public bool StopByteAdded { get; set; }
|
||||
|
||||
public ZStream Stream { get; set; } = new ZStream();
|
||||
|
||||
public byte[] GZippedData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// CRC32 of uncompressed data
|
||||
/// </summary>
|
||||
public ulong CRC { get; set; } = 0;
|
||||
|
||||
public byte[] Buf { get; set; } = null;
|
||||
|
||||
public int BufSize { get; set; } = 0;
|
||||
|
||||
public long HeaderSize { get; set; }
|
||||
|
||||
public long TrailerSize { get; set; }
|
||||
|
||||
public long SeekSkipped { get; set; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfInputGZip(GsfInput source = null, bool raw = false, long uncompressedSize = -1)
|
||||
{
|
||||
Source = source;
|
||||
Raw = raw;
|
||||
UncompressedSize = uncompressedSize;
|
||||
|
||||
Exception tempErr = null;
|
||||
if (Source == null)
|
||||
Err = new Exception("Null source");
|
||||
else if (Raw && UncompressedSize < 0)
|
||||
Err = new Exception("Uncompressed size not set");
|
||||
else if (InitZip(ref tempErr) != false)
|
||||
Err = tempErr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a reference to <paramref name="source"/>.
|
||||
/// </summary>
|
||||
/// <param name="source">The underlying data source.</param>
|
||||
/// <param name="err">Place to store an Exception if anything goes wrong</param>
|
||||
/// <returns></returns>
|
||||
public static GsfInputGZip Create(GsfInput source, ref Exception err)
|
||||
{
|
||||
if (source == null)
|
||||
return null;
|
||||
|
||||
GsfInputGZip gzip = new GsfInputGZip(source: source)
|
||||
{
|
||||
Name = source.Name,
|
||||
};
|
||||
|
||||
return gzip;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfInputGZip()
|
||||
{
|
||||
Source = null;
|
||||
|
||||
if (Stream != null)
|
||||
Stream.inflateEnd();
|
||||
|
||||
Err = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override GsfInput DupImpl(ref Exception err)
|
||||
{
|
||||
GsfInput src_source_copy;
|
||||
if (Source != null)
|
||||
{
|
||||
src_source_copy = Source.Duplicate(ref err);
|
||||
if (err != null)
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
src_source_copy = null;
|
||||
}
|
||||
|
||||
GsfInputGZip dst = new GsfInputGZip(source: src_source_copy, raw: (Source as GsfInputGZip)?.Raw ?? false);
|
||||
|
||||
if (Err != null)
|
||||
{
|
||||
dst.Err = Err;
|
||||
}
|
||||
else if (dst.Err != null)
|
||||
{
|
||||
err = dst.Err;
|
||||
return null;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0)
|
||||
{
|
||||
if (optional_buffer == null)
|
||||
{
|
||||
if (BufSize < num_bytes)
|
||||
{
|
||||
BufSize = Math.Max(num_bytes, 256);
|
||||
Buf = new byte[BufSize];
|
||||
}
|
||||
|
||||
optional_buffer = Buf;
|
||||
}
|
||||
|
||||
Stream.next_out = optional_buffer;
|
||||
Stream.avail_out = num_bytes;
|
||||
while (Stream.avail_out != 0)
|
||||
{
|
||||
int zerr;
|
||||
if (Stream.avail_in == 0)
|
||||
{
|
||||
long remain = Source.Remaining();
|
||||
if (remain <= TrailerSize)
|
||||
{
|
||||
if (remain < TrailerSize || StopByteAdded)
|
||||
{
|
||||
Err = new Exception("Truncated source");
|
||||
return null;
|
||||
}
|
||||
|
||||
// zlib requires an extra byte.
|
||||
Stream.avail_in = 1;
|
||||
GZippedData = new byte[0];
|
||||
StopByteAdded = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
int n = (int)Math.Min(remain - TrailerSize, 4096);
|
||||
|
||||
GZippedData = Source.Read(n, null);
|
||||
if (GZippedData == null)
|
||||
{
|
||||
Err = new Exception("Failed to read from source");
|
||||
return null;
|
||||
}
|
||||
|
||||
Stream.avail_in = n;
|
||||
}
|
||||
|
||||
Stream.next_in = GZippedData;
|
||||
}
|
||||
|
||||
zerr = Stream.inflate(Z_NO_FLUSH);
|
||||
if (zerr != Z_OK)
|
||||
{
|
||||
if (zerr != Z_STREAM_END)
|
||||
return null;
|
||||
|
||||
// Premature end of stream.
|
||||
if (Stream.avail_out != 0)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Enable CRC32 calculation
|
||||
//CRC = crc32(CRC, optional_buffer, (uint)(Stream.next_out - optional_buffer));
|
||||
return optional_buffer;
|
||||
}
|
||||
|
||||
// Global flag -- we don't want one per stream.
|
||||
private static bool warned = false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence)
|
||||
{
|
||||
long pos = offset;
|
||||
|
||||
// Note, that pos has already been sanity checked.
|
||||
switch (whence)
|
||||
{
|
||||
case SeekOrigin.Begin: break;
|
||||
case SeekOrigin.Current: pos += CurrentOffset; break;
|
||||
case SeekOrigin.End: pos += Size; break;
|
||||
default: return true;
|
||||
}
|
||||
|
||||
if (pos < CurrentOffset)
|
||||
{
|
||||
if (Source.Seek(HeaderSize, SeekOrigin.Begin))
|
||||
return true;
|
||||
|
||||
// TODO: Enable CRC32 calculation
|
||||
//CRC = crc32(0L, Z_null, 0);
|
||||
Stream.avail_in = 0;
|
||||
if (Stream.inflateInit() != Z_OK)
|
||||
return true;
|
||||
|
||||
CurrentOffset = 0;
|
||||
}
|
||||
|
||||
if (SeekEmulate(pos))
|
||||
return true;
|
||||
|
||||
SeekSkipped += pos;
|
||||
if (!warned &&
|
||||
SeekSkipped != pos && // Don't warn for single seek.
|
||||
SeekSkipped >= 1000000)
|
||||
{
|
||||
warned = true;
|
||||
Console.Error.WriteLine("Seeking in gzipped streams is awfully slow.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private bool CheckHeader()
|
||||
{
|
||||
if (Raw)
|
||||
{
|
||||
HeaderSize = 0;
|
||||
TrailerSize = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] data;
|
||||
uint len;
|
||||
|
||||
// Check signature
|
||||
byte[] signature = { 0x1f, 0x8b };
|
||||
if ((data = Source.Read(2 + 1 + 1 + 6, null)) == null
|
||||
|| !(data[0] == signature[0] && data[1] == signature[1]))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// zlib constant
|
||||
int Z_DEFLATED = 8;
|
||||
|
||||
// Verify flags and compression type
|
||||
GZIP_HEADER_FLAGS flags = (GZIP_HEADER_FLAGS)data[3];
|
||||
if (data[2] != Z_DEFLATED)
|
||||
return true;
|
||||
|
||||
uint modutime = GSF_LE_GET_GUINT32(data, 4);
|
||||
if (modutime != 0)
|
||||
{
|
||||
DateTime modtime = DateTimeOffset.FromUnixTimeSeconds(modutime).DateTime;
|
||||
ModTime = modtime;
|
||||
}
|
||||
|
||||
// If we have the size, don't bother seeking to the end.
|
||||
if (UncompressedSize < 0)
|
||||
{
|
||||
// Get the uncompressed size
|
||||
if (Source.Seek(-4, SeekOrigin.End)
|
||||
|| (data = Source.Read(4, null)) == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// FIXME, but how? The size read here is modulo 2^32.
|
||||
UncompressedSize = GSF_LE_GET_GUINT32(data, 0);
|
||||
|
||||
if (UncompressedSize / 1000 > Source.Size)
|
||||
{
|
||||
Console.Error.WriteLine("Suspiciously well compressed file with better than 1000:1 ratio.\n"
|
||||
+ "It is probably truncated or corrupt");
|
||||
}
|
||||
}
|
||||
|
||||
if (Source.Seek(2 + 1 + 1 + 6, SeekOrigin.Begin))
|
||||
return true;
|
||||
|
||||
if (flags.HasFlag(GZIP_HEADER_FLAGS.GZIP_EXTRA_FIELD))
|
||||
{
|
||||
if ((data = Source.Read(2, null)) == null)
|
||||
return true;
|
||||
|
||||
len = GSF_LE_GET_GUINT16(data, 0);
|
||||
if (Source.Read((int)len, null) == null)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (flags.HasFlag(GZIP_HEADER_FLAGS.GZIP_ORIGINAL_NAME))
|
||||
{
|
||||
// Skip over the filename (which is in ISO 8859-1 encoding).
|
||||
do
|
||||
{
|
||||
if ((data = Source.Read(1, null)) == null)
|
||||
return true;
|
||||
} while (data[0] != 0);
|
||||
}
|
||||
|
||||
if (flags.HasFlag(GZIP_HEADER_FLAGS.GZIP_HAS_COMMENT))
|
||||
{
|
||||
// Skip over the comment (which is in ISO 8859-1 encoding).
|
||||
do
|
||||
{
|
||||
if ((data = Source.Read(1, null)) == null)
|
||||
return true;
|
||||
} while (data[0] != 0);
|
||||
}
|
||||
|
||||
if (flags.HasFlag(GZIP_HEADER_FLAGS.GZIP_HEADER_CRC) && (data = Source.Read(2, null)) == null)
|
||||
return true;
|
||||
|
||||
HeaderSize = Source.CurrentOffset;
|
||||
|
||||
// the last 8 bytes are the crc and size.
|
||||
TrailerSize = 8;
|
||||
}
|
||||
|
||||
Size = UncompressedSize;
|
||||
|
||||
if (Source.Remaining() < TrailerSize)
|
||||
return true; // No room for payload
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool InitZip(ref Exception err)
|
||||
{
|
||||
if (Z_OK != Stream.inflateInit(-15))
|
||||
{
|
||||
err = new Exception("Unable to initialize zlib");
|
||||
return true;
|
||||
}
|
||||
|
||||
long cur_pos = Source.CurrentOffset;
|
||||
if (Source.Seek(0, SeekOrigin.Begin))
|
||||
{
|
||||
err = new Exception("Failed to rewind source");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CheckHeader() != false)
|
||||
{
|
||||
err = new Exception("Invalid gzip header");
|
||||
if (Source.Seek(cur_pos, SeekOrigin.Begin))
|
||||
Console.Error.WriteLine("attempt to restore position failed ??");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
240
BurnOutSharp/External/libgsf/Input/GsfInputGio.cs
vendored
240
BurnOutSharp/External/libgsf/Input/GsfInputGio.cs
vendored
@@ -1,240 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-input-gio.c:
|
||||
*
|
||||
* Copyright (C) 2007 Dom Lachowicz <cinamod@hotmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using LibGSF.Output;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public class GsfInputGio : GsfInput
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public string File { get; set; } = null;
|
||||
|
||||
public Stream Stream { get; set; } = null;
|
||||
|
||||
public byte[] Buf { get; set; } = null;
|
||||
|
||||
public int BufSize { get; set; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfInputGio() { }
|
||||
|
||||
/// <param name="err">Place to store an Exception if anything goes wrong</param>
|
||||
/// <returns>A new GsfInputGio or null</returns>
|
||||
public static GsfInput Create(string file, ref Exception err)
|
||||
{
|
||||
if (file == null || !System.IO.File.Exists(file))
|
||||
return null;
|
||||
|
||||
long filesize;
|
||||
|
||||
Stream stream;
|
||||
try
|
||||
{
|
||||
stream = System.IO.File.OpenRead(file);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
err = ex;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (true)
|
||||
{
|
||||
// see https://bugzilla.gnome.org/show_bug.cgi?id=724970
|
||||
return MakeLocalCopy(file, stream);
|
||||
}
|
||||
|
||||
if (!stream.CanSeek)
|
||||
return MakeLocalCopy(file, stream);
|
||||
|
||||
{
|
||||
FileInfo info = new FileInfo(file);
|
||||
filesize = info.Length;
|
||||
}
|
||||
|
||||
GsfInputGio input = new GsfInputGio
|
||||
{
|
||||
Size = filesize,
|
||||
Stream = stream,
|
||||
File = file,
|
||||
Buf = null,
|
||||
BufSize = 0,
|
||||
};
|
||||
|
||||
input.SetNameFromFile(file);
|
||||
return input;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfInputGio()
|
||||
{
|
||||
Stream.Close();
|
||||
Stream = null;
|
||||
|
||||
File = null;
|
||||
|
||||
if (Buf != null)
|
||||
{
|
||||
Buf = null;
|
||||
BufSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override GsfInput DupImpl(ref Exception err)
|
||||
{
|
||||
if (File != null)
|
||||
return null;
|
||||
|
||||
return Create(File, ref err);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0)
|
||||
{
|
||||
if (Stream == null)
|
||||
return null;
|
||||
|
||||
int total_read = 0;
|
||||
|
||||
if (optional_buffer == null)
|
||||
{
|
||||
if (BufSize < num_bytes)
|
||||
{
|
||||
BufSize = num_bytes;
|
||||
Buf = new byte[BufSize];
|
||||
}
|
||||
|
||||
optional_buffer = Buf;
|
||||
}
|
||||
|
||||
while (total_read < num_bytes)
|
||||
{
|
||||
int try_to_read = Math.Min(int.MaxValue, num_bytes - total_read);
|
||||
int nread = Stream.Read(optional_buffer, total_read, try_to_read);
|
||||
if (nread > 0)
|
||||
{
|
||||
total_read += nread;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Getting zero means EOF which isn't supposed to
|
||||
// happen. Negative means error.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return optional_buffer;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence)
|
||||
{
|
||||
if (Stream == null)
|
||||
return true;
|
||||
|
||||
if (!Stream.CanSeek)
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
Stream.Seek(offset, whence);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private static GsfInput MakeLocalCopy(string file, Stream stream)
|
||||
{
|
||||
GsfInput copy;
|
||||
GsfOutput output = GsfOutputMemory.Create();
|
||||
while (true)
|
||||
{
|
||||
byte[] buf = new byte[4096];
|
||||
int nread = stream.Read(buf, 0, buf.Length);
|
||||
if (nread > 0)
|
||||
{
|
||||
if (!output.Write(nread, buf))
|
||||
{
|
||||
copy = null;
|
||||
output.Close();
|
||||
stream.Close();
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
else if (nread == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
copy = null;
|
||||
output.Close();
|
||||
stream.Close();
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
|
||||
copy = GsfInputMemory.Clone((output as GsfOutputMemory).Buffer, output.CurrentSize);
|
||||
|
||||
if (copy != null)
|
||||
{
|
||||
if (System.IO.File.Exists(file))
|
||||
{
|
||||
FileInfo info = new FileInfo(file);
|
||||
copy.Name = info.Name;
|
||||
}
|
||||
}
|
||||
|
||||
output.Close();
|
||||
stream.Close();
|
||||
copy.SetNameFromFile(file);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
187
BurnOutSharp/External/libgsf/Input/GsfInputHTTP.cs
vendored
187
BurnOutSharp/External/libgsf/Input/GsfInputHTTP.cs
vendored
@@ -1,187 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-input-http.c: retrieves input via HTTP
|
||||
*
|
||||
* Copyright (C) 2006 Michael Lawrence (lawremi@iastate.edu)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using LibGSF.Output;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public class GsfInputHTTP : GsfInput
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public string Url { get; private set; } = null;
|
||||
|
||||
public string ContentType { get; private set; } = null;
|
||||
|
||||
public Stream ResponseData { get; private set; } = null;
|
||||
|
||||
public byte[] Buffer { get; private set; } = null;
|
||||
|
||||
public int BufferSize { get; private set; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfInputHTTP() { }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="url">A string containing the URL to retrieve</param>
|
||||
/// <param name="error">Holds any errors encountered when establishing the HTTP connection</param>
|
||||
/// <returns>An open HTTP connection, ready for reading.</returns>
|
||||
public static GsfInput Create(string url, ref Exception error)
|
||||
{
|
||||
if (url == null)
|
||||
return null;
|
||||
|
||||
WebRequest request = WebRequest.Create(url);
|
||||
WebResponse response = request.GetResponse();
|
||||
Stream ctx = response.GetResponseStream();
|
||||
|
||||
string content_type = response.ContentType;
|
||||
|
||||
// Always make a local copy
|
||||
// see https://bugzilla.gnome.org/show_bug.cgi?id=724970
|
||||
GsfInput input = MakeLocalCopy(ctx);
|
||||
if (input != null)
|
||||
{
|
||||
input.Name = url;
|
||||
return input;
|
||||
}
|
||||
|
||||
GsfInputHTTP obj = new GsfInputHTTP
|
||||
{
|
||||
Url = url,
|
||||
ContentType = content_type,
|
||||
Size = ctx.Length,
|
||||
ResponseData = ctx,
|
||||
};
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
~GsfInputHTTP()
|
||||
{
|
||||
Url = null;
|
||||
ContentType = null;
|
||||
|
||||
if (ResponseData != null)
|
||||
{
|
||||
ResponseData.Close();
|
||||
ResponseData = null;
|
||||
}
|
||||
|
||||
Buffer = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override GsfInput DupImpl(ref Exception err) => Create(Url, ref err);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0)
|
||||
{
|
||||
int nread;
|
||||
int total_read;
|
||||
|
||||
if (optional_buffer == null)
|
||||
{
|
||||
if (BufferSize < num_bytes)
|
||||
{
|
||||
BufferSize = num_bytes;
|
||||
Buffer = new byte[BufferSize];
|
||||
}
|
||||
|
||||
optional_buffer = Buffer;
|
||||
}
|
||||
|
||||
int ptr = 0;
|
||||
for (total_read = 0; total_read < num_bytes; total_read += nread)
|
||||
{
|
||||
nread = ResponseData.Read(optional_buffer, ptr, num_bytes - total_read);
|
||||
if (nread <= 0)
|
||||
return null;
|
||||
|
||||
ptr += nread;
|
||||
}
|
||||
|
||||
return optional_buffer;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence) => false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private static GsfInput MakeLocalCopy(Stream ctx)
|
||||
{
|
||||
GsfOutputMemory output = GsfOutputMemory.Create();
|
||||
|
||||
GsfInput copy;
|
||||
while (true)
|
||||
{
|
||||
byte[] buf = new byte[4096];
|
||||
int nread;
|
||||
|
||||
nread = ctx.Read(buf, 0, buf.Length);
|
||||
|
||||
if (nread > 0)
|
||||
{
|
||||
if (!output.Write(nread, buf))
|
||||
{
|
||||
copy = null;
|
||||
output.Close();
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
else if (nread == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
copy = null;
|
||||
output.Close();
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
|
||||
copy = GsfInputMemory.Clone(output.Buffer, output.Capacity);
|
||||
output.Close();
|
||||
return copy;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-input-iochannel.c: GIOChannel based input
|
||||
*
|
||||
* Copyright (C) 2003-2006 Rodrigo Moya (rodrigo@gnome-db.org)
|
||||
* Copyright (C) 2003-2006 Dom Lachowicz (cinamod@hotmail.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public partial class GsfInputMemory
|
||||
{
|
||||
// TODO: Enable once GIOChannel is converted
|
||||
|
||||
///// <returns>A new GsfInputMemory or null.</returns>
|
||||
//public static GsfInputMemory CreateFromIoChannel(GIOChannel channel, ref Exception err)
|
||||
//{
|
||||
// if (channel == null)
|
||||
// return null;
|
||||
|
||||
// if (G_IO_STATUS_NORMAL != channel.ReadToEnd(out byte[] buf, out int len, err))
|
||||
// return null;
|
||||
|
||||
// return Create(buf, len, true);
|
||||
//}
|
||||
}
|
||||
}
|
||||
149
BurnOutSharp/External/libgsf/Input/GsfInputMemory.cs
vendored
149
BurnOutSharp/External/libgsf/Input/GsfInputMemory.cs
vendored
@@ -1,149 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-input-memory.c:
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public partial class GsfInputMemory : GsfInput
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public GsfSharedMemory Shared { get; set; } = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="buf">The input bytes</param>
|
||||
/// <param name="length">The length of <paramref name="buf"/></param>
|
||||
/// <param name="needs_free">Whether you want this memory to be free'd at object destruction</param>
|
||||
/// <returns>A new GsfInputMemory</returns>
|
||||
public static GsfInputMemory Create(byte[] buf, long length, bool needs_free)
|
||||
{
|
||||
GsfInputMemory mem = new GsfInputMemory();
|
||||
mem.Shared = GsfSharedMemory.Create(buf, length, needs_free);
|
||||
mem.Size = length;
|
||||
return mem;
|
||||
}
|
||||
|
||||
/// <param name="buf">The input bytes</param>
|
||||
/// <param name="length">The length of <paramref name="buf"/></param>
|
||||
/// <returns>A new GsfInputMemory</returns>
|
||||
public static GsfInputMemory Clone(byte[] buf, long length)
|
||||
{
|
||||
if (buf == null || length < 0)
|
||||
return null;
|
||||
|
||||
GsfInputMemory mem = new GsfInputMemory();
|
||||
byte[] cpy = new byte[Math.Max(1, length)];
|
||||
if (buf.Length > 0)
|
||||
Array.Copy(buf, cpy, length);
|
||||
|
||||
mem.Shared = GsfSharedMemory.Create(cpy, length, true);
|
||||
mem.Size = length;
|
||||
return mem;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override GsfInput DupImpl(ref Exception err)
|
||||
{
|
||||
return new GsfInputMemory
|
||||
{
|
||||
Shared = Shared,
|
||||
Size = Shared.Size,
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0)
|
||||
{
|
||||
byte[] src = Shared.Buf;
|
||||
if (src == null)
|
||||
return null;
|
||||
|
||||
if (optional_buffer != null)
|
||||
{
|
||||
Array.Copy(src, CurrentOffset, optional_buffer, bufferPtr, num_bytes);
|
||||
return optional_buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ReadOnlySpan<byte>(src, (int)CurrentOffset, num_bytes).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence) => false;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class GsfInputMemoryMap : GsfInputMemory
|
||||
{
|
||||
private const int PROT_READ = 0x1;
|
||||
|
||||
/// <param name="filename">The file on disk that you want to mmap</param>
|
||||
/// <param name="err">An Exception</param>
|
||||
/// <returns>A new GsfInputMemory</returns>
|
||||
public GsfInputMemoryMap Create(string filename, ref Exception err)
|
||||
{
|
||||
FileStream fd;
|
||||
try
|
||||
{
|
||||
fd = File.OpenRead(filename);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
err = ex;
|
||||
return null;
|
||||
}
|
||||
|
||||
FileInfo st = new FileInfo(filename);
|
||||
if (!st.Attributes.HasFlag(FileAttributes.Normal))
|
||||
{
|
||||
err = new Exception($"{filename}: Is not a regular file");
|
||||
fd.Close();
|
||||
return null;
|
||||
}
|
||||
|
||||
long size = fd.Length;
|
||||
byte[] buf = new byte[size];
|
||||
fd.Read(buf, 0, (int)size);
|
||||
|
||||
GsfInputMemoryMap mem = new GsfInputMemoryMap
|
||||
{
|
||||
Shared = GsfSharedMemory.CreateMemoryMapped(buf, size),
|
||||
Size = size,
|
||||
Name = filename,
|
||||
ModTime = st.LastWriteTime,
|
||||
};
|
||||
|
||||
fd.Close();
|
||||
|
||||
return mem;
|
||||
}
|
||||
}
|
||||
}
|
||||
111
BurnOutSharp/External/libgsf/Input/GsfInputProxy.cs
vendored
111
BurnOutSharp/External/libgsf/Input/GsfInputProxy.cs
vendored
@@ -1,111 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-input-proxy.c: proxy object (with its own current position)
|
||||
*
|
||||
* Copyright (C) 2004-2006 Morten Welinder (terra@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public class GsfInputProxy : GsfInput
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public GsfInput Source { get; set; } = null;
|
||||
|
||||
public long Offset { get; set; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// This creates a new proxy to a section of the given source. The new
|
||||
/// object will have its own current position, but any operation on it
|
||||
/// can change the source's position.
|
||||
///
|
||||
/// If a proxy to a proxy is created, the intermediate proxy is short-
|
||||
/// circuited.
|
||||
///
|
||||
/// This function will ref the source.
|
||||
/// </summary>
|
||||
/// <param name="source">The underlying data source.</param>
|
||||
/// <param name="offset">Offset into source for start of section.</param>
|
||||
/// <param name="size">Length of section.</param>
|
||||
/// <returns>A new input object.</returns>
|
||||
public static GsfInputProxy Create(GsfInput source, long offset, long size)
|
||||
{
|
||||
if (source == null)
|
||||
return null;
|
||||
if (offset < 0)
|
||||
return null;
|
||||
|
||||
long source_size = source.Size;
|
||||
if (offset > source_size)
|
||||
return null;
|
||||
if (size > source_size - offset)
|
||||
return null;
|
||||
|
||||
GsfInputProxy proxy = new GsfInputProxy
|
||||
{
|
||||
Offset = offset,
|
||||
Size = size,
|
||||
Name = source.Name,
|
||||
};
|
||||
|
||||
// Short-circuit multiple proxies.
|
||||
if (source is GsfInputProxy proxy_source)
|
||||
{
|
||||
proxy.Offset += proxy_source.Offset;
|
||||
source = proxy_source.Source;
|
||||
}
|
||||
|
||||
proxy.Source = source;
|
||||
return proxy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This creates a new proxy to the entire, given input source. See
|
||||
/// Create for details.
|
||||
/// </summary>
|
||||
/// <param name="source">The underlying data source.</param>
|
||||
/// <returns>A new input object.</returns>
|
||||
public static GsfInput Create(GsfInput source) => Create(source, 0, source.Size);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override GsfInput DupImpl(ref Exception err) => Create(this);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0)
|
||||
{
|
||||
// Seek to our position in the source.
|
||||
if (Source.Seek(Offset + CurrentOffset, SeekOrigin.Begin))
|
||||
return null;
|
||||
|
||||
// Read the data.
|
||||
return Source.Read(num_bytes, optional_buffer, bufferPtr);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence) => false;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
227
BurnOutSharp/External/libgsf/Input/GsfInputStdio.cs
vendored
227
BurnOutSharp/External/libgsf/Input/GsfInputStdio.cs
vendored
@@ -1,227 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-input-stdio.c: stdio based input
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using LibGSF.Output;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public class GsfInputStdio : GsfInput
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public FileStream File { get; set; } = null;
|
||||
|
||||
public string Filename { get; set; } = null;
|
||||
|
||||
public byte[] Buf { get; set; } = null;
|
||||
|
||||
public int BufSize { get; set; } = 0;
|
||||
|
||||
public bool KeepOpen { get; set; } = false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfInputStdio()
|
||||
{
|
||||
if (File != null && !KeepOpen)
|
||||
File.Close();
|
||||
}
|
||||
|
||||
public static GsfInput MakeLocalCopy(FileStream stream, string filename, ref Exception err)
|
||||
{
|
||||
GsfOutputMemory output = GsfOutputMemory.Create();
|
||||
|
||||
while (true)
|
||||
{
|
||||
byte[] buf = new byte[4096];
|
||||
int nread = stream.Read(buf, 0, buf.Length);
|
||||
|
||||
if (nread > 0)
|
||||
{
|
||||
if (!output.Write(nread, buf))
|
||||
{
|
||||
err = new Exception($"{(filename != null ? filename : "?")}: not a regular file");
|
||||
output.Close();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else if (nread == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
err = new Exception($"{(filename != null ? filename : "?")}: not a regular file");
|
||||
output.Close();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
GsfInput copy = GsfInputMemory.Clone(output.Buffer, output.Capacity);
|
||||
|
||||
output.Close();
|
||||
|
||||
if (filename != null)
|
||||
copy.SetNameFromFilename(filename);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
/// <param name="filename">In UTF-8.</param>
|
||||
/// <param name="err">Place to store an Exception if anything goes wrong</param>
|
||||
/// <returns>A new file or null.</returns>
|
||||
public static GsfInput Create(string filename, ref Exception err)
|
||||
{
|
||||
if (filename == null)
|
||||
return null;
|
||||
|
||||
FileStream file;
|
||||
try
|
||||
{
|
||||
file = System.IO.File.OpenRead(filename);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
err = ex;
|
||||
return null;
|
||||
}
|
||||
|
||||
FileInfo fstat = new FileInfo(filename);
|
||||
if (!fstat.Attributes.HasFlag(FileAttributes.Normal))
|
||||
{
|
||||
GsfInput res = MakeLocalCopy(file, filename, ref err);
|
||||
file.Close();
|
||||
return res;
|
||||
}
|
||||
|
||||
long size = fstat.Length;
|
||||
GsfInputStdio input = new GsfInputStdio
|
||||
{
|
||||
File = file,
|
||||
Filename = filename,
|
||||
Buf = null,
|
||||
BufSize = 0,
|
||||
KeepOpen = false,
|
||||
Size = size,
|
||||
ModTime = fstat.LastWriteTime,
|
||||
};
|
||||
|
||||
input.SetNameFromFilename(filename);
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assumes ownership of <paramref name="file"/> when succeeding. If <paramref name="keep_open"/> is true,
|
||||
/// ownership reverts to caller when the GsfInput is closed.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename corresponding to <paramref name="file"/>.</param>
|
||||
/// <param name="file">An existing stdio <see cref="FileStream"/></param>
|
||||
/// <param name="keep_open">Should <paramref name="file"/> be closed when the wrapper is closed</param>
|
||||
/// <returns>
|
||||
/// A new GsfInput wrapper for <paramref name="file"/>. Note that if the file is not
|
||||
/// seekable, this function will make a local copy of the entire file.
|
||||
/// </returns>
|
||||
public static GsfInput Create(string filename, FileStream file, bool keep_open)
|
||||
{
|
||||
if (filename == null)
|
||||
return null;
|
||||
if (file == null)
|
||||
return null;
|
||||
|
||||
FileInfo st = new FileInfo(filename);
|
||||
if (!st.Attributes.HasFlag(FileAttributes.Normal))
|
||||
{
|
||||
Exception err = null;
|
||||
return MakeLocalCopy(file, filename, ref err);
|
||||
}
|
||||
|
||||
GsfInputStdio stdio = new GsfInputStdio
|
||||
{
|
||||
File = file,
|
||||
KeepOpen = keep_open,
|
||||
Filename = filename,
|
||||
Size = st.Length,
|
||||
};
|
||||
|
||||
stdio.SetNameFromFilename(filename);
|
||||
return stdio;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override GsfInput DupImpl(ref Exception err) => Create(Filename, ref err);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0)
|
||||
{
|
||||
if (File == null)
|
||||
return null;
|
||||
|
||||
if (optional_buffer == null)
|
||||
{
|
||||
if (BufSize < num_bytes)
|
||||
{
|
||||
BufSize = num_bytes;
|
||||
Buf = new byte[BufSize];
|
||||
}
|
||||
|
||||
optional_buffer = Buf;
|
||||
}
|
||||
|
||||
int total_read = 0;
|
||||
while (total_read < num_bytes)
|
||||
{
|
||||
int nread = File.Read(optional_buffer, total_read, num_bytes - total_read);
|
||||
total_read += nread;
|
||||
if (total_read < num_bytes && File.Position >= File.Length)
|
||||
return null;
|
||||
}
|
||||
|
||||
return optional_buffer;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence)
|
||||
{
|
||||
if (File == null)
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
long pos = File.Seek(offset, whence);
|
||||
return pos != offset;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-input-textline.c: textline based input
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public class GsfInputTextline : GsfInput
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public GsfInput Source { get; set; } = null;
|
||||
|
||||
public byte[] Remainder { get; set; } = null;
|
||||
|
||||
public int RemainderSize { get; set; } = 0;
|
||||
|
||||
public int MaxLineSize { get; set; } = 512; // An initial guess
|
||||
|
||||
public byte[] Buf { get; set; } = null;
|
||||
|
||||
public int BufSize { get; set; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <param name="source">In some combination of ascii and UTF-8</param>
|
||||
/// <returns>A new file</returns>
|
||||
/// <remarks>This adds a reference to @source.</remarks>
|
||||
public static GsfInputTextline Create(GsfInput source)
|
||||
{
|
||||
if (source == null)
|
||||
return null;
|
||||
|
||||
return new GsfInputTextline
|
||||
{
|
||||
Source = source,
|
||||
Buf = null,
|
||||
BufSize = 0,
|
||||
Size = source.Size,
|
||||
Name = source.Name,
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override GsfInput DupImpl(ref Exception err)
|
||||
{
|
||||
return new GsfInputTextline()
|
||||
{
|
||||
Source = this.Source,
|
||||
Size = this.Size,
|
||||
Name = this.Name,
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0)
|
||||
{
|
||||
Remainder = null;
|
||||
byte[] res = Source.Read(num_bytes, optional_buffer);
|
||||
CurrentOffset = Source.CurrentOffset;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence)
|
||||
{
|
||||
Remainder = null;
|
||||
bool res = Source.Seek(offset, whence);
|
||||
CurrentOffset = Source.CurrentOffset;
|
||||
return res;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
/// <summary>
|
||||
/// A utility routine to read things line by line from the underlying source.
|
||||
/// Trailing newlines and carriage returns are stripped, and the resultant buffer
|
||||
/// can be edited.
|
||||
/// </summary>
|
||||
/// <returns>The string read, or null on eof.</returns>
|
||||
internal string GetStringASCII() => GetStringUTF8();
|
||||
|
||||
/// <summary>
|
||||
/// A utility routine to read things line by line from the underlying source.
|
||||
/// Trailing newlines and carriage returns are stripped, and the resultant buffer
|
||||
/// can be edited.
|
||||
/// </summary>
|
||||
/// <returns>The string read, or null on eof.</returns>
|
||||
internal string GetStringUTF8()
|
||||
{
|
||||
int len, ptr, end;
|
||||
int count = 0;
|
||||
while (true)
|
||||
{
|
||||
if (Remainder == null || RemainderSize == 0)
|
||||
{
|
||||
long remain = Source.Remaining();
|
||||
len = (int)Math.Min(remain, MaxLineSize);
|
||||
|
||||
Remainder = Source.Read(len, null);
|
||||
if (Remainder == null)
|
||||
return null;
|
||||
|
||||
RemainderSize = len;
|
||||
}
|
||||
|
||||
ptr = 0; // Remainder[0]
|
||||
end = ptr + RemainderSize;
|
||||
|
||||
for (; ptr < end; ptr++)
|
||||
{
|
||||
if (Remainder[ptr] == '\n' || Remainder[ptr] == '\r')
|
||||
break;
|
||||
}
|
||||
|
||||
// Copy the remains into the buffer, grow it if necessary
|
||||
len = ptr;
|
||||
if (count + len >= BufSize)
|
||||
{
|
||||
BufSize = len;
|
||||
Buf = new byte[BufSize + 1];
|
||||
}
|
||||
|
||||
if (Buf == null)
|
||||
return null;
|
||||
|
||||
Array.Copy(Remainder, 0, Buf, count, len);
|
||||
count += len;
|
||||
|
||||
if (ptr < end)
|
||||
{
|
||||
char last = (char)Remainder[ptr];
|
||||
|
||||
// Eat the trailing eol marker: \n, \r\n, or \r.
|
||||
ptr++;
|
||||
if (ptr >= end && last == '\r')
|
||||
{
|
||||
// Be extra careful, the CR is at the bound
|
||||
if (Source.Remaining() > 0)
|
||||
{
|
||||
if (Source.Read(1, Remainder, ptr) == null)
|
||||
return null;
|
||||
|
||||
RemainderSize = 1;
|
||||
end = ptr + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = end = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ptr != -1 && last == '\r' && Remainder[ptr] == '\n')
|
||||
ptr++;
|
||||
|
||||
break;
|
||||
}
|
||||
else if (Source.Remaining() <= 0)
|
||||
{
|
||||
ptr = end = -1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
Remainder = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (ptr == -1)
|
||||
{
|
||||
Remainder = null;
|
||||
RemainderSize = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
RemainderSize = end - ptr;
|
||||
}
|
||||
|
||||
CurrentOffset = Source.CurrentOffset - (Remainder != null ? RemainderSize : 0);
|
||||
Buf[count] = 0x00;
|
||||
|
||||
return Encoding.UTF8.GetString(Buf);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-structured_blob.c : Utility storage to blob in/out a tree of data
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using LibGSF.Output;
|
||||
|
||||
namespace LibGSF.Input
|
||||
{
|
||||
public class GsfStructuredBlob : GsfInfile
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public GsfSharedMemory Data { get; set; } = null;
|
||||
|
||||
public GsfStructuredBlob[] Children { get; set; } = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfStructuredBlob() { }
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfStructuredBlob()
|
||||
{
|
||||
if (Data != null)
|
||||
Data = null;
|
||||
|
||||
if (Children != null)
|
||||
Children = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override GsfInput DupImpl(ref Exception err)
|
||||
{
|
||||
GsfStructuredBlob dst = new GsfStructuredBlob();
|
||||
|
||||
if (Data != null)
|
||||
dst.Data = Data;
|
||||
|
||||
if (Children != null)
|
||||
{
|
||||
dst.Children = new GsfStructuredBlob[Children.Length];
|
||||
Array.Copy(Children, dst.Children, Children.Length);
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0)
|
||||
{
|
||||
byte[] src = Data.Buf;
|
||||
if (src == null)
|
||||
return null;
|
||||
|
||||
if (optional_buffer == null)
|
||||
optional_buffer = new byte[num_bytes];
|
||||
|
||||
Array.Copy(src, CurrentOffset, optional_buffer, 0, num_bytes);
|
||||
return optional_buffer;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence) => false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int NumChildren() => Children != null ? Children.Length : -1;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string NameByIndex(int i)
|
||||
{
|
||||
if (Children == null)
|
||||
return null;
|
||||
|
||||
if (i < 0 || i >= Children.Length)
|
||||
return null;
|
||||
|
||||
return Children[i].Name;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfInput ChildByIndex(int i, ref Exception error)
|
||||
{
|
||||
if (Children == null)
|
||||
return null;
|
||||
|
||||
if (i < 0 || i >= Children.Length)
|
||||
return null;
|
||||
|
||||
return Children[i];
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfInput ChildByName(string name, ref Exception error)
|
||||
{
|
||||
if (Children == null)
|
||||
return null;
|
||||
|
||||
for (int i = 0; i < Children.Length; i++)
|
||||
{
|
||||
GsfInput child = Children[i];
|
||||
if (child != null && child.Name == name)
|
||||
return child.Duplicate(ref error);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a tree of binary blobs with unknown content from a GsfInput or
|
||||
/// #GsfInfile and store it in a newly created #GsfStructuredBlob.
|
||||
/// </summary>
|
||||
/// <param name="input">An input (potentially a GsfInfile) holding the blob</param>
|
||||
/// <returns>A new GsfStructuredBlob object which the caller is responsible for.</returns>
|
||||
public static GsfStructuredBlob Read(GsfInput input)
|
||||
{
|
||||
if (input == null)
|
||||
return null;
|
||||
|
||||
GsfStructuredBlob blob = new GsfStructuredBlob();
|
||||
long content_size = input.Remaining();
|
||||
if (content_size > 0)
|
||||
{
|
||||
byte[] buf = new byte[content_size];
|
||||
input.Read((int)content_size, buf);
|
||||
blob.Data = GsfSharedMemory.Create(buf, content_size, true);
|
||||
}
|
||||
|
||||
blob.Name = input.Name;
|
||||
|
||||
if (input is GsfInfile infile)
|
||||
{
|
||||
int i = infile.NumChildren();
|
||||
if (i > 0)
|
||||
{
|
||||
Exception err = null;
|
||||
blob.Children = new GsfStructuredBlob[i];
|
||||
while (i-- > 0)
|
||||
{
|
||||
GsfInput child = infile.ChildByIndex(i, ref err);
|
||||
GsfStructuredBlob child_blob = null;
|
||||
if (child != null)
|
||||
child_blob = Read(child);
|
||||
|
||||
blob.Children[i] = child_blob;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return blob;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps structured blob @blob onto the <paramref name="container"/>. Will fail if the output is
|
||||
/// not an Outfile and blob has multiple streams.
|
||||
/// </summary>
|
||||
/// <returns>True on success.</returns>
|
||||
public bool Write(GsfOutfile container)
|
||||
{
|
||||
if (container == null)
|
||||
return false;
|
||||
|
||||
bool has_kids = (Children != null && Children.Length > 0);
|
||||
GsfOutput output = container.NewChild(Name, has_kids);
|
||||
if (has_kids)
|
||||
{
|
||||
for (int i = 0; i < Children.Length; i++)
|
||||
{
|
||||
GsfStructuredBlob child_blob = Children[i];
|
||||
if (!child_blob.Write(output as GsfOutfile))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Data != null)
|
||||
output.Write((int)Data.Size, Data.Buf);
|
||||
|
||||
output.Close();
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-outfile.c :
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
public abstract class GsfOutfile : GsfOutput
|
||||
{
|
||||
#region Virtual Functions
|
||||
|
||||
/// <param name="name">The name of the new child to create</param>
|
||||
/// <param name="is_dir">true to create a directory, false to create a plain file</param>
|
||||
/// <returns>A newly created child</returns>
|
||||
public virtual GsfOutput NewChild(string name, bool is_dir) => null;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,878 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-outfile-msole.c:
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Outc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using LibGSF.Input;
|
||||
using static LibGSF.GsfMSOleImpl;
|
||||
using static LibGSF.GsfUtils;
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
public class GsfOutfileMSOleBlock
|
||||
{
|
||||
public int Shift { get; set; }
|
||||
|
||||
public int Size { get; set; }
|
||||
}
|
||||
|
||||
public class GsfOutfileMSOle : GsfOutfile
|
||||
{
|
||||
#region Constants
|
||||
|
||||
// The most common values
|
||||
private const int OLE_DEFAULT_THRESHOLD = 0x1000;
|
||||
private const int OLE_DEFAULT_SB_SHIFT = 6;
|
||||
private const int OLE_DEFAULT_BB_SHIFT = 9;
|
||||
private const long OLE_DEFAULT_BB_SIZE = (1u << OLE_DEFAULT_BB_SHIFT);
|
||||
private const long OLE_DEFAULT_SB_SIZE = (1u << OLE_DEFAULT_SB_SHIFT);
|
||||
|
||||
// Globals to support variable OLE sector size.
|
||||
|
||||
/// <summary>
|
||||
/// 512 and 4096 bytes are the only known values for sector size on
|
||||
/// Win2k/XP platforms. Attempts to create OLE files on Win2k/XP with
|
||||
/// other values using StgCreateStorageEx() fail with invalid parameter.
|
||||
/// This code has been tested with 128,256,512,4096,8192 sizes for
|
||||
/// libgsf read/write. Interoperability with MS OLE32.DLL has been
|
||||
/// tested with 512 and 4096 block size for filesizes up to 2GB.
|
||||
/// </summary>
|
||||
private const int ZERO_PAD_BUF_SIZE = 4096;
|
||||
|
||||
private const uint CHUNK_SIZE = 1024u;
|
||||
|
||||
private static readonly byte[] default_header =
|
||||
{
|
||||
/* 0x00 */ 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1,
|
||||
/* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 0x18 */ 0x3e, 0x00, 0x03, 0x00, 0xfe, 0xff, 0x09, 0x00,
|
||||
/* 0x20 */ 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x30 */ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x38 */ 0x00, 0x10, 0x00, 0x00 /* 0x3c-0x4b: filled on close */
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region Enums
|
||||
|
||||
public enum MSOleOutfileType
|
||||
{
|
||||
MSOLE_DIR,
|
||||
MSOLE_SMALL_BLOCK,
|
||||
MSOLE_BIG_BLOCK
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public GsfOutfile Parent { get; set; }
|
||||
|
||||
public GsfOutput Sink { get; set; } = null;
|
||||
|
||||
public GsfOutfileMSOle Root { get; set; } = null;
|
||||
|
||||
public GsfMSOleSortingKey Key { get; set; }
|
||||
|
||||
public MSOleOutfileType Type { get; set; } = MSOleOutfileType.MSOLE_DIR;
|
||||
|
||||
public int FirstBlock { get; set; }
|
||||
|
||||
public int Blocks { get; set; }
|
||||
|
||||
public int ChildIndex { get; set; }
|
||||
|
||||
public GsfOutfileMSOleBlock BigBlock { get; set; }
|
||||
|
||||
public GsfOutfileMSOleBlock SmallBlock { get; set; }
|
||||
|
||||
#region Union (Content)
|
||||
|
||||
#region Struct (Dir)
|
||||
|
||||
public List<GsfOutfileMSOle> Content_Dir_Children { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Only valid for the root, ordered
|
||||
/// </summary>
|
||||
public List<GsfOutfileMSOle> Content_Dir_RootOrder { get; set; } = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Struct (SmallBlock)
|
||||
|
||||
public byte[] Content_SmallBlock_Buf { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Struct (BigBlock)
|
||||
|
||||
/// <summary>
|
||||
/// In bytes
|
||||
/// </summary>
|
||||
public int Content_BigBlock_StartOffset { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 16 byte GUID used by some apps
|
||||
/// </summary>
|
||||
public byte[] ClassID { get; set; } = new byte[16];
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfOutfileMSOle() => MakeSortingName();
|
||||
|
||||
/// <summary>
|
||||
/// Creates the root directory of an MS OLE file and manages the addition of
|
||||
/// children.
|
||||
/// </summary>
|
||||
/// <param name="sink">A GsfOutput to hold the OLE2 file.</param>
|
||||
/// <param name="bb_size">Size of large blocks.</param>
|
||||
/// <param name="sb_size">Size of small blocks.</param>
|
||||
/// <returns>The new ole file handler.</returns>
|
||||
/// <remarks>This adds a reference to <paramref name="sink"/>.</remarks>
|
||||
public static GsfOutfileMSOle CreateFull(GsfOutput sink, int bb_size, int sb_size)
|
||||
{
|
||||
if (sink == null)
|
||||
return null;
|
||||
if (sb_size != (1u << ComputeShift(sb_size)))
|
||||
return null;
|
||||
if (bb_size != (1u << ComputeShift(bb_size)))
|
||||
return null;
|
||||
if (sb_size > bb_size)
|
||||
return null;
|
||||
|
||||
GsfOutfileMSOle ole = new GsfOutfileMSOle
|
||||
{
|
||||
Sink = sink,
|
||||
SmallBlock = new GsfOutfileMSOleBlock
|
||||
{
|
||||
Size = sb_size,
|
||||
},
|
||||
BigBlock = new GsfOutfileMSOleBlock
|
||||
{
|
||||
Size = bb_size,
|
||||
},
|
||||
Container = null,
|
||||
Name = sink.Name,
|
||||
Type = MSOleOutfileType.MSOLE_DIR,
|
||||
Content_Dir_RootOrder = new List<GsfOutfileMSOle>(),
|
||||
};
|
||||
|
||||
ole.RegisterChild(ole);
|
||||
|
||||
// Build the header
|
||||
byte[] buf = Enumerable.Repeat<byte>(0xFF, MSOleHeader.OLE_HEADER_SIZE).ToArray();
|
||||
|
||||
MSOleHeader header = MSOleHeader.CreateDefault();
|
||||
header.BB_SHIFT = (ushort)ole.BigBlock.Shift;
|
||||
header.SB_SHIFT = (ushort)ole.SmallBlock.Shift;
|
||||
|
||||
// 4k sector OLE files seen in the wild have version 4
|
||||
if (ole.BigBlock.Size == 4096)
|
||||
header.MAJOR_VER = 4;
|
||||
|
||||
header.Write(buf, 0);
|
||||
sink.Write(MSOleHeader.OLE_HEADER_SIZE, buf);
|
||||
|
||||
// Header must be padded out to BigBlock.Size with zeros
|
||||
ole.PadZero();
|
||||
|
||||
return ole;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the root directory of an MS OLE file and manages the addition of
|
||||
/// children.
|
||||
/// </summary>
|
||||
/// <param name="sink">A GsfOutput to hold the OLE2 file</param>
|
||||
/// <returns>The new ole file handler.</returns>
|
||||
/// <remarks>This adds a reference to <paramref name="sink"/>.</remarks>
|
||||
public static GsfOutfileMSOle Create(GsfOutput sink) => CreateFull(sink, (int)OLE_DEFAULT_BB_SIZE, (int)OLE_DEFAULT_SB_SIZE);
|
||||
|
||||
~GsfOutfileMSOle()
|
||||
{
|
||||
Key = null;
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case MSOleOutfileType.MSOLE_DIR:
|
||||
Content_Dir_Children.Clear();
|
||||
Content_Dir_Children = null;
|
||||
if (Content_Dir_RootOrder != null)
|
||||
Console.Error.WriteLine("Finalizing a MSOle Outfile without closing it.");
|
||||
|
||||
break;
|
||||
|
||||
case MSOleOutfileType.MSOLE_SMALL_BLOCK:
|
||||
Content_SmallBlock_Buf = null;
|
||||
break;
|
||||
|
||||
case MSOleOutfileType.MSOLE_BIG_BLOCK:
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception("This should not be reached");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (!IsClosed)
|
||||
Close();
|
||||
|
||||
Sink = null;
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence)
|
||||
{
|
||||
switch (whence)
|
||||
{
|
||||
case SeekOrigin.Begin: break;
|
||||
case SeekOrigin.Current: offset += CurrentOffset; break;
|
||||
case SeekOrigin.End: offset += CurrentSize; break;
|
||||
default: throw new Exception("This should not be reached");
|
||||
}
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case MSOleOutfileType.MSOLE_DIR:
|
||||
if (offset != 0)
|
||||
{
|
||||
Console.Error.WriteLine("Attempt to seek a directory");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
case MSOleOutfileType.MSOLE_SMALL_BLOCK:
|
||||
// It is ok to seek past the big block threshold
|
||||
// we don't convert until they _write_ something
|
||||
return true;
|
||||
|
||||
case MSOleOutfileType.MSOLE_BIG_BLOCK:
|
||||
return Sink.Seek(Content_BigBlock_StartOffset + offset, SeekOrigin.Begin);
|
||||
|
||||
default:
|
||||
throw new Exception("This should not be reached");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool CloseImpl()
|
||||
{
|
||||
if (Container == null)
|
||||
{
|
||||
// The root dir
|
||||
bool ok = WriteDirectory();
|
||||
HoistError();
|
||||
if (!Sink.Close())
|
||||
ok = false;
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
if (Type == MSOleOutfileType.MSOLE_BIG_BLOCK)
|
||||
{
|
||||
Seek(0, SeekOrigin.End);
|
||||
PadZero();
|
||||
Blocks = CurrentBlock() - FirstBlock;
|
||||
return Unwrap(Sink);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool WriteImpl(int num_bytes, byte[] data)
|
||||
{
|
||||
if (Type == MSOleOutfileType.MSOLE_DIR)
|
||||
return false;
|
||||
|
||||
if (Type == MSOleOutfileType.MSOLE_SMALL_BLOCK)
|
||||
{
|
||||
if ((CurrentOffset + num_bytes) < OLE_DEFAULT_THRESHOLD)
|
||||
{
|
||||
Array.Copy(Content_SmallBlock_Buf, CurrentOffset, data, 0, num_bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ok = Wrap(Sink);
|
||||
if (!ok)
|
||||
return false;
|
||||
|
||||
byte[] buf = Content_SmallBlock_Buf;
|
||||
Content_SmallBlock_Buf = null;
|
||||
|
||||
long start_offset = Sink.CurrentOffset;
|
||||
Content_BigBlock_StartOffset = (int)start_offset;
|
||||
if (Content_BigBlock_StartOffset != start_offset)
|
||||
{
|
||||
// Check for overflow
|
||||
Console.Error.WriteLine("File too big");
|
||||
return false;
|
||||
}
|
||||
|
||||
FirstBlock = CurrentBlock();
|
||||
Type = MSOleOutfileType.MSOLE_BIG_BLOCK;
|
||||
|
||||
int wsize = (int)CurrentSize;
|
||||
if (wsize != CurrentSize)
|
||||
{
|
||||
// Check for overflow
|
||||
Console.Error.WriteLine("File too big");
|
||||
return false;
|
||||
}
|
||||
|
||||
Sink.Write(wsize, buf);
|
||||
|
||||
// If we had a seek then we might not be at the right location.
|
||||
// This can happen both with a seek beyond the end of file
|
||||
// (see bug #14) and with a backward seek.
|
||||
Sink.Seek(Content_BigBlock_StartOffset + CurrentOffset, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
if (Type != MSOleOutfileType.MSOLE_BIG_BLOCK)
|
||||
return false;
|
||||
|
||||
Sink.Write(num_bytes, data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override long VPrintFImpl(string format, params string[] args)
|
||||
{
|
||||
// An optimization.
|
||||
if (Type == MSOleOutfileType.MSOLE_BIG_BLOCK)
|
||||
return Sink.VPrintF(format, args);
|
||||
|
||||
// In other cases, use the gsf_output_real_vprintf fallback method.
|
||||
// (This eventually calls gsf_outfile_msole_write, which will also
|
||||
// check that ole.type != MSOLE_DIR.)
|
||||
return VPrintF(format, args);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfOutput NewChild(string name, bool is_dir)
|
||||
{
|
||||
if (Type != MSOleOutfileType.MSOLE_DIR)
|
||||
return null;
|
||||
|
||||
GsfOutfileMSOle child = new GsfOutfileMSOle();
|
||||
if (is_dir)
|
||||
{
|
||||
child.Type = MSOleOutfileType.MSOLE_DIR;
|
||||
child.Content_Dir_Children = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Start as small block
|
||||
child.Type = MSOleOutfileType.MSOLE_SMALL_BLOCK;
|
||||
child.Content_SmallBlock_Buf = new byte[OLE_DEFAULT_THRESHOLD];
|
||||
}
|
||||
|
||||
child.Root = Root;
|
||||
child.Sink = Sink;
|
||||
|
||||
child.SetSmallBlockSize(SmallBlock.Size);
|
||||
child.SetBigBlockSize(BigBlock.Size);
|
||||
|
||||
child.Name = name;
|
||||
child.Container = this;
|
||||
|
||||
Content_Dir_Children.Add(child);
|
||||
Root.RegisterChild(child);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write <paramref name="clsid"/> to the directory associated with ole.
|
||||
/// </summary>
|
||||
/// <param name="clsid">Identifier (often a GUID in MS Windows apps)</param>
|
||||
/// <returns>True on success.</returns>
|
||||
public bool SetClassID(byte[] clsid)
|
||||
{
|
||||
if (Type != MSOleOutfileType.MSOLE_DIR)
|
||||
return false;
|
||||
|
||||
Array.Copy(clsid, ClassID, ClassID.Length);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of times 1 must be shifted left to reach value
|
||||
/// </summary>
|
||||
private static int ComputeShift(int value)
|
||||
{
|
||||
int i = 0;
|
||||
while ((value >> i) > 1)
|
||||
i++;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
private void SetBigBlockSize(int size)
|
||||
{
|
||||
BigBlock.Size = size;
|
||||
BigBlock.Shift = ComputeShift(size);
|
||||
}
|
||||
|
||||
private void SetSmallBlockSize(int size)
|
||||
{
|
||||
SmallBlock.Size = size;
|
||||
SmallBlock.Shift = ComputeShift(size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static objects are zero-initialized
|
||||
/// </summary>
|
||||
private static byte[] zero_buf = new byte[ZERO_PAD_BUF_SIZE];
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the block of the current offset in the file. A useful idiom is to
|
||||
/// pad_zero to move to the start of the next block, then get the block number.
|
||||
/// This avoids fence post type problems with partial blocks.
|
||||
/// </summary>
|
||||
private int CurrentBlock() => (int)((Sink.CurrentOffset - MSOleHeader.OLE_HEADER_SIZE) >> BigBlock.Shift);
|
||||
|
||||
private int BytesLeftInBlock()
|
||||
{
|
||||
// Blocks are multiples of BigBlock.Size (the header is padded out to BigBlock.Size)
|
||||
long r = Sink.CurrentOffset % BigBlock.Size;
|
||||
return (int)((r != 0) ? (BigBlock.Size - r) : 0);
|
||||
}
|
||||
|
||||
private void PadZero()
|
||||
{
|
||||
// No need to bounds check. len will always be less than bb.size, and
|
||||
// we already check that zero_buf is big enough at creation
|
||||
int len = BytesLeftInBlock();
|
||||
if (len > 0)
|
||||
Sink.Write(len, zero_buf);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utility routine to generate a BAT for a file known to be sequential and
|
||||
/// continuous.
|
||||
/// </summary>
|
||||
private static void WriteBat(GsfOutput sink, int block, int blocks)
|
||||
{
|
||||
byte[] buf = new byte[BAT_INDEX_SIZE * CHUNK_SIZE];
|
||||
int bufi = 0;
|
||||
|
||||
while (blocks-- > 1)
|
||||
{
|
||||
block++;
|
||||
|
||||
byte[] temp2 = BitConverter.GetBytes(block);
|
||||
Array.Copy(temp2, 0, buf, bufi * BAT_INDEX_SIZE, buf.Length);
|
||||
|
||||
bufi++;
|
||||
if (bufi == CHUNK_SIZE)
|
||||
{
|
||||
if (bufi != 0)
|
||||
sink.Write(bufi * BAT_INDEX_SIZE, buf);
|
||||
|
||||
bufi = 0;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] temp = BitConverter.GetBytes(BAT_MAGIC_END_OF_CHAIN);
|
||||
Array.Copy(temp, 0, buf, bufi * BAT_INDEX_SIZE, buf.Length);
|
||||
|
||||
bufi++;
|
||||
if (bufi == CHUNK_SIZE)
|
||||
{
|
||||
if (bufi != 0)
|
||||
sink.Write(bufi * BAT_INDEX_SIZE, buf);
|
||||
|
||||
bufi = 0;
|
||||
}
|
||||
|
||||
if (bufi != 0)
|
||||
sink.Write(bufi * BAT_INDEX_SIZE, buf);
|
||||
}
|
||||
|
||||
private static void WriteConst(GsfOutput sink, uint value, int n)
|
||||
{
|
||||
byte[] buf = new byte[BAT_INDEX_SIZE * CHUNK_SIZE];
|
||||
int bufi = 0;
|
||||
|
||||
while (n-- > 0)
|
||||
{
|
||||
byte[] temp = BitConverter.GetBytes(value);
|
||||
Array.Copy(temp, 0, buf, bufi * BAT_INDEX_SIZE, buf.Length);
|
||||
|
||||
bufi++;
|
||||
if (bufi == CHUNK_SIZE)
|
||||
{
|
||||
if (bufi != 0)
|
||||
sink.Write(bufi * BAT_INDEX_SIZE, buf);
|
||||
|
||||
bufi = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (bufi != 0)
|
||||
sink.Write(bufi * BAT_INDEX_SIZE, buf);
|
||||
|
||||
bufi = 0;
|
||||
}
|
||||
|
||||
private void PadBatUnused(int residual)
|
||||
{
|
||||
WriteConst(Sink, BAT_MAGIC_UNUSED, (BytesLeftInBlock() / BAT_INDEX_SIZE) - residual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the metadata (dirents, small block, xbats)
|
||||
/// </summary>
|
||||
private bool WriteDirectory()
|
||||
{
|
||||
byte[] buf = new byte[MSOleHeader.OLE_HEADER_SIZE];
|
||||
uint next;
|
||||
uint xbat_pos;
|
||||
int metabat_size = BigBlock.Size / BAT_INDEX_SIZE - 1;
|
||||
List<GsfOutfileMSOle> elem = Root.Content_Dir_RootOrder;
|
||||
|
||||
// Write small block data
|
||||
int blocks = 0;
|
||||
int sb_data_start = CurrentBlock();
|
||||
long data_size = Sink.CurrentOffset;
|
||||
|
||||
foreach (GsfOutfileMSOle child in elem)
|
||||
{
|
||||
if (child.Type == MSOleOutfileType.MSOLE_SMALL_BLOCK)
|
||||
{
|
||||
long size = child.CurrentSize;
|
||||
if (size > 0)
|
||||
{
|
||||
child.Blocks = (int)(((size - 1) >> SmallBlock.Shift) + 1);
|
||||
Sink.Write(child.Blocks << SmallBlock.Shift, child.Content_SmallBlock_Buf);
|
||||
child.FirstBlock = blocks;
|
||||
blocks += child.Blocks;
|
||||
}
|
||||
else
|
||||
{
|
||||
child.Blocks = 0;
|
||||
unchecked
|
||||
{
|
||||
child.FirstBlock = (int)BAT_MAGIC_END_OF_CHAIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data_size = Sink.CurrentOffset - data_size;
|
||||
int sb_data_size = (int)data_size;
|
||||
if (sb_data_size != data_size)
|
||||
{
|
||||
// Check for overflow
|
||||
Console.Error.WriteLine("File too big");
|
||||
return false;
|
||||
}
|
||||
|
||||
PadZero();
|
||||
int sb_data_blocks = CurrentBlock() - sb_data_start;
|
||||
|
||||
// Write small block BAT (the meta bat is in a file)
|
||||
int sbat_start = CurrentBlock();
|
||||
foreach (GsfOutfileMSOle child in elem)
|
||||
{
|
||||
if (child.Type == MSOleOutfileType.MSOLE_SMALL_BLOCK && child.Blocks > 0)
|
||||
WriteBat(Sink, child.FirstBlock, child.Blocks);
|
||||
}
|
||||
|
||||
PadBatUnused(0);
|
||||
int num_sbat = CurrentBlock() - sbat_start;
|
||||
|
||||
// Write dirents
|
||||
int dirent_start = CurrentBlock();
|
||||
for (int i = 0; i < elem.Count; i++)
|
||||
{
|
||||
GsfOutfileMSOle child = elem[i];
|
||||
|
||||
buf = Enumerable.Repeat<byte>(0, MSOleDirectoryEntry.DIRENT_SIZE).ToArray();
|
||||
|
||||
MSOleDirectoryEntry directoryEntry = MSOleDirectoryEntry.CreateDefault();
|
||||
|
||||
// Hard code 'Root Entry' for the root
|
||||
if (i == 0 || child.Name != null)
|
||||
{
|
||||
string name = (i == 0) ? "Root Entry" : child.Name;
|
||||
|
||||
byte[] nameUtf16Bytes = Encoding.UTF8.GetBytes(name);
|
||||
nameUtf16Bytes = Encoding.Convert(Encoding.UTF8, Encoding.Unicode, nameUtf16Bytes);
|
||||
ushort name_len = (ushort)nameUtf16Bytes.Length;
|
||||
if (name_len >= MSOleDirectoryEntry.DIRENT_MAX_NAME_SIZE)
|
||||
name_len = MSOleDirectoryEntry.DIRENT_MAX_NAME_SIZE - 1;
|
||||
|
||||
directoryEntry.NAME = nameUtf16Bytes;
|
||||
directoryEntry.NAME_LEN = (ushort)(name_len + 1);
|
||||
}
|
||||
|
||||
if (child.Root == child)
|
||||
{
|
||||
directoryEntry.TYPE_FLAG = DIRENT_TYPE.DIRENT_TYPE_ROOTDIR;
|
||||
directoryEntry.FIRSTBLOCK = (sb_data_size > 0) ? (uint)sb_data_start : BAT_MAGIC_END_OF_CHAIN;
|
||||
directoryEntry.FILE_SIZE = (uint)sb_data_size;
|
||||
directoryEntry.CLSID = child.ClassID;
|
||||
}
|
||||
else if (child.Type == MSOleOutfileType.MSOLE_DIR)
|
||||
{
|
||||
directoryEntry.TYPE_FLAG = DIRENT_TYPE.DIRENT_TYPE_DIR;
|
||||
directoryEntry.FIRSTBLOCK = BAT_MAGIC_END_OF_CHAIN;
|
||||
directoryEntry.FILE_SIZE = 0;
|
||||
directoryEntry.CLSID = child.ClassID;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint size = (uint)child.Parent.CurrentSize;
|
||||
if (size != child.Parent.CurrentSize)
|
||||
Console.Error.WriteLine("File too big");
|
||||
|
||||
directoryEntry.TYPE_FLAG = DIRENT_TYPE.DIRENT_TYPE_FILE;
|
||||
directoryEntry.FIRSTBLOCK = (uint)child.FirstBlock;
|
||||
directoryEntry.FILE_SIZE = size;
|
||||
directoryEntry.CLSID = new byte[0x10];
|
||||
}
|
||||
|
||||
directoryEntry.MODIFY_TIME = (ulong)(child.ModTime?.ToFileTime() ?? 0);
|
||||
|
||||
// Make everything black (red == 0)
|
||||
directoryEntry.COLOR = 1;
|
||||
|
||||
GsfOutfileMSOle tmp = child.Container as GsfOutfileMSOle;
|
||||
next = MSOleDirectoryEntry.DIRENT_MAGIC_END;
|
||||
if (child.Root != child && tmp != null)
|
||||
{
|
||||
for (int j = 0; j < tmp.Content_Dir_Children.Count; j++)
|
||||
{
|
||||
GsfOutfileMSOle ptr = tmp.Content_Dir_Children[j];
|
||||
if (ptr != child)
|
||||
continue;
|
||||
|
||||
if (j + 1 != tmp.Content_Dir_Children.Count && tmp.Content_Dir_Children[j + 1] != null)
|
||||
{
|
||||
GsfOutfileMSOle sibling = tmp.Content_Dir_Children[j + 1];
|
||||
next = (uint)sibling.ChildIndex;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Make linked list rather than tree, only use next
|
||||
directoryEntry.PREV = MSOleDirectoryEntry.DIRENT_MAGIC_END;
|
||||
directoryEntry.NEXT = next;
|
||||
|
||||
uint child_index = MSOleDirectoryEntry.DIRENT_MAGIC_END;
|
||||
if (child.Type == MSOleOutfileType.MSOLE_DIR && child.Content_Dir_Children != null)
|
||||
{
|
||||
GsfOutfileMSOle first = child.Content_Dir_Children[0];
|
||||
child_index = (uint)first.ChildIndex;
|
||||
}
|
||||
|
||||
directoryEntry.CHILD = child_index;
|
||||
|
||||
directoryEntry.Write(buf, 0);
|
||||
Sink.Write(MSOleDirectoryEntry.DIRENT_SIZE, buf);
|
||||
}
|
||||
|
||||
PadZero();
|
||||
int num_dirent_blocks = CurrentBlock() - dirent_start;
|
||||
|
||||
// Write BAT
|
||||
int bat_start = CurrentBlock();
|
||||
foreach (GsfOutfileMSOle child in elem)
|
||||
{
|
||||
if (child.Type == MSOleOutfileType.MSOLE_BIG_BLOCK)
|
||||
WriteBat(Sink, child.FirstBlock, child.Blocks);
|
||||
}
|
||||
|
||||
if (sb_data_blocks > 0)
|
||||
WriteBat(Sink, sb_data_start, sb_data_blocks);
|
||||
|
||||
if (num_sbat > 0)
|
||||
WriteBat(Sink, sbat_start, num_sbat);
|
||||
|
||||
WriteBat(Sink, dirent_start, num_dirent_blocks);
|
||||
|
||||
// List the BAT and meta-BAT blocks in the BAT. Doing this may
|
||||
// increase the size of the bat and hence the metabat, so be
|
||||
// prepared to iterate.
|
||||
long num_bat = 0;
|
||||
long num_xbat = 0;
|
||||
|
||||
while (Sink.Error == null)
|
||||
{
|
||||
// If we have an error, then the actual size as reported
|
||||
// by _tell and .cur_size may be out of sync. We don't
|
||||
// want to loop forever here.
|
||||
|
||||
long i = ((Sink.CurrentSize + BAT_INDEX_SIZE * (num_bat + num_xbat) - MSOleHeader.OLE_HEADER_SIZE - 1) >> BigBlock.Shift) + 1;
|
||||
i -= bat_start;
|
||||
if (num_bat != i)
|
||||
{
|
||||
num_bat = i;
|
||||
continue;
|
||||
}
|
||||
i = 0;
|
||||
if (num_bat > OLE_HEADER_METABAT_SIZE)
|
||||
i = 1 + ((num_bat - OLE_HEADER_METABAT_SIZE - 1)
|
||||
/ metabat_size);
|
||||
if (num_xbat != i)
|
||||
{
|
||||
num_xbat = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
WriteConst(Sink, BAT_MAGIC_BAT, (int)num_bat);
|
||||
WriteConst(Sink, BAT_MAGIC_METABAT, (int)num_xbat);
|
||||
PadBatUnused(0);
|
||||
|
||||
if (num_xbat > 0)
|
||||
{
|
||||
xbat_pos = (uint)CurrentBlock();
|
||||
blocks = OLE_HEADER_METABAT_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
xbat_pos = BAT_MAGIC_END_OF_CHAIN;
|
||||
blocks = (int)num_bat;
|
||||
}
|
||||
|
||||
// Fix up the header
|
||||
if (BigBlock.Size == 4096)
|
||||
{
|
||||
// Set _cSectDir for 4k sector files
|
||||
GSF_LE_SET_GUINT32(buf, 0, (uint)num_dirent_blocks);
|
||||
Sink.Seek(OLE_HEADER_CSECTDIR, SeekOrigin.Begin);
|
||||
Sink.Write(4, buf);
|
||||
}
|
||||
|
||||
GSF_LE_SET_GUINT32(buf, 0, (uint)num_bat);
|
||||
GSF_LE_SET_GUINT32(buf, 4, (uint)dirent_start);
|
||||
Sink.Seek(OLE_HEADER_NUM_BAT, SeekOrigin.Begin);
|
||||
Sink.Write(8, buf);
|
||||
|
||||
GSF_LE_SET_GUINT32(buf, 0x0, (num_sbat > 0) ? (uint)sbat_start : BAT_MAGIC_END_OF_CHAIN);
|
||||
GSF_LE_SET_GUINT32(buf, 0x4, (uint)num_sbat);
|
||||
GSF_LE_SET_GUINT32(buf, 0x8, xbat_pos);
|
||||
GSF_LE_SET_GUINT32(buf, 0xc, (uint)num_xbat);
|
||||
Sink.Seek(OLE_HEADER_SBAT_START, SeekOrigin.Begin);
|
||||
Sink.Write(0x10, buf);
|
||||
|
||||
// Write initial Meta-BAT
|
||||
for (int i = 0; i < blocks; i++)
|
||||
{
|
||||
GSF_LE_SET_GUINT32(buf, 0, (uint)(bat_start + i));
|
||||
Sink.Write(BAT_INDEX_SIZE, buf);
|
||||
}
|
||||
|
||||
// Write extended Meta-BAT
|
||||
if (num_xbat > 0)
|
||||
{
|
||||
Sink.Seek(0, SeekOrigin.End);
|
||||
for (long i = 0; i++ < num_xbat;)
|
||||
{
|
||||
bat_start += blocks;
|
||||
num_bat -= blocks;
|
||||
blocks = (int)((num_bat > metabat_size) ? metabat_size : num_bat);
|
||||
for (int j = 0; j < blocks; j++)
|
||||
{
|
||||
GSF_LE_SET_GUINT32(buf, 0, (uint)(bat_start + j));
|
||||
Sink.Write(BAT_INDEX_SIZE, buf);
|
||||
}
|
||||
|
||||
if (i == num_xbat)
|
||||
{
|
||||
PadBatUnused(1);
|
||||
xbat_pos = BAT_MAGIC_END_OF_CHAIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
xbat_pos++;
|
||||
}
|
||||
|
||||
GSF_LE_SET_GUINT32(buf, 0, xbat_pos);
|
||||
Sink.Write(BAT_INDEX_SIZE, buf);
|
||||
}
|
||||
}
|
||||
|
||||
// Free the children
|
||||
Content_Dir_RootOrder = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void HoistError()
|
||||
{
|
||||
if (Error == null && Sink.Error != null)
|
||||
Error = Sink.Error;
|
||||
}
|
||||
|
||||
private void RegisterChild(GsfOutfileMSOle child)
|
||||
{
|
||||
child.Root = this;
|
||||
child.ChildIndex = Content_Dir_RootOrder.Count;
|
||||
Content_Dir_RootOrder.Add(child);
|
||||
}
|
||||
|
||||
private static int NameCompare(GsfOutfileMSOle a, GsfOutfileMSOle b) => GsfInfileMSOle.SortingKeyCompare(a.Key, b.Key);
|
||||
|
||||
private void MakeSortingName()
|
||||
{
|
||||
Key = GsfMSOleSortingKey.Create(Name);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-outfile-stdio.c: A directory tree wrapper for Outfile
|
||||
*
|
||||
* Copyright (C) 2004-2006 Novell, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
public class GsfOutfileStdio : GsfOutfile
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public string Root { get; set; } = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfOutfileStdio() { }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="root">Root directory in utf8.</param>
|
||||
/// <param name="err">Place to store an Exception if anything goes wrong</param>
|
||||
/// <returns>A new outfile or null.</returns>
|
||||
public static GsfOutfileStdio Create(string root, ref Exception err)
|
||||
{
|
||||
if (!Directory.Exists(root))
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(root);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
err = new Exception($"{root}: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
GsfOutfileStdio ofs = new GsfOutfileStdio
|
||||
{
|
||||
Root = root,
|
||||
};
|
||||
|
||||
ofs.SetNameFromFilename(root);
|
||||
return ofs;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfOutput NewChild(string name, bool is_dir)
|
||||
{
|
||||
string path = Path.Combine(Root, name);
|
||||
|
||||
Exception err = null;
|
||||
|
||||
GsfOutput child;
|
||||
if (is_dir)
|
||||
child = Create(path, ref err);
|
||||
else
|
||||
child = GsfOutputStdio.Create(path, ref err);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool CloseImpl() => true;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
895
BurnOutSharp/External/libgsf/Output/GsfOutfileZip.cs
vendored
895
BurnOutSharp/External/libgsf/Output/GsfOutfileZip.cs
vendored
@@ -1,895 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-outfile-zip.c: zip archive output.
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jon K Hellan (hellan@acm.org)
|
||||
* Copyright (C) 2014 Morten Welinder (terra@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Outc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using ComponentAce.Compression.Libs.zlib;
|
||||
using static ComponentAce.Compression.Libs.zlib.zlibConst;
|
||||
using static LibGSF.GsfUtils;
|
||||
using static LibGSF.GsfZipImpl;
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
public class GsfOutfileZip : GsfOutfile
|
||||
{
|
||||
#region Constants
|
||||
|
||||
public static readonly bool? ZIP_CREATE_DEFAULT_ZIP64 = null;
|
||||
|
||||
public const bool ZIP_ADD_UNIXTIME_FIELD = true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public GsfOutput Sink { get; set; } = null;
|
||||
|
||||
public GsfOutfileZip Root { get; set; } = null;
|
||||
|
||||
public bool? SinkIsSeekable { get; set; } = null;
|
||||
|
||||
public bool? Zip64 { get; set; } = ZIP_CREATE_DEFAULT_ZIP64;
|
||||
|
||||
public string EntryName { get; set; } = null;
|
||||
|
||||
public GsfZipVDir VDir { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Only valid for the root, ordered
|
||||
/// </summary>
|
||||
public List<GsfOutfileZip> RootOrder { get; set; } = null;
|
||||
|
||||
public ZStream Stream { get; set; } = null;
|
||||
|
||||
public GsfZipCompressionMethod CompressionMethod { get; set; } = GsfZipCompressionMethod.GSF_ZIP_DEFLATED;
|
||||
|
||||
public int DeflateLevel { get; set; } = Z_DEFAULT_COMPRESSION;
|
||||
|
||||
public bool Writing { get; set; } = false;
|
||||
|
||||
public byte[] Buf { get; set; } = null;
|
||||
|
||||
public int BufSize { get; set; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfOutfileZip() { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates the root directory of a Zip file and manages the addition of
|
||||
/// children.
|
||||
/// </summary>
|
||||
/// <param name="sink">A GsfOutput to hold the ZIP file</param>
|
||||
/// <param name="err">Location to store error, or null; currently unused.</param>
|
||||
/// <returns>The new zip file handler</returns>
|
||||
/// <remarks>This adds a reference to <paramref name="sink"/>.</remarks>
|
||||
public static GsfOutfileZip Create(GsfOutput sink, ref Exception err)
|
||||
{
|
||||
if (sink == null)
|
||||
return null;
|
||||
|
||||
err = null;
|
||||
GsfOutfileZip zip = new GsfOutfileZip { Sink = sink };
|
||||
if (zip.EntryName == null)
|
||||
{
|
||||
zip.VDir = GsfZipVDir.Create(string.Empty, true, null);
|
||||
zip.RootOrder = new List<GsfOutfileZip>();
|
||||
zip.Root = zip;
|
||||
|
||||
// The names are the same
|
||||
zip.Name = zip.Sink.Name;
|
||||
zip.Container = null;
|
||||
}
|
||||
|
||||
if (zip.ModTime == null)
|
||||
{
|
||||
DateTime? modtime = DateTime.UtcNow;
|
||||
zip.ModTime = modtime;
|
||||
}
|
||||
|
||||
return zip;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfOutfileZip()
|
||||
{
|
||||
// If the closing failed, we might have stuff here.
|
||||
DisconnectChildren();
|
||||
|
||||
if (Sink != null)
|
||||
SetSink(null);
|
||||
|
||||
if (Stream != null)
|
||||
Stream.deflateEnd();
|
||||
|
||||
if (this == Root)
|
||||
VDir.Free(true); // Frees vdirs recursively
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence) => false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool CloseImpl()
|
||||
{
|
||||
bool ret;
|
||||
|
||||
// The root dir
|
||||
if (this == Root)
|
||||
{
|
||||
ret = CloseRoot();
|
||||
}
|
||||
else if (VDir.IsDirectory)
|
||||
{
|
||||
// Directories: Do nothing. Should change this to actually
|
||||
// write dirs which don't have children.
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = CloseStream();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool WriteImpl(int num_bytes, byte[] data)
|
||||
{
|
||||
if (VDir == null)
|
||||
return false;
|
||||
if (VDir.IsDirectory)
|
||||
return false;
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
int ret;
|
||||
|
||||
if (!Writing)
|
||||
{
|
||||
if (!InitWrite())
|
||||
return false;
|
||||
}
|
||||
|
||||
GsfZipDirectoryEntry dirent = VDir.DirectoryEntry;
|
||||
|
||||
if (dirent.Zip64 == false &&
|
||||
(num_bytes >= uint.MaxValue || CurrentOffset >= (long)(uint.MaxValue - num_bytes)))
|
||||
{
|
||||
// Uncompressed size field would overflow.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CompressionMethod == GsfZipCompressionMethod.GSF_ZIP_DEFLATED)
|
||||
{
|
||||
Stream.next_in = data;
|
||||
Stream.avail_in = num_bytes;
|
||||
|
||||
while (Stream.avail_in > 0)
|
||||
{
|
||||
if (Stream.avail_out == 0)
|
||||
{
|
||||
if (!OutputBlock())
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = Stream.deflate(Z_NO_FLUSH);
|
||||
if (ret != Z_OK)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Sink.Write(num_bytes, data))
|
||||
return false;
|
||||
|
||||
dirent.CompressedSize += num_bytes;
|
||||
}
|
||||
|
||||
// TODO: Enable CRC32 calculation
|
||||
//dirent.CRC32 = crc32(dirent.crc32, data, num_bytes);
|
||||
dirent.UncompressedSize += num_bytes;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void RootRegisterChild(GsfOutfileZip child)
|
||||
{
|
||||
child.Root = this;
|
||||
if (!child.VDir.IsDirectory)
|
||||
Root.RootOrder.Add(child);
|
||||
}
|
||||
|
||||
public void SetSink(GsfOutput sink)
|
||||
{
|
||||
Sink = sink;
|
||||
SinkIsSeekable = null;
|
||||
}
|
||||
|
||||
public override GsfOutput NewChild(string name, bool is_dir)
|
||||
{
|
||||
if (VDir == null)
|
||||
return null;
|
||||
if (!VDir.IsDirectory)
|
||||
return null;
|
||||
if (string.IsNullOrEmpty(name))
|
||||
return null;
|
||||
|
||||
GsfOutfileZip child = new GsfOutfileZip
|
||||
{
|
||||
Name = name,
|
||||
Zip64 = this.Zip64,
|
||||
VDir = GsfZipVDir.Create(name, is_dir, null),
|
||||
Container = this,
|
||||
};
|
||||
|
||||
if (child.EntryName == null)
|
||||
{
|
||||
child.VDir = GsfZipVDir.Create(string.Empty, true, null);
|
||||
child.RootOrder = new List<GsfOutfileZip>();
|
||||
child.Root = child;
|
||||
|
||||
// The names are the same
|
||||
child.Name = child.Sink.Name;
|
||||
child.Container = null;
|
||||
}
|
||||
|
||||
if (child.ModTime == null)
|
||||
{
|
||||
DateTime? modtime = DateTime.UtcNow;
|
||||
child.ModTime = modtime;
|
||||
}
|
||||
|
||||
VDir.AddChild(child.VDir);
|
||||
Root.RootRegisterChild(child);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private void DisconnectChildren()
|
||||
{
|
||||
if (RootOrder == null)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < RootOrder.Count; i++)
|
||||
{
|
||||
RootOrder[i] = null;
|
||||
}
|
||||
|
||||
RootOrder = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The "mimetype" member is special for ODF. It cannot have any
|
||||
/// extra field (and thus cannot be a zip64 member). Hardcode
|
||||
/// this to help compatibility with libgsf users depending on
|
||||
/// past behaviour of zip creation.
|
||||
///
|
||||
/// The flip side is that such a file cannot be 4G+.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private static bool SpecialMimetypeDirectoryEntry(GsfZipDirectoryEntry dirent)
|
||||
{
|
||||
return (dirent.Offset == 0
|
||||
&& dirent.Zip64 != true
|
||||
&& dirent.CompressionMethod == GsfZipCompressionMethod.GSF_ZIP_STORED
|
||||
&& dirent.Name == "mimetype");
|
||||
}
|
||||
|
||||
private bool DirectoryEntryWrite(GsfZipDirectoryEntry dirent)
|
||||
{
|
||||
int nlen = dirent.Name.Length;
|
||||
List<byte> extras = new List<byte>(ZIP_DIRENT_SIZE + nlen + 100);
|
||||
bool offset_in_zip64 = dirent.Offset >= uint.MaxValue;
|
||||
bool zip64_here = (dirent.Zip64 == true || offset_in_zip64);
|
||||
byte extract = (byte)(zip64_here ? 45 : 20); // Unsure if dirent.zip64 is enough
|
||||
|
||||
if (zip64_here)
|
||||
{
|
||||
byte[] tmp = new byte[8];
|
||||
|
||||
// We could unconditionally store the offset here, but
|
||||
// zipinfo has a known bug in which it fails to account
|
||||
// for differences in extra fields between the global
|
||||
// and the local headers. So we try to make them the
|
||||
// same.
|
||||
|
||||
GSF_LE_SET_GUINT16(tmp, 0, (ushort)ExtraFieldTags.ZIP_DIRENT_EXTRA_FIELD_ZIP64);
|
||||
GSF_LE_SET_GUINT16(tmp, 2, (ushort)(2 + (offset_in_zip64 ? 1 : 0) * 8));
|
||||
extras.AddRange(new ReadOnlySpan<byte>(tmp, 0, 4).ToArray());
|
||||
GSF_LE_SET_GUINT64(tmp, 0, (ulong)dirent.UncompressedSize);
|
||||
extras.AddRange(new ReadOnlySpan<byte>(tmp, 0, 8).ToArray());
|
||||
GSF_LE_SET_GUINT64(tmp, 0, (ulong)dirent.CompressedSize);
|
||||
extras.AddRange(new ReadOnlySpan<byte>(tmp, 0, 8).ToArray());
|
||||
if (offset_in_zip64)
|
||||
{
|
||||
GSF_LE_SET_GUINT64(tmp, 0, (ulong)dirent.Offset);
|
||||
extras.AddRange(new ReadOnlySpan<byte>(tmp, 0, 8).ToArray());
|
||||
}
|
||||
}
|
||||
else if (dirent.Zip64 == null)
|
||||
{
|
||||
byte[] tmp = new byte[8];
|
||||
|
||||
// Match the local header.
|
||||
GSF_LE_SET_GUINT16(tmp, 0, (ushort)ExtraFieldTags.ZIP_DIRENT_EXTRA_FIELD_IGNORE);
|
||||
GSF_LE_SET_GUINT16(tmp, 2, 2 * 8);
|
||||
extras.AddRange(new ReadOnlySpan<byte>(tmp, 0, 4).ToArray());
|
||||
GSF_LE_SET_GUINT64(tmp, 0, 0);
|
||||
extras.AddRange(new ReadOnlySpan<byte>(tmp, 0, 8).ToArray());
|
||||
GSF_LE_SET_GUINT64(tmp, 0, 0);
|
||||
extras.AddRange(new ReadOnlySpan<byte>(tmp, 0, 8).ToArray());
|
||||
}
|
||||
|
||||
if (ZIP_ADD_UNIXTIME_FIELD && dirent.ModifiedTime != null && !SpecialMimetypeDirectoryEntry(dirent))
|
||||
{
|
||||
// Clearly a year 2038 problem here.
|
||||
byte[] tmp = new byte[4];
|
||||
|
||||
GSF_LE_SET_GUINT16(tmp, 0, (ushort)(ExtraFieldTags.ZIP_DIRENT_EXTRA_FIELD_UNIXTIME));
|
||||
GSF_LE_SET_GUINT16(tmp, 2, 5);
|
||||
extras.AddRange(new ReadOnlySpan<byte>(tmp, 0, 4).ToArray());
|
||||
tmp[0] = 1;
|
||||
extras.AddRange(new ReadOnlySpan<byte>(tmp, 0, 1).ToArray());
|
||||
GSF_LE_SET_GUINT32(tmp, 0, (uint)new DateTimeOffset(dirent.ModifiedTime ?? DateTime.UtcNow).ToUnixTimeSeconds());
|
||||
extras.AddRange(new ReadOnlySpan<byte>(tmp, 0, 4).ToArray());
|
||||
}
|
||||
|
||||
byte[] buf = new byte[ZIP_DIRENT_SIZE];
|
||||
|
||||
GSF_LE_SET_GUINT32(buf, 0, ZIP_DIRENT_SIGNATURE);
|
||||
GSF_LE_SET_GUINT16(buf, ZIP_DIRENT_ENCODER, (ushort)(((int)OSCodes.ZIP_OS_UNIX << 8) + extract));
|
||||
GSF_LE_SET_GUINT16(buf, ZIP_DIRENT_EXTRACT, (ushort)(((int)OSCodes.ZIP_OS_MSDOS << 8) + extract));
|
||||
GSF_LE_SET_GUINT16(buf, ZIP_DIRENT_FLAGS, (ushort)dirent.Flags);
|
||||
GSF_LE_SET_GUINT16(buf, ZIP_DIRENT_COMPR_METHOD, (ushort)dirent.CompressionMethod);
|
||||
GSF_LE_SET_GUINT32(buf, ZIP_DIRENT_DOSTIME, dirent.DosTime);
|
||||
GSF_LE_SET_GUINT32(buf, ZIP_DIRENT_CRC32, dirent.CRC32);
|
||||
GSF_LE_SET_GUINT32(buf, ZIP_DIRENT_CSIZE, (uint)(zip64_here ? uint.MaxValue : dirent.CompressedSize));
|
||||
GSF_LE_SET_GUINT32(buf, ZIP_DIRENT_USIZE, (uint)(zip64_here ? uint.MaxValue : dirent.UncompressedSize));
|
||||
GSF_LE_SET_GUINT16(buf, ZIP_DIRENT_NAME_SIZE, (ushort)nlen);
|
||||
GSF_LE_SET_GUINT16(buf, ZIP_DIRENT_EXTRAS_SIZE, (ushort)extras.Count);
|
||||
GSF_LE_SET_GUINT16(buf, ZIP_DIRENT_COMMENT_SIZE, 0);
|
||||
GSF_LE_SET_GUINT16(buf, ZIP_DIRENT_DISKSTART, 0);
|
||||
GSF_LE_SET_GUINT16(buf, ZIP_DIRENT_FILE_TYPE, 0);
|
||||
|
||||
// Hardcode file mode 644
|
||||
GSF_LE_SET_GUINT32(buf, ZIP_DIRENT_FILE_MODE, 0100644u << 16);
|
||||
GSF_LE_SET_GUINT32(buf, ZIP_DIRENT_OFFSET, (uint)(offset_in_zip64 ? uint.MaxValue : dirent.Offset));
|
||||
|
||||
// Stuff everything into buf so we can do just one write.
|
||||
List<byte> header = new List<byte>();
|
||||
header.AddRange(buf);
|
||||
header.AddRange(extras);
|
||||
header.AddRange(Encoding.ASCII.GetBytes(dirent.Name));
|
||||
|
||||
return Sink.Write(header.Count, header.ToArray());
|
||||
}
|
||||
|
||||
private bool TrailerWrite(int entries, long dirpos, long dirsize)
|
||||
{
|
||||
byte[] buf = new byte[ZIP_TRAILER_SIZE];
|
||||
|
||||
GSF_LE_SET_GUINT32(buf, 0, ZIP_TRAILER_SIGNATURE);
|
||||
GSF_LE_SET_GUINT16(buf, ZIP_TRAILER_ENTRIES, (ushort)Math.Min(entries, ushort.MaxValue));
|
||||
GSF_LE_SET_GUINT16(buf, ZIP_TRAILER_TOTAL_ENTRIES, (ushort)Math.Min(entries, ushort.MaxValue));
|
||||
GSF_LE_SET_GUINT32(buf, ZIP_TRAILER_DIR_SIZE, (uint)Math.Min(dirsize, uint.MaxValue));
|
||||
GSF_LE_SET_GUINT32(buf, ZIP_TRAILER_DIR_POS, (uint)Math.Min(dirpos, uint.MaxValue));
|
||||
|
||||
return Sink.Write(buf.Length, buf);
|
||||
}
|
||||
|
||||
private bool Trailer64Write(int entries, long dirpos, long dirsize)
|
||||
{
|
||||
byte[] buf = new byte[ZIP_TRAILER64_SIZE];
|
||||
byte extract = 45;
|
||||
|
||||
GSF_LE_SET_GUINT32(buf, 0, ZIP_TRAILER64_SIGNATURE);
|
||||
GSF_LE_SET_GUINT64(buf, ZIP_TRAILER64_RECSIZE, ZIP_TRAILER64_SIZE - 12);
|
||||
GSF_LE_SET_GUINT16(buf, ZIP_TRAILER64_ENCODER, (ushort)(((int)OSCodes.ZIP_OS_UNIX << 8) + extract));
|
||||
GSF_LE_SET_GUINT16(buf, ZIP_TRAILER64_EXTRACT, (ushort)(((int)OSCodes.ZIP_OS_MSDOS << 8) + extract));
|
||||
GSF_LE_SET_GUINT32(buf, ZIP_TRAILER64_DISK, 0);
|
||||
GSF_LE_SET_GUINT32(buf, ZIP_TRAILER64_DIR_DISK, 0);
|
||||
GSF_LE_SET_GUINT32(buf, ZIP_TRAILER64_ENTRIES, (uint)entries);
|
||||
GSF_LE_SET_GUINT32(buf, ZIP_TRAILER64_TOTAL_ENTRIES, (uint)entries);
|
||||
GSF_LE_SET_GUINT64(buf, ZIP_TRAILER64_DIR_SIZE, (ulong)dirsize);
|
||||
GSF_LE_SET_GUINT64(buf, ZIP_TRAILER64_DIR_POS, (ulong)dirpos);
|
||||
|
||||
return Sink.Write(buf.Length, buf);
|
||||
}
|
||||
|
||||
private bool Zip64LocatorWrite(long trailerpos)
|
||||
{
|
||||
byte[] buf = new byte[ZIP_ZIP64_LOCATOR_SIZE];
|
||||
|
||||
GSF_LE_SET_GUINT32(buf, 0, ZIP_ZIP64_LOCATOR_SIGNATURE);
|
||||
GSF_LE_SET_GUINT32(buf, ZIP_ZIP64_LOCATOR_DISK, 0);
|
||||
GSF_LE_SET_GUINT64(buf, ZIP_TRAILER64_DIR_POS, (ulong)trailerpos);
|
||||
GSF_LE_SET_GUINT32(buf, ZIP_ZIP64_LOCATOR_DISKS, 1);
|
||||
|
||||
return Sink.Write(buf.Length, buf);
|
||||
}
|
||||
|
||||
private static int OffsetOrdering(GsfOutfileZip a, GsfOutfileZip b)
|
||||
{
|
||||
long diff = a.VDir.DirectoryEntry.Offset - b.VDir.DirectoryEntry.Offset;
|
||||
return diff < 0 ? -1 : diff > 0 ? +1 : 0;
|
||||
}
|
||||
|
||||
private bool CloseRoot()
|
||||
{
|
||||
bool? zip64 = Zip64;
|
||||
|
||||
// Check that children are closed
|
||||
for (int i = 0; i < RootOrder.Count; i++)
|
||||
{
|
||||
GsfOutfileZip child = RootOrder[i];
|
||||
GsfZipDirectoryEntry dirent = child.VDir.DirectoryEntry;
|
||||
if (dirent.Zip64 == true)
|
||||
zip64 = true;
|
||||
|
||||
if (!child.IsClosed)
|
||||
{
|
||||
Console.Error.WriteLine("Child still open");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (true)
|
||||
{
|
||||
// It is unclear whether we need this. However, the
|
||||
// zipdetails utility gets utterly confused if we do
|
||||
// not.
|
||||
//
|
||||
// If we do not sort, we will use the ordering in which
|
||||
// the members were actually being written. Note, that
|
||||
// merely creating the member doesn't count -- it's the
|
||||
// actual writing (or closing an empty member) that
|
||||
// counts.
|
||||
|
||||
RootOrder.Sort(OffsetOrdering);
|
||||
}
|
||||
|
||||
// Write directory
|
||||
long dirpos = Sink.CurrentOffset;
|
||||
for (int i = 0; i < RootOrder.Count; i++)
|
||||
{
|
||||
GsfOutfileZip child = RootOrder[i];
|
||||
GsfZipDirectoryEntry dirent = child.VDir.DirectoryEntry;
|
||||
if (!DirectoryEntryWrite(dirent))
|
||||
return false;
|
||||
}
|
||||
|
||||
long dirend = Sink.CurrentOffset;
|
||||
|
||||
if (RootOrder.Count >= ushort.MaxValue || dirend >= uint.MaxValue - ZIP_TRAILER_SIZE)
|
||||
{
|
||||
// We don't have a choice; force zip64.
|
||||
zip64 = true;
|
||||
}
|
||||
|
||||
DisconnectChildren();
|
||||
|
||||
if (zip64 == null)
|
||||
zip64 = false;
|
||||
|
||||
if (zip64 == true)
|
||||
{
|
||||
if (!Trailer64Write(RootOrder.Count, dirpos, dirend - dirpos))
|
||||
return false;
|
||||
|
||||
if (!Zip64LocatorWrite(dirend))
|
||||
return false;
|
||||
}
|
||||
|
||||
return TrailerWrite(RootOrder.Count, dirpos, dirend - dirpos);
|
||||
}
|
||||
|
||||
private void StreamNameWriteToBuf(string res)
|
||||
{
|
||||
if (this == Root)
|
||||
return;
|
||||
|
||||
if (Container != null)
|
||||
{
|
||||
(Container as GsfOutfileZip).StreamNameWriteToBuf(res);
|
||||
if (res.Length != 0)
|
||||
{
|
||||
// Forward slash is specified by the format.
|
||||
res += ZIP_NAME_SEPARATOR;
|
||||
}
|
||||
}
|
||||
|
||||
if (EntryName != null)
|
||||
res += EntryName;
|
||||
}
|
||||
|
||||
private string StreamNameBuild()
|
||||
{
|
||||
string str = new string('\0', 80);
|
||||
StreamNameWriteToBuf(str);
|
||||
return str;
|
||||
}
|
||||
|
||||
private static uint ZipTimeMake(DateTime? modtime) => (uint)(modtime ?? DateTime.UtcNow).ToFileTime();
|
||||
|
||||
private GsfZipDirectoryEntry NewDirectoryEntry()
|
||||
{
|
||||
string name = StreamNameBuild();
|
||||
|
||||
// The spec is a bit vague about the length limit for file names, but
|
||||
// clearly we should not go beyond 0xffff.
|
||||
if (name.Length < ushort.MaxValue)
|
||||
{
|
||||
GsfZipDirectoryEntry dirent = GsfZipDirectoryEntry.Create();
|
||||
DateTime? modtime = ModTime;
|
||||
|
||||
dirent.Name = name;
|
||||
dirent.CompressionMethod = CompressionMethod;
|
||||
|
||||
if (modtime == null)
|
||||
modtime = DateTime.UtcNow;
|
||||
|
||||
dirent.DosTime = ZipTimeMake(modtime);
|
||||
dirent.ModifiedTime = modtime;
|
||||
dirent.Zip64 = Zip64;
|
||||
|
||||
return dirent;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private bool HeaderWrite()
|
||||
{
|
||||
byte[] hbuf = new byte[ZIP_HEADER_SIZE];
|
||||
|
||||
GsfZipDirectoryEntry dirent = VDir.DirectoryEntry;
|
||||
string name = dirent.Name;
|
||||
int nlen = name.Length;
|
||||
|
||||
GSF_LE_SET_GUINT32(hbuf, 0, ZIP_HEADER_SIGNATURE);
|
||||
|
||||
if (SinkIsSeekable == null)
|
||||
{
|
||||
// We need to figure out if the sink is seekable, but we lack
|
||||
// an API to check that. Instead, write the signature and
|
||||
// try to seek back onto it. If seeking back fails, just
|
||||
// don't rewrite it.
|
||||
|
||||
if (!Sink.Write(4, hbuf))
|
||||
return false;
|
||||
|
||||
SinkIsSeekable = Sink.Seek(dirent.Offset, SeekOrigin.Begin);
|
||||
if (SinkIsSeekable == false)
|
||||
hbuf = new byte[ZIP_HEADER_SIZE];
|
||||
}
|
||||
|
||||
// Now figure out if we need a DDESC record.
|
||||
if (SinkIsSeekable == true)
|
||||
dirent.Flags &= ~ZIP_DIRENT_FLAGS_HAS_DDESC;
|
||||
else
|
||||
dirent.Flags |= ZIP_DIRENT_FLAGS_HAS_DDESC;
|
||||
|
||||
bool has_ddesc = (dirent.Flags & ZIP_DIRENT_FLAGS_HAS_DDESC) != 0;
|
||||
uint crc32 = has_ddesc ? 0 : dirent.CRC32;
|
||||
long csize = has_ddesc ? 0 : dirent.CompressedSize;
|
||||
long usize = has_ddesc ? 0 : dirent.UncompressedSize;
|
||||
|
||||
// Determine if we need a real zip64 extra field. We do so, if
|
||||
// - forced
|
||||
// - in auto mode, if usize or csize has overflowed
|
||||
// - in auto mode, if we use a DDESC
|
||||
bool real_zip64 = (dirent.Zip64 == true
|
||||
|| (dirent.Zip64 == null
|
||||
&& (has_ddesc
|
||||
|| dirent.UncompressedSize >= uint.MaxValue
|
||||
|| dirent.CompressedSize >= uint.MaxValue)));
|
||||
|
||||
byte extract = 20;
|
||||
if (real_zip64)
|
||||
extract = 45;
|
||||
|
||||
List<byte> extras = new List<byte>(ZIP_HEADER_SIZE + nlen + 100);
|
||||
|
||||
// In the has_ddesc case, we write crc32/size/usize as zero and store
|
||||
// the right values in the DDESC record that follows the data.
|
||||
//
|
||||
// In the !has_ddesc case, we return to the same spot and write the
|
||||
// header a second time correcting crc32/size/usize, see
|
||||
// see HeaderPatchSizes. For this reason, we must ensure that
|
||||
// the record's length does not depend on the the sizes.
|
||||
//
|
||||
// In the the has_ddesc case we store zeroes here. No idea what we
|
||||
// were supposed to write.
|
||||
|
||||
// Auto or forced
|
||||
if (dirent.Zip64 != false)
|
||||
{
|
||||
byte[] tmp = new byte[8];
|
||||
ExtraFieldTags typ = real_zip64
|
||||
? ExtraFieldTags.ZIP_DIRENT_EXTRA_FIELD_ZIP64
|
||||
: ExtraFieldTags.ZIP_DIRENT_EXTRA_FIELD_IGNORE;
|
||||
|
||||
GSF_LE_SET_GUINT16(tmp, 0, (ushort)typ);
|
||||
GSF_LE_SET_GUINT16(tmp, 2, 2 * 8);
|
||||
extras.AddRange(new ReadOnlySpan<byte>(tmp, 0, 4).ToArray());
|
||||
GSF_LE_SET_GUINT64(tmp, 0, (ulong)usize);
|
||||
extras.AddRange(new ReadOnlySpan<byte>(tmp, 0, 8).ToArray());
|
||||
GSF_LE_SET_GUINT64(tmp, 0, (ulong)csize);
|
||||
extras.AddRange(new ReadOnlySpan<byte>(tmp, 0, 8).ToArray());
|
||||
}
|
||||
|
||||
if (ZIP_ADD_UNIXTIME_FIELD && dirent.ModifiedTime != null && !SpecialMimetypeDirectoryEntry(dirent))
|
||||
{
|
||||
byte[] tmp = new byte[4];
|
||||
|
||||
// Clearly a year 2038 problem here.
|
||||
GSF_LE_SET_GUINT16(tmp, 0, (ushort)ExtraFieldTags.ZIP_DIRENT_EXTRA_FIELD_UNIXTIME);
|
||||
GSF_LE_SET_GUINT16(tmp, 2, 5);
|
||||
extras.AddRange(new ReadOnlySpan<byte>(tmp, 0, 4).ToArray());
|
||||
tmp[0] = 1;
|
||||
extras.AddRange(new ReadOnlySpan<byte>(tmp, 0, 1).ToArray());
|
||||
GSF_LE_SET_GUINT32(tmp, 0, (uint)new DateTimeOffset(dirent.ModifiedTime ?? DateTime.UtcNow).ToUnixTimeSeconds());
|
||||
extras.AddRange(new ReadOnlySpan<byte>(tmp, 0, 4).ToArray());
|
||||
}
|
||||
|
||||
GSF_LE_SET_GUINT16(hbuf, ZIP_HEADER_EXTRACT, (ushort)(((int)OSCodes.ZIP_OS_MSDOS << 8) + extract));
|
||||
GSF_LE_SET_GUINT16(hbuf, ZIP_HEADER_FLAGS, (ushort)dirent.Flags);
|
||||
GSF_LE_SET_GUINT16(hbuf, ZIP_HEADER_COMP_METHOD, (ushort)dirent.CompressionMethod);
|
||||
GSF_LE_SET_GUINT32(hbuf, ZIP_HEADER_DOSTIME, dirent.DosTime);
|
||||
GSF_LE_SET_GUINT32(hbuf, ZIP_HEADER_CRC32, crc32);
|
||||
GSF_LE_SET_GUINT32(hbuf, ZIP_HEADER_CSIZE, (uint)(real_zip64 && !has_ddesc ? uint.MaxValue : csize));
|
||||
GSF_LE_SET_GUINT32(hbuf, ZIP_HEADER_USIZE, (uint)(real_zip64 && !has_ddesc ? uint.MaxValue : usize));
|
||||
GSF_LE_SET_GUINT16(hbuf, ZIP_HEADER_NAME_SIZE, (ushort)nlen);
|
||||
GSF_LE_SET_GUINT16(hbuf, ZIP_HEADER_EXTRAS_SIZE, (ushort)extras.Count);
|
||||
|
||||
// Stuff everything into buf so we can do just one write.
|
||||
List<byte> header = new List<byte>();
|
||||
header.AddRange(hbuf);
|
||||
header.AddRange(extras);
|
||||
header.AddRange(Encoding.ASCII.GetBytes(name));
|
||||
|
||||
bool ret = Sink.Write(header.Count, header.ToArray());
|
||||
if (real_zip64)
|
||||
dirent.Zip64 = true;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private bool InitWrite()
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (Root.Writing)
|
||||
{
|
||||
Console.Error.WriteLine("Already writing to another stream in archive");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Wrap(Sink))
|
||||
return false;
|
||||
|
||||
GsfZipDirectoryEntry dirent = NewDirectoryEntry();
|
||||
if (dirent == null)
|
||||
{
|
||||
Unwrap(Sink);
|
||||
return false;
|
||||
}
|
||||
|
||||
dirent.Offset = Sink.CurrentOffset;
|
||||
if (SpecialMimetypeDirectoryEntry(dirent))
|
||||
dirent.Zip64 = false;
|
||||
|
||||
VDir.DirectoryEntry = dirent;
|
||||
|
||||
HeaderWrite();
|
||||
Writing = true;
|
||||
Root.Writing = true;
|
||||
|
||||
// TODO: Enable CRC32 calculation
|
||||
// dirent.CRC32 = crc32(0L, Z_null, 0);
|
||||
if (CompressionMethod == GsfZipCompressionMethod.GSF_ZIP_DEFLATED)
|
||||
{
|
||||
if (Stream == null)
|
||||
Stream = new ZStream();
|
||||
|
||||
ret = Stream.inflateInit(-15);
|
||||
if (ret != Z_OK)
|
||||
return false;
|
||||
|
||||
ret = Stream.deflateParams(DeflateLevel, Z_DEFAULT_STRATEGY);
|
||||
if (ret != Z_OK)
|
||||
return false;
|
||||
|
||||
if (Buf == null)
|
||||
{
|
||||
BufSize = ZIP_BUF_SIZE;
|
||||
Buf = new byte[BufSize];
|
||||
}
|
||||
|
||||
Stream.next_out = Buf;
|
||||
Stream.avail_out = BufSize;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool OutputBlock()
|
||||
{
|
||||
int num_bytes = BufSize - Stream.avail_out;
|
||||
GsfZipDirectoryEntry dirent = VDir.DirectoryEntry;
|
||||
|
||||
if (!Sink.Write(num_bytes, Buf))
|
||||
return false;
|
||||
|
||||
dirent.CompressedSize += num_bytes;
|
||||
if (dirent.Zip64 == false && dirent.CompressedSize >= uint.MaxValue)
|
||||
return false;
|
||||
|
||||
Stream.next_out = Buf;
|
||||
Stream.avail_out = BufSize;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool Flush()
|
||||
{
|
||||
int zret;
|
||||
do
|
||||
{
|
||||
zret = Stream.deflate(Z_FINISH);
|
||||
if (zret == Z_OK || (zret == Z_BUF_ERROR && Stream.avail_out == 0))
|
||||
{
|
||||
// In this case Z_OK or Z_BUF_ERROR means more buffer space is needed
|
||||
if (!OutputBlock())
|
||||
return false;
|
||||
}
|
||||
} while (zret == Z_OK || zret == Z_BUF_ERROR);
|
||||
|
||||
if (zret != Z_STREAM_END)
|
||||
return false;
|
||||
|
||||
if (!OutputBlock())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the per stream data descriptor
|
||||
/// </summary>
|
||||
private bool DataDescriptorWrite()
|
||||
{
|
||||
byte[] buf = new byte[Math.Max(ZIP_DDESC_SIZE, ZIP_DDESC64_SIZE)];
|
||||
GsfZipDirectoryEntry dirent = VDir.DirectoryEntry;
|
||||
int size;
|
||||
|
||||
// Documentation says signature is not official.
|
||||
|
||||
if (dirent.Zip64 != false)
|
||||
{
|
||||
GSF_LE_SET_GUINT32(buf, 0, ZIP_DDESC64_SIGNATURE);
|
||||
GSF_LE_SET_GUINT32(buf, ZIP_DDESC64_CRC32, dirent.CRC32);
|
||||
GSF_LE_SET_GUINT64(buf, ZIP_DDESC64_CSIZE, (ulong)dirent.CompressedSize);
|
||||
GSF_LE_SET_GUINT64(buf, ZIP_DDESC64_USIZE, (ulong)dirent.UncompressedSize);
|
||||
size = ZIP_DDESC64_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
GSF_LE_SET_GUINT32(buf, 0, ZIP_DDESC_SIGNATURE);
|
||||
GSF_LE_SET_GUINT32(buf, ZIP_DDESC_CRC32, dirent.CRC32);
|
||||
GSF_LE_SET_GUINT32(buf, ZIP_DDESC_CSIZE, (uint)dirent.CompressedSize);
|
||||
GSF_LE_SET_GUINT32(buf, ZIP_DDESC_USIZE, (uint)dirent.UncompressedSize);
|
||||
size = ZIP_DDESC_SIZE;
|
||||
}
|
||||
|
||||
if (!Sink.Write(size, buf))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool HeaderPatchSizes()
|
||||
{
|
||||
GsfZipDirectoryEntry dirent = VDir.DirectoryEntry;
|
||||
long pos = Sink.CurrentOffset;
|
||||
|
||||
// Rewrite the header in the same location again.
|
||||
bool ok = (Sink.Seek(dirent.Offset, SeekOrigin.Begin)
|
||||
&& HeaderWrite()
|
||||
&& Sink.Seek(pos, SeekOrigin.Begin));
|
||||
|
||||
if (ok && dirent.Zip64 == null)
|
||||
{
|
||||
// We just wrote the final header. Since we still are in
|
||||
// auto-mode, the header did not use a real zip64 extra
|
||||
// field. Hence we don't need such a field.
|
||||
dirent.Zip64 = false;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
private bool CloseStream()
|
||||
{
|
||||
if (!Writing)
|
||||
{
|
||||
if (!InitWrite())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CompressionMethod == GsfZipCompressionMethod.GSF_ZIP_DEFLATED)
|
||||
{
|
||||
if (!Flush())
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((VDir.DirectoryEntry.Flags & ZIP_DIRENT_FLAGS_HAS_DDESC) != 0)
|
||||
{
|
||||
// Write data descriptor
|
||||
if (!DataDescriptorWrite())
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Write crc, sizes
|
||||
if (!HeaderPatchSizes())
|
||||
return false;
|
||||
}
|
||||
|
||||
Root.Writing = false;
|
||||
|
||||
bool result = Unwrap(Sink);
|
||||
|
||||
// Free unneeded memory
|
||||
if (Stream != null)
|
||||
{
|
||||
Stream.deflateEnd();
|
||||
Stream = null;
|
||||
Buf = null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
286
BurnOutSharp/External/libgsf/Output/GsfOutput.cs
vendored
286
BurnOutSharp/External/libgsf/Output/GsfOutput.cs
vendored
@@ -1,286 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-output.c: interface for storing data
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
public abstract class GsfOutput : IDisposable
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public long CurrentSize { get; protected set; } = 0;
|
||||
|
||||
public long CurrentOffset { get; protected set; } = 0;
|
||||
|
||||
public string Name { get; protected set; } = null;
|
||||
|
||||
public object WrappedBy { get; protected set; } = null;
|
||||
|
||||
public GsfOutfile Container { get; protected set; } = null;
|
||||
|
||||
public Exception Error { get; protected set; } = null;
|
||||
|
||||
public bool IsClosed { get; protected set; } = false;
|
||||
|
||||
public string PrintFBuf { get; protected set; } = null;
|
||||
|
||||
public int PrintFBufSize { get; protected set; } = 0;
|
||||
|
||||
public DateTime? ModTime { get; protected set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Close a stream.
|
||||
/// </summary>
|
||||
/// <returns>false on error</returns>
|
||||
public bool Close()
|
||||
{
|
||||
if (IsClosed)
|
||||
return SetError(0, "<internal>");
|
||||
|
||||
// The implementation will log any errors, but we can never try to
|
||||
// close multiple times even on failure.
|
||||
bool res = CloseImpl();
|
||||
IsClosed = true;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reposition in output stream @output. @whence specifies what the offset is
|
||||
/// relative to: the beginning of the stream (SeekOrigin.Begin), current position in
|
||||
/// the stream (SeekOrigin.Current) or the end of the stream (SeekOrigin.End).
|
||||
/// This function is similar to fseek(3)
|
||||
/// </summary>
|
||||
/// <param name="offset">Relative amount to reposition</param>
|
||||
/// <param name="whence">What the offset is relative to.</param>
|
||||
/// <returns>false on error.</returns>
|
||||
public bool Seek(long offset, SeekOrigin whence)
|
||||
{
|
||||
long pos = offset;
|
||||
|
||||
switch (whence)
|
||||
{
|
||||
case SeekOrigin.Begin: break;
|
||||
case SeekOrigin.Current: pos += CurrentOffset; break;
|
||||
case SeekOrigin.End: pos += CurrentSize; break;
|
||||
default:
|
||||
Console.Error.WriteLine($"Invalid seek type {whence}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pos < 0)
|
||||
{
|
||||
Console.Error.WriteLine($"Invalid seek position {pos}, which is before the start of the file");
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we go nowhere, just return. This in particular handles null
|
||||
// seeks for streams with no seek method.
|
||||
if (pos == CurrentOffset)
|
||||
return true;
|
||||
|
||||
if (SeekImpl(offset, whence))
|
||||
{
|
||||
// NOTE : it is possible for the current pos to be beyond the
|
||||
// end of the file. The intervening space is not filled with 0
|
||||
// until something is written.
|
||||
CurrentOffset = pos;
|
||||
return true;
|
||||
}
|
||||
|
||||
// The implementation should have assigned whatever errors are necessary
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write <paramref name="num_bytes"/> of <paramref name="data"/> to output.
|
||||
/// </summary>
|
||||
/// <param name="num_bytes">Number of bytes to write</param>
|
||||
/// <param name="data">Data to write.</param>
|
||||
/// <returns>%false on error.</returns>
|
||||
public bool Write(int num_bytes, byte[] data)
|
||||
{
|
||||
if (num_bytes == 0)
|
||||
return true;
|
||||
|
||||
if (WriteImpl(num_bytes, data))
|
||||
return IncrementCurrentOffset(num_bytes);
|
||||
|
||||
// The implementation should have assigned whatever errors are necessary
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <returns>True if the wrapping succeeded.</returns>
|
||||
public bool Wrap(object wrapper)
|
||||
{
|
||||
if (wrapper == null)
|
||||
return false;
|
||||
|
||||
if (WrappedBy != null)
|
||||
{
|
||||
Console.Error.WriteLine("Attempt to wrap an output that is already wrapped.");
|
||||
return false;
|
||||
}
|
||||
|
||||
WrappedBy = wrapper;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Unwrap(object wrapper)
|
||||
{
|
||||
if (WrappedBy != wrapper)
|
||||
return false;
|
||||
|
||||
WrappedBy = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Output <paramref name="va"/> to output using the format string <paramref name="format"/>, similar to printf(3)
|
||||
/// </summary>
|
||||
/// <param name="format">The printf-style format string</param>
|
||||
/// <param name="va">The arguments for @format</param>
|
||||
/// <returns>True if successful, false if not</returns>
|
||||
public bool PrintF(string format, params string[] va) => VPrintF(format, va) >= 0;
|
||||
|
||||
/// <summary>
|
||||
/// Output <paramref name="args"/> to output using the format string <paramref name="format"/>, similar to vprintf(3)
|
||||
/// </summary>
|
||||
/// <param name="format">The printf-style format string</param>
|
||||
/// <param name="args">The arguments for @format</param>
|
||||
/// <returns>Number of bytes printed, a negative value if not successful</returns>
|
||||
public long VPrintF(string format, params string[] args)
|
||||
{
|
||||
if (format == null)
|
||||
return -1;
|
||||
|
||||
long num_bytes = VPrintFImpl(format, args);
|
||||
if (num_bytes >= 0)
|
||||
{
|
||||
if (!IncrementCurrentOffset(num_bytes))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return num_bytes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Like fputs, this assumes that the line already ends with a newline
|
||||
/// </summary>
|
||||
/// <param name="line">Nul terminated string to write</param>
|
||||
/// <returns>%true if successful, %false if not</returns>
|
||||
public bool PutString(string line)
|
||||
{
|
||||
if (line == null)
|
||||
return false;
|
||||
|
||||
int nbytes = line.Length;
|
||||
return Write(nbytes, Encoding.UTF8.GetBytes($"{line}"));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Virtual Functions
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
if (!IsClosed)
|
||||
{
|
||||
Console.Error.WriteLine("Disposing of an unclosed stream");
|
||||
Close();
|
||||
}
|
||||
|
||||
Container = null;
|
||||
Name = null;
|
||||
ModTime = null;
|
||||
|
||||
PrintFBuf = null;
|
||||
|
||||
Error = null;
|
||||
}
|
||||
|
||||
protected virtual bool CloseImpl() => false;
|
||||
|
||||
protected virtual bool SeekImpl(long offset, SeekOrigin whence) => false;
|
||||
|
||||
protected virtual bool WriteImpl(int num_bytes, byte[] data) => false;
|
||||
|
||||
protected virtual long VPrintFImpl(string format, params string[] args) => -1;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private bool IncrementCurrentOffset(long num_bytes)
|
||||
{
|
||||
CurrentOffset += num_bytes;
|
||||
if (CurrentOffset < num_bytes)
|
||||
return SetError(0, "Output size overflow.");
|
||||
|
||||
if (CurrentSize < CurrentOffset)
|
||||
CurrentSize = CurrentOffset;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <param name="filename">The (fs-sys encoded) filename</param>
|
||||
/// <returns> %true if the assignment was ok.</returns>
|
||||
/// <remarks>This is a utility routine that should only be used by derived outputs.</remarks>
|
||||
protected bool SetNameFromFilename(string filename)
|
||||
{
|
||||
string name = null;
|
||||
if (filename != null)
|
||||
{
|
||||
byte[] filenameBytes = Encoding.Unicode.GetBytes(filename);
|
||||
filenameBytes = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, filenameBytes);
|
||||
name = Encoding.UTF8.GetString(filenameBytes);
|
||||
}
|
||||
|
||||
Name = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <param name="code">The error id</param>
|
||||
/// <param name="format">printf style format string</param>
|
||||
/// <param name="va">arguments for @format</param>
|
||||
/// <returns>Always returns false to facilitate its use.</returns>
|
||||
/// <remarks>This is a utility routine that should only be used by derived outputs.</remarks>
|
||||
protected bool SetError(int code, string format, params string[] va)
|
||||
{
|
||||
Error = null;
|
||||
if (format != null)
|
||||
{
|
||||
string message = string.Format(format, va);
|
||||
Error = new Exception(message); // TODO: How to include `code`?
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
212
BurnOutSharp/External/libgsf/Output/GsfOutputBzip.cs
vendored
212
BurnOutSharp/External/libgsf/Output/GsfOutputBzip.cs
vendored
@@ -1,212 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-output-bzip.c: wrapper to compress to bzipped output
|
||||
*
|
||||
* Copyright (C) 2003-2006 Dom Lachowicz (cinamod@hotmail.com)
|
||||
* 2002-2006 Jon K Hellan (hellan@acm.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
// TODO: Implement BZIP writing
|
||||
public class GsfOutputBzip : GsfOutput
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private const int BZ_BUFSIZE = 1024;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
#if BZIP2
|
||||
/// <summary>
|
||||
/// Compressed data
|
||||
/// </summary>
|
||||
public GsfOutput Sink { get; set; } = null;
|
||||
|
||||
public bz_stream Stream { get; set; } = new bz_stream();
|
||||
|
||||
public byte[] Buf { get; set; } = null;
|
||||
|
||||
public int BufSize { get; set; } = 0;
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfOutputBzip() { }
|
||||
|
||||
/// <param name="sink">The underlying data source.</param>
|
||||
/// <returns>A new file or null.</returns>
|
||||
/// <remarks>Adds a reference to <paramref name="sink"/>.</remarks>
|
||||
public static GsfOutputBzip Create(GsfOutput sink, ref Exception err)
|
||||
{
|
||||
#if BZIP2
|
||||
if (sink == null)
|
||||
return null;
|
||||
|
||||
GsfOutputBzip bzip = new GsfOutputBzip
|
||||
{
|
||||
Sink = sink,
|
||||
};
|
||||
|
||||
if (!InitBzip(ref err))
|
||||
return null;
|
||||
|
||||
return bzip;
|
||||
#else
|
||||
err = new Exception("BZ2 support not enabled");
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool WriteImpl(int num_bytes, byte[] data)
|
||||
{
|
||||
#if BZIP2
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
Stream.next_in = data;
|
||||
Stream.avail_in = num_bytes;
|
||||
|
||||
while (Stream.avail_in > 0)
|
||||
{
|
||||
if (Stream.avail_out == 0)
|
||||
{
|
||||
if (!OutputBlock())
|
||||
return false;
|
||||
}
|
||||
|
||||
int zret = BZ2_bzCompress(&bzip.stream, BZ_RUN);
|
||||
if (zret != BZ_RUN_OK)
|
||||
{
|
||||
Console.Error.WriteLine($"Unexpected error code {zret} from bzlib during compression.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Stream.avail_out == 0)
|
||||
{
|
||||
if (!OutputBlock())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence) => false;
|
||||
|
||||
protected override bool CloseImpl()
|
||||
{
|
||||
#if BZIP2
|
||||
bool rt = Flush();
|
||||
BZ2_bzCompressEnd(Stream);
|
||||
|
||||
return rt;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
#if BZIP2
|
||||
private bool InitBzip(ref Exception err)
|
||||
{
|
||||
int ret = BZ2_bzCompressInit(Stream, 6, 0, 0);
|
||||
|
||||
if (ret != BZ_OK)
|
||||
{
|
||||
err = new Exception("Unable to initialize BZ2 library");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Buf == null)
|
||||
{
|
||||
BufSize = BZ_BUFSIZE;
|
||||
Buf = new byte[BufSize];
|
||||
}
|
||||
|
||||
Stream.next_out = Buf;
|
||||
Stream.avail_out = BufSize;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool OutputBlock()
|
||||
{
|
||||
int num_bytes = BufSize - Stream.avail_out;
|
||||
|
||||
if (!Sink.Write(num_bytes, Buf))
|
||||
return false;
|
||||
|
||||
Stream.next_out = Buf;
|
||||
Stream.avail_out = BufSize;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool Flush()
|
||||
{
|
||||
int zret;
|
||||
|
||||
do
|
||||
{
|
||||
zret = BZ2_bzCompress(Stream, BZ_FINISH);
|
||||
if (zret == BZ_FINISH_OK)
|
||||
{
|
||||
// In this case BZ_FINISH_OK means more buffer space needed
|
||||
if (!OutputBlock())
|
||||
return false;
|
||||
}
|
||||
} while (zret == BZ_FINISH_OK);
|
||||
|
||||
if (zret != BZ_STREAM_END)
|
||||
{
|
||||
Console.Error.WriteLine($"Unexpected error code {zret} from bzlib during compression.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!OutputBlock())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
181
BurnOutSharp/External/libgsf/Output/GsfOutputCsv.cs
vendored
181
BurnOutSharp/External/libgsf/Output/GsfOutputCsv.cs
vendored
@@ -1,181 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-output-csv.c: a GsfOutput to write .csv style files.
|
||||
*
|
||||
* Copyright (C) 2005-2006 Morten Welinder (terra@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
#region Enums
|
||||
|
||||
/// <summary>
|
||||
/// Controls when to add quotes around fields.
|
||||
/// </summary>
|
||||
public enum GsfOutputCsvQuotingMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Never add quotes around fields
|
||||
/// </summary>
|
||||
GSF_OUTPUT_CSV_QUOTING_MODE_NEVER,
|
||||
|
||||
/// <summary>
|
||||
/// Add quotes around fields when needed
|
||||
/// </summary>
|
||||
GSF_OUTPUT_CSV_QUOTING_MODE_AUTO,
|
||||
|
||||
/// <summary>
|
||||
/// Always add quotes around fields
|
||||
/// </summary>
|
||||
GSF_OUTPUT_CSV_QUOTING_MODE_ALWAYS
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Classes
|
||||
|
||||
public class GsfOutputCsv : GsfOutput
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public GsfOutput Sink { get; set; }
|
||||
|
||||
public string Quote { get; set; }
|
||||
|
||||
public GsfOutputCsvQuotingMode QuotingMode { get; set; }
|
||||
|
||||
public string QuotingTriggers { get; set; } = string.Empty;
|
||||
|
||||
public string EndOfLine { get; set; } = "\n";
|
||||
|
||||
public string Separator { get; set; }
|
||||
|
||||
public bool FieldsOnLine { get; set; }
|
||||
|
||||
public string Buf { get; set; } = null;
|
||||
|
||||
public bool? QuotingOnWhitespace { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool WriteImpl(int num_bytes, byte[] data) => Sink.Write(num_bytes, data);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence) => Sink.Seek(offset, whence);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool CloseImpl() => true;
|
||||
|
||||
public bool WriteField(string field, int len)
|
||||
{
|
||||
if (field == null)
|
||||
return false;
|
||||
|
||||
bool quote;
|
||||
bool ok;
|
||||
|
||||
if (len == -1)
|
||||
len = field.Length;
|
||||
|
||||
int end = len;
|
||||
if (FieldsOnLine && Separator.Length != 0)
|
||||
Buf += Separator;
|
||||
|
||||
FieldsOnLine = true;
|
||||
|
||||
switch (QuotingMode)
|
||||
{
|
||||
default:
|
||||
case GsfOutputCsvQuotingMode.GSF_OUTPUT_CSV_QUOTING_MODE_NEVER:
|
||||
quote = false;
|
||||
break;
|
||||
|
||||
case GsfOutputCsvQuotingMode.GSF_OUTPUT_CSV_QUOTING_MODE_ALWAYS:
|
||||
quote = true;
|
||||
break;
|
||||
|
||||
case GsfOutputCsvQuotingMode.GSF_OUTPUT_CSV_QUOTING_MODE_AUTO:
|
||||
{
|
||||
int p = 0; // field[0]
|
||||
quote = false;
|
||||
while (p < end)
|
||||
{
|
||||
if (QuotingTriggers.Contains(field[p].ToString()))
|
||||
{
|
||||
quote = true;
|
||||
break;
|
||||
}
|
||||
|
||||
p++;
|
||||
}
|
||||
|
||||
if (!quote
|
||||
&& field[0] != '\0'
|
||||
&& (char.IsWhiteSpace(field[0]) || char.IsWhiteSpace(field[p - 1]))
|
||||
&& QuotingOnWhitespace != null)
|
||||
{
|
||||
quote = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (quote && Quote.Length > 0)
|
||||
{
|
||||
Buf += quote;
|
||||
int fieldPtr = 0; // field[0]
|
||||
while (fieldPtr < end)
|
||||
{
|
||||
char c = field[fieldPtr];
|
||||
if (this.Quote.Contains(c.ToString()))
|
||||
Buf += this.Quote;
|
||||
|
||||
Buf += c;
|
||||
fieldPtr++;
|
||||
}
|
||||
|
||||
Buf += quote;
|
||||
}
|
||||
else
|
||||
{
|
||||
Buf += field;
|
||||
}
|
||||
|
||||
ok = Sink.Write(Buf.Length, Encoding.UTF8.GetBytes(Buf));
|
||||
Buf = string.Empty;
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
public bool WriteEndOfLine()
|
||||
{
|
||||
FieldsOnLine = false;
|
||||
return Sink.Write(EndOfLine.Length, Encoding.UTF8.GetBytes(EndOfLine));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
298
BurnOutSharp/External/libgsf/Output/GsfOutputGZip.cs
vendored
298
BurnOutSharp/External/libgsf/Output/GsfOutputGZip.cs
vendored
@@ -1,298 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-output-gzip.c: wrapper to compress to gzipped output. See rfc1952.
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jon K Hellan (hellan@acm.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using ComponentAce.Compression.Libs.zlib;
|
||||
using static ComponentAce.Compression.Libs.zlib.zlibConst;
|
||||
using static LibGSF.GsfUtils;
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
public class GsfOutputGZip : GsfOutput
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// GZip flag byte - The original is stored
|
||||
/// </summary>
|
||||
private const byte GZIP_ORIGINAL_NAME = 0x08;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Compressed data
|
||||
/// </summary>
|
||||
public GsfOutput Sink { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// No header and no trailer.
|
||||
/// </summary>
|
||||
public bool Raw { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// zlib compression level
|
||||
/// </summary>
|
||||
public int DeflateLevel { get; set; } = Z_DEFAULT_COMPRESSION;
|
||||
|
||||
public ZStream Stream { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// CRC32 of uncompressed data
|
||||
/// </summary>
|
||||
public uint CRC { get; set; } = 0;
|
||||
|
||||
public int ISize { get; set; } = 0;
|
||||
|
||||
public bool Setup { get; set; } = false;
|
||||
|
||||
public byte[] Buf { get; set; } = null;
|
||||
|
||||
public int BufSize { get; set; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfOutputGZip() { }
|
||||
|
||||
/// <param name="sink">The underlying data source.</param>
|
||||
/// <param name="err">Optionally null.</param>
|
||||
/// <returns>A new file or null</returns>
|
||||
/// <remarks>Adds a reference to <paramref name="sink"/>.</remarks>
|
||||
public static GsfOutputGZip Create(GsfOutput sink, ref Exception err)
|
||||
{
|
||||
if (sink == null)
|
||||
return null;
|
||||
|
||||
GsfOutputGZip output = new GsfOutputGZip
|
||||
{
|
||||
Sink = sink,
|
||||
};
|
||||
|
||||
if (output.Error != null)
|
||||
{
|
||||
err = output.Error;
|
||||
return null;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfOutputGZip()
|
||||
{
|
||||
// FIXME: check for error?
|
||||
Stream.deflateEnd();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool WriteImpl(int num_bytes, byte[] data)
|
||||
{
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
// Write header, if needed
|
||||
SetupImpl();
|
||||
|
||||
Stream.next_in = data;
|
||||
Stream.avail_in = num_bytes;
|
||||
|
||||
while (Stream.avail_in > 0)
|
||||
{
|
||||
if (Stream.avail_out == 0)
|
||||
{
|
||||
if (!OutputBlock())
|
||||
return false;
|
||||
}
|
||||
|
||||
int zret = Stream.deflate(Z_NO_FLUSH);
|
||||
if (zret != Z_OK)
|
||||
{
|
||||
Error = new Exception("Unexpected compression failure");
|
||||
Console.Error.WriteLine($"Unexpected error code {zret} from zlib during compression.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Enable CRC32 calculation
|
||||
//CRC = crc32(gzip.crc, data, num_bytes);
|
||||
ISize += num_bytes;
|
||||
|
||||
if (Stream.avail_out == 0)
|
||||
{
|
||||
if (!OutputBlock())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence) => false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool CloseImpl()
|
||||
{
|
||||
// Just in case nothing was ever written
|
||||
SetupImpl();
|
||||
|
||||
if (Error != null)
|
||||
{
|
||||
if (!Flush())
|
||||
return false;
|
||||
|
||||
if (!Raw)
|
||||
{
|
||||
byte[] buf = new byte[8];
|
||||
|
||||
GSF_LE_SET_GUINT32(buf, 0, CRC);
|
||||
GSF_LE_SET_GUINT32(buf, 4, (uint)(ISize));
|
||||
if (!Sink.Write(8, buf))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private bool InitGZip()
|
||||
{
|
||||
int ret = Stream.deflateInit(DeflateLevel);
|
||||
if (ret != Z_OK)
|
||||
return false;
|
||||
|
||||
ret = Stream.deflateParams(DeflateLevel, Z_DEFAULT_STRATEGY);
|
||||
if (ret != Z_OK)
|
||||
return false;
|
||||
|
||||
if (Buf == null)
|
||||
{
|
||||
BufSize = 0x100;
|
||||
Buf = new byte[BufSize];
|
||||
}
|
||||
|
||||
Stream.next_out = Buf;
|
||||
Stream.avail_out = BufSize;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool OutputHeader()
|
||||
{
|
||||
byte[] buf = new byte[3 + 1 + 4 + 2];
|
||||
|
||||
DateTime? modtime = ModTime;
|
||||
ulong mtime = (ulong)(modtime != null ? new DateTimeOffset(modtime.Value).ToUnixTimeSeconds() : 0);
|
||||
|
||||
string name = Sink.Name;
|
||||
// FIXME: What to do about gz extension ... ?
|
||||
int nlen = 0; // name ? strlen (name) : 0;
|
||||
|
||||
Array.Copy(new byte[] { 0x1f, 0x8b, 0x08 }, buf, 3);
|
||||
if (nlen > 0)
|
||||
buf[3] = GZIP_ORIGINAL_NAME;
|
||||
|
||||
GSF_LE_SET_GUINT32(buf, 4, (uint)mtime);
|
||||
buf[9] = 3; // UNIX
|
||||
bool ret = Sink.Write(buf.Length, buf);
|
||||
if (ret && name != null && nlen > 0)
|
||||
ret = Sink.Write(nlen, Encoding.ASCII.GetBytes(name));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void SetupImpl()
|
||||
{
|
||||
if (Setup)
|
||||
return;
|
||||
|
||||
if (!InitGZip())
|
||||
Error = new Exception("Failed to initialize zlib structure");
|
||||
else if (!Raw && !OutputHeader())
|
||||
Error = new Exception("Failed to write gzip header");
|
||||
|
||||
Setup = true;
|
||||
}
|
||||
|
||||
private bool OutputBlock()
|
||||
{
|
||||
int num_bytes = BufSize - Stream.avail_out;
|
||||
if (!Sink.Write(num_bytes, Buf))
|
||||
{
|
||||
Error = new Exception("Failed to write");
|
||||
return false;
|
||||
}
|
||||
|
||||
Stream.next_out = Buf;
|
||||
Stream.avail_out = BufSize;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool Flush()
|
||||
{
|
||||
int zret;
|
||||
|
||||
do
|
||||
{
|
||||
zret = Stream.deflate(Z_FINISH);
|
||||
if (zret == Z_OK)
|
||||
{
|
||||
// In this case Z_OK means more buffer space needed
|
||||
if (!OutputBlock())
|
||||
return false;
|
||||
}
|
||||
} while (zret == Z_OK);
|
||||
|
||||
if (zret != Z_STREAM_END)
|
||||
{
|
||||
Error = new Exception("Unexpected compression failure");
|
||||
Console.Error.WriteLine($"Unexpected error code {zret} from zlib during compression.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!OutputBlock())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
144
BurnOutSharp/External/libgsf/Output/GsfOutputGio.cs
vendored
144
BurnOutSharp/External/libgsf/Output/GsfOutputGio.cs
vendored
@@ -1,144 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-output-gio.c:
|
||||
*
|
||||
* Copyright (C) 2007 Dom Lachowicz <cinamod@hotmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
public class GsfOutputGio : GsfOutput
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public Stream Stream { get; set; } = null;
|
||||
|
||||
public bool CanSeek { get; set; } = false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfOutputGio() { }
|
||||
|
||||
/// <param name="file">An existing GFile</param>
|
||||
/// <returns>A new GsfOutputGio or null</returns>
|
||||
public static GsfOutputGio Create(string file)
|
||||
{
|
||||
Exception err = null;
|
||||
return Create(file, ref err);
|
||||
}
|
||||
|
||||
/// <param name="file">An existing GFile</param>
|
||||
/// <returns>A new GsfOutputGio or null</returns>
|
||||
public static GsfOutputGio Create(string file, ref Exception err)
|
||||
{
|
||||
if (file == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
Stream stream = File.OpenWrite(file);
|
||||
return new GsfOutputGio
|
||||
{
|
||||
Stream = stream,
|
||||
CanSeek = CanSeekSafe(stream),
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~GsfOutputGio() => Close();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool CloseImpl()
|
||||
{
|
||||
if (Stream != null)
|
||||
{
|
||||
Stream.Close();
|
||||
Stream = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool WriteImpl(int num_bytes, byte[] data)
|
||||
{
|
||||
if (Stream == null)
|
||||
return false;
|
||||
|
||||
if (num_bytes <= 0)
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
Stream.Write(data, 0, num_bytes);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence)
|
||||
{
|
||||
if (Stream == null)
|
||||
return false;
|
||||
if (CanSeek)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
Stream.Seek(offset, whence); ;
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private static bool CanSeekSafe(Stream stream) => stream?.CanSeek ?? false;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-output-iochannel.c
|
||||
*
|
||||
* Copyright (C) 2002-2006 Dom Lachowicz (cinamod@hotmail.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
public class GsfOutputIOChannel : GsfOutput
|
||||
{
|
||||
// TODO: Enable once GIOChannel is converted
|
||||
|
||||
//#region Properties
|
||||
|
||||
//public GIOChannel Channel { get; set; } = null;
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Constructor and Destructor
|
||||
|
||||
///// <returns>A new file or null.</returns>
|
||||
//public static GsfOutputIOChannel Create(GIOChannel channel)
|
||||
//{
|
||||
// if (channel == null)
|
||||
// return null;
|
||||
|
||||
// return new GsfOutputIOChannel
|
||||
// {
|
||||
// Channel = channel,
|
||||
// };
|
||||
//}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Functions
|
||||
|
||||
///// <inheritdoc/>
|
||||
//protected override bool CloseImpl()
|
||||
//{
|
||||
// g_io_channel_shutdown(Channel, true, null);
|
||||
// return true;
|
||||
//}
|
||||
|
||||
///// <inheritdoc/>
|
||||
//protected override bool SeekImpl(long offset, SeekOrigin whence)
|
||||
//{
|
||||
// if (!Channel.IsSeekable)
|
||||
// return false;
|
||||
|
||||
// GIOStatus status = g_io_channel_seek_position(Channel, offset, whence, null);
|
||||
// if (status == G_IO_STATUS_NORMAL)
|
||||
// return true;
|
||||
|
||||
// Error = new Exception($"{status}?");
|
||||
// return false;
|
||||
//}
|
||||
|
||||
///// <inheritdoc/>
|
||||
//protected override bool WriteImpl(int num_bytes, byte[] data)
|
||||
//{
|
||||
// GIOStatus status = G_IO_STATUS_NORMAL;
|
||||
// int bytes_written = 0, total_written = 0;
|
||||
|
||||
// while ((status == G_IO_STATUS_NORMAL) && (total_written < num_bytes))
|
||||
// {
|
||||
// status = g_io_channel_write_chars(Channel, data + total_written, num_bytes - total_written, ref bytes_written, null);
|
||||
// total_written += bytes_written;
|
||||
// }
|
||||
|
||||
// return (status == G_IO_STATUS_NORMAL && total_written == num_bytes);
|
||||
//}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-output-iconv.c: wrapper to convert character sets.
|
||||
*
|
||||
* Copyright (C) 2005-2006 Morten Welinder (terra@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
public class GsfOutputIconv : GsfOutput
|
||||
{
|
||||
#region Constants
|
||||
|
||||
public const int BUF_SIZE = 0x400;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public GsfOutput Sink { get; set; }
|
||||
|
||||
public Encoding InputCharset { get; set; }
|
||||
|
||||
public Encoding OutputCharset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Either null or a UTF-8 string (representable in the target encoding)
|
||||
/// to convert and output in place of characters that cannot be represented
|
||||
/// in the target encoding. null means use \u1234 or \U12345678 format.
|
||||
/// </summary>
|
||||
public string Fallback { get; set; }
|
||||
|
||||
public byte[] Buf { get; set; } = new byte[BUF_SIZE];
|
||||
|
||||
public int BufLen { get; set; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfOutputIconv() { }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="sink">The underlying data source.</param>
|
||||
/// <param name="dst">The target character set.</param>
|
||||
/// <param name="src">he source character set.</param>
|
||||
/// <returns>A new GsfOutput object or null.</returns>
|
||||
/// <remarks>Adds a reference to <paramref name="sink"/>.</remarks>
|
||||
public static GsfOutputIconv Create(GsfOutput sink, Encoding dst, Encoding src)
|
||||
{
|
||||
if (sink == null)
|
||||
return null;
|
||||
|
||||
if (dst == null)
|
||||
dst = Encoding.UTF8;
|
||||
if (src == null)
|
||||
src = Encoding.UTF8;
|
||||
|
||||
return new GsfOutputIconv
|
||||
{
|
||||
Sink = sink,
|
||||
InputCharset = src,
|
||||
OutputCharset = dst,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool WriteImpl(int num_bytes, byte[] data)
|
||||
{
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
int dataPtr = 0; // data[0]
|
||||
while (num_bytes > 0)
|
||||
{
|
||||
if (Error != null)
|
||||
return false;
|
||||
|
||||
if (BufLen == BUF_SIZE)
|
||||
{
|
||||
Flush(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
int count = Math.Min(BUF_SIZE - BufLen, num_bytes);
|
||||
Array.Copy(data, dataPtr, Buf, BufLen, count);
|
||||
BufLen += count;
|
||||
num_bytes -= count;
|
||||
dataPtr += count;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence) => false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool CloseImpl()
|
||||
{
|
||||
if (Error != null)
|
||||
return true;
|
||||
|
||||
return Flush(true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private bool Flush(bool must_empty)
|
||||
{
|
||||
if (Error != null)
|
||||
return false;
|
||||
|
||||
if (BufLen <= 0)
|
||||
return true;
|
||||
|
||||
bool ok = true;
|
||||
|
||||
byte[] data = Encoding.Convert(InputCharset, OutputCharset, Buf, 0, BufLen);
|
||||
if (data == null || data.Length <= 0)
|
||||
{
|
||||
Error = new Exception("Failed to convert string");
|
||||
ok = false;
|
||||
}
|
||||
else if (!Sink.Write(data.Length, data))
|
||||
{
|
||||
Error = new Exception("Failed to write");
|
||||
ok = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
BufLen = 0;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
return ok && (!must_empty || BufLen == 0);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-output-memory.c:
|
||||
*
|
||||
* Copyright (C) 2002-2006 Dom Lachowicz (cinamod@hotmail.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
public class GsfOutputMemory : GsfOutput
|
||||
{
|
||||
#region Constants
|
||||
|
||||
public const int MIN_BLOCK = 512;
|
||||
|
||||
public const int MAX_STEP = MIN_BLOCK * 128;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public byte[] Buffer { get; set; } = null;
|
||||
|
||||
public int Capacity { get; set; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfOutputMemory() { }
|
||||
|
||||
/// <returns>A new file.</returns>
|
||||
public static GsfOutputMemory Create() => new GsfOutputMemory();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool CloseImpl() => true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence) => true;
|
||||
|
||||
public bool Expand(long needed)
|
||||
{
|
||||
// If we need >= MAX_STEP, align to a next multiple of MAX_STEP.
|
||||
// Since MAX_STEP is probably a power of two, this computation
|
||||
// should reduce to "dec, shr, inc, shl", which is probably
|
||||
// quicker then branching.
|
||||
|
||||
long capacity = Math.Max(Capacity, MIN_BLOCK);
|
||||
|
||||
if (needed < MAX_STEP)
|
||||
{
|
||||
while (capacity < needed)
|
||||
capacity *= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
capacity = ((needed - 1) / MAX_STEP + 1) * MAX_STEP;
|
||||
}
|
||||
|
||||
// Check for overflow: g_renew() casts its parameters to int.
|
||||
int lcapacity = (int)capacity;
|
||||
if ((long)lcapacity != capacity || capacity < 0)
|
||||
{
|
||||
Console.Error.WriteLine("Overflow in Expand");
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] tempBuffer = new byte[lcapacity];
|
||||
Array.Copy(Buffer, tempBuffer, Buffer.Length);
|
||||
Buffer = tempBuffer;
|
||||
Capacity = lcapacity;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool WriteImpl(int num_bytes, byte[] data)
|
||||
{
|
||||
if (Buffer == null)
|
||||
{
|
||||
Buffer = new byte[MIN_BLOCK];
|
||||
Capacity = MIN_BLOCK;
|
||||
}
|
||||
|
||||
if (num_bytes + CurrentOffset > Capacity)
|
||||
{
|
||||
if (!Expand(CurrentOffset + num_bytes))
|
||||
return false;
|
||||
}
|
||||
|
||||
Array.Copy(data, 0, Buffer, CurrentOffset, num_bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override long VPrintFImpl(string format, params string[] args)
|
||||
{
|
||||
if (Buffer != null)
|
||||
{
|
||||
byte[] temp = Encoding.UTF8.GetBytes(string.Format(format, args));
|
||||
long len = Math.Min(temp.Length, Capacity - CurrentOffset);
|
||||
Array.Copy(temp, 0, Buffer, CurrentOffset, len);
|
||||
|
||||
// There was insufficient space
|
||||
if (temp.Length >= len)
|
||||
len = base.VPrintFImpl(format, args);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
return base.VPrintFImpl(format, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The data that has been written to mem.
|
||||
/// The caller takes ownership and the buffer belonging to mem is set
|
||||
/// to NULL.
|
||||
/// </returns>
|
||||
public byte[] StealBytes()
|
||||
{
|
||||
byte[] bytes = Buffer;
|
||||
Buffer = null;
|
||||
Capacity = 0;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,260 +0,0 @@
|
||||
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||
/*
|
||||
* gsf-output-stdio.c: stdio based output
|
||||
*
|
||||
* Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace LibGSF.Output
|
||||
{
|
||||
public class GsfOutputStdio : GsfOutput
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private static int W_OK = 2;
|
||||
|
||||
private static int GSF_MAX_LINK_LEVEL = 256;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public FileStream FileStream { get; set; } = null;
|
||||
|
||||
public string RealFilename { get; set; }
|
||||
|
||||
public string TempFilename { get; set; }
|
||||
|
||||
public bool CreateBackupCopy { get; set; } = false;
|
||||
|
||||
public bool KeepOpen { get; set; } = false;
|
||||
|
||||
public FileInfo Stat { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private GsfOutputStdio() { }
|
||||
|
||||
public static GsfOutputStdio Create(string filename, ref Exception err)
|
||||
{
|
||||
try
|
||||
{
|
||||
FileStream fs = File.OpenWrite(filename);
|
||||
return new GsfOutputStdio
|
||||
{
|
||||
FileStream = fs,
|
||||
RealFilename = filename,
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
err = ex;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool CloseImpl()
|
||||
{
|
||||
bool res;
|
||||
string backup_filename = null;
|
||||
|
||||
if (FileStream == null)
|
||||
return false;
|
||||
|
||||
if (Error != null)
|
||||
{
|
||||
res = true;
|
||||
if (!KeepOpen && !CloseFileHelper(false))
|
||||
res = false;
|
||||
|
||||
if (!UnlinkFileHelper())
|
||||
res = false;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
if (KeepOpen)
|
||||
{
|
||||
FileStream.Flush();
|
||||
FileStream = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
res = CloseFileHelper(true);
|
||||
|
||||
// short circuit our when dealing with raw FILE
|
||||
if (RealFilename == null)
|
||||
return res;
|
||||
|
||||
if (!res)
|
||||
{
|
||||
UnlinkFileHelper();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Move the original file to a backup
|
||||
if (CreateBackupCopy)
|
||||
{
|
||||
backup_filename = $"{RealFilename}.bak";
|
||||
int result = RenameWrapper(RealFilename, backup_filename);
|
||||
if (result != 0)
|
||||
{
|
||||
Error = new Exception($"Could not backup the original as {backup_filename}.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Move the temp file to the original file
|
||||
if (RenameWrapper(TempFilename, RealFilename) != 0)
|
||||
{
|
||||
Error = new Exception();
|
||||
return false;
|
||||
}
|
||||
|
||||
DateTime? modtime = ModTime;
|
||||
if (modtime != null)
|
||||
new FileInfo(RealFilename).LastWriteTime = modtime.Value;
|
||||
|
||||
// Restore permissions. There is not much error checking we
|
||||
// can do here, I'm afraid. The final data is saved anyways.
|
||||
// Note the order: mode, uid+gid, gid, uid, mode.
|
||||
new FileInfo(RealFilename).Attributes = Stat.Attributes;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence)
|
||||
{
|
||||
if (FileStream == null)
|
||||
return SetError(0, "Missing file");
|
||||
|
||||
if (!FileStream.CanSeek)
|
||||
return SetError(0, "Stream can't seek");
|
||||
|
||||
try
|
||||
{
|
||||
FileStream.Seek(offset, whence);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return SetError(0, $"Stream can't seek to {offset} from {whence}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool WriteImpl(int num_bytes, byte[] data)
|
||||
{
|
||||
if (FileStream == null)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
FileStream.Write(data, 0, num_bytes);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return SetError(0, $"Stream can't write {num_bytes}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
protected override long VPrintFImpl(string format, params string[] args)
|
||||
{
|
||||
if (FileStream == null)
|
||||
return -1;
|
||||
|
||||
string temp = string.Format(format, args);
|
||||
byte[] tempBytes = Encoding.UTF8.GetBytes(temp);
|
||||
FileStream.Write(tempBytes, 0, tempBytes.Length);
|
||||
return temp.Length;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private static int RenameWrapper(string oldfilename, string newfilename)
|
||||
{
|
||||
try
|
||||
{
|
||||
System.IO.File.Move(oldfilename, newfilename);
|
||||
return 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static string FollowSymlinks(string filename, ref Exception error)
|
||||
{
|
||||
FileAttributes fa = System.IO.File.GetAttributes(filename);
|
||||
while (fa.HasFlag(FileAttributes.ReparsePoint))
|
||||
{
|
||||
// TODO: This should actually try to follow links
|
||||
break;
|
||||
}
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
private bool CloseFileHelper(bool seterr)
|
||||
{
|
||||
try
|
||||
{
|
||||
FileStream.Close();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
Error = new Exception($"Failed to close file: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
FileStream = null;
|
||||
}
|
||||
}
|
||||
|
||||
private bool UnlinkFileHelper()
|
||||
{
|
||||
if (TempFilename == null)
|
||||
return true;
|
||||
|
||||
// TODO: This should actually try to unlink
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
10
BurnOutSharp/External/libgsf/README.md
vendored
10
BurnOutSharp/External/libgsf/README.md
vendored
@@ -1,10 +0,0 @@
|
||||
# LibMSPackSharp
|
||||
|
||||
<!-- [](https://ci.appveyor.com/project/mnadareski/libmspacksharp) -->
|
||||
|
||||
|
||||
C# port of the GNOME I/O [libgsf](https://github.com/GNOME/libgsf/tree/master/gsf) plus cleanup and commenting. This currently compiles as a library so it can be used in any C# application.
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions to the project are welcome. Please follow the current coding styles and please do not add any keys or legally dubious things to the code.
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using LibGSF.Input;
|
||||
|
||||
namespace LibMSI.Internal
|
||||
{
|
||||
internal class LibmsiIStream : Stream
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public GsfInput Input { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private LibmsiIStream() { }
|
||||
|
||||
public static LibmsiIStream Create(GsfInput input)
|
||||
{
|
||||
Exception err = null;
|
||||
GsfInput dup = input.Duplicate(ref err);
|
||||
if (dup == null)
|
||||
return null;
|
||||
|
||||
return new LibmsiIStream
|
||||
{
|
||||
Input = dup,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public override long Seek(long offset, SeekOrigin type) => (Input?.Seek(offset, type) ?? false) ? offset : -1;
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int remaining = (int)(Input?.Remaining() ?? 0);
|
||||
if (remaining == 0)
|
||||
return 0;
|
||||
|
||||
count = Math.Min(count, remaining);
|
||||
if (Input.Read(count, buffer, offset) == null)
|
||||
return -1;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public override void Close() { }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stream Implementation
|
||||
|
||||
public override bool CanRead => true;
|
||||
|
||||
public override bool CanSeek => true;
|
||||
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override long Length => Input.Size;
|
||||
|
||||
public override long Position { get => Input.CurrentOffset; set => Input.CurrentOffset = value; }
|
||||
|
||||
public override void Flush() { }
|
||||
|
||||
public override void SetLength(long value) => throw new NotImplementedException();
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count) => throw new NotImplementedException();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
2011
BurnOutSharp/External/libmsi/Internal/LibmsiSQLInput.cs
vendored
2011
BurnOutSharp/External/libmsi/Internal/LibmsiSQLInput.cs
vendored
File diff suppressed because it is too large
Load Diff
1593
BurnOutSharp/External/libmsi/Internal/LibmsiTable.cs
vendored
1593
BurnOutSharp/External/libmsi/Internal/LibmsiTable.cs
vendored
File diff suppressed because it is too large
Load Diff
100
BurnOutSharp/External/libmsi/Internal/MsiPriv.cs
vendored
100
BurnOutSharp/External/libmsi/Internal/MsiPriv.cs
vendored
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Implementation of the Microsoft Installer (msi.dll)
|
||||
*
|
||||
* Copyright 2002-2005 Mike McCormack for CodeWeavers
|
||||
* Copyright 2005 Aric Stewart for CodeWeavers
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
namespace LibMSI.Internal
|
||||
{
|
||||
#region Enums
|
||||
|
||||
internal enum LibmsiCondition
|
||||
{
|
||||
LIBMSI_CONDITION_FALSE = 0,
|
||||
LIBMSI_CONDITION_TRUE = 1,
|
||||
LIBMSI_CONDITION_NONE = 2,
|
||||
LIBMSI_CONDITION_ERROR = 3,
|
||||
}
|
||||
|
||||
internal enum LibmsiOLEVariantType
|
||||
{
|
||||
OLEVT_EMPTY = 0,
|
||||
OLEVT_NULL = 1,
|
||||
OLEVT_I2 = 2,
|
||||
OLEVT_I4 = 3,
|
||||
OLEVT_LPSTR = 30,
|
||||
OLEVT_FILETIME = 64,
|
||||
}
|
||||
|
||||
internal enum StringPersistence
|
||||
{
|
||||
StringPersistent = 0,
|
||||
StringNonPersistent = 1
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Delegates
|
||||
|
||||
internal delegate LibmsiResult record_func(LibmsiRecord record, object o);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Classes
|
||||
|
||||
internal static class MsiPriv
|
||||
{
|
||||
#region Constants
|
||||
|
||||
public const ushort MSI_DATASIZEMASK = 0x00ff;
|
||||
public const ushort MSITYPE_VALID = 0x0100;
|
||||
public const ushort MSITYPE_LOCALIZABLE = 0x200;
|
||||
public const ushort MSITYPE_STRING = 0x0800;
|
||||
public const ushort MSITYPE_NULLABLE = 0x1000;
|
||||
public const ushort MSITYPE_KEY = 0x2000;
|
||||
public const ushort MSITYPE_TEMPORARY = 0x4000;
|
||||
public const ushort MSITYPE_UNKNOWN = 0x8000;
|
||||
|
||||
public const int MAX_STREAM_NAME_LEN = 62;
|
||||
public const int LONG_STR_BYTES = 3;
|
||||
|
||||
public const int MSI_INITIAL_MEDIA_TRANSFORM_OFFSET = 10000;
|
||||
public const int MSI_INITIAL_MEDIA_TRANSFORM_DISKID = 30000;
|
||||
|
||||
public const int MSI_MAX_PROPS = 20;
|
||||
|
||||
/* common strings */
|
||||
public static readonly string szEmpty = "";
|
||||
public static readonly string szStreams = "_Streams";
|
||||
public static readonly string szStorages = "_Storages";
|
||||
public static readonly string szStringData = "_StringData";
|
||||
public static readonly string szStringPool = "_StringPool";
|
||||
public static readonly string szName = "Name";
|
||||
public static readonly string szData = "Data";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public static bool MSITYPE_IS_BINARY(int type) => ((type & ~MSITYPE_NULLABLE) == (MSITYPE_STRING | MSITYPE_VALID));
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
684
BurnOutSharp/External/libmsi/Internal/StringTable.cs
vendored
684
BurnOutSharp/External/libmsi/Internal/StringTable.cs
vendored
@@ -1,684 +0,0 @@
|
||||
/*
|
||||
* String Table Functions
|
||||
*
|
||||
* Copyright 2002-2004, Mike McCormack for CodeWeavers
|
||||
* Copyright 2007 Robert Shearman for CodeWeavers
|
||||
* Copyright 2010 Hans Leidekker for CodeWeavers
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using LibGSF.Input;
|
||||
using static LibMSI.Internal.LibmsiTable;
|
||||
using static LibMSI.Internal.MsiPriv;
|
||||
|
||||
namespace LibMSI.Internal
|
||||
{
|
||||
internal class msistring
|
||||
{
|
||||
public ushort PersistentRefCount { get; set; }
|
||||
|
||||
public ushort NonPersistentRefCount { get; set; }
|
||||
|
||||
public string Str { get; set; }
|
||||
}
|
||||
|
||||
internal class StringTable
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private const int CP_ACP = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// The number of strings
|
||||
/// </summary>
|
||||
public int MaxCount { get; set; }
|
||||
|
||||
public int FreeSlot { get; set; }
|
||||
|
||||
public int CodePage { get; set; }
|
||||
|
||||
public int SortCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// An array of strings
|
||||
/// </summary>
|
||||
public msistring[] Strings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Index
|
||||
/// </summary>
|
||||
public int[] Sorted { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
Strings = null;
|
||||
Sorted = null;
|
||||
}
|
||||
|
||||
public int AddString(string data, int len, ushort refcount, StringPersistence persistence)
|
||||
{
|
||||
if (string.IsNullOrEmpty(data) || data[0] == '\0')
|
||||
return 0;
|
||||
|
||||
if (IdFromStringUTF8(data, out int n) == LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
if (persistence == StringPersistence.StringPersistent)
|
||||
Strings[n].PersistentRefCount += refcount;
|
||||
else
|
||||
Strings[n].NonPersistentRefCount += refcount;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
n = FindFreeEntry();
|
||||
if (n == -1)
|
||||
return -1;
|
||||
|
||||
// Allocate a new string
|
||||
if (len < 0)
|
||||
len = data.Length;
|
||||
|
||||
SetEntry(n, data + '\0', refcount, persistence);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the string identified by an id - return null if there's none
|
||||
/// </summary>
|
||||
public string LookupId(int id)
|
||||
{
|
||||
if (id == 0)
|
||||
return szEmpty;
|
||||
|
||||
if (id >= MaxCount)
|
||||
return null;
|
||||
|
||||
if (id != 0 && Strings[id].PersistentRefCount == 0 && Strings[id].NonPersistentRefCount == 0)
|
||||
return null;
|
||||
|
||||
return Strings[id].Str;
|
||||
}
|
||||
|
||||
/// <param name="str">String to find in the string table</param>
|
||||
/// <param name="id">Id of the string, if found</param>
|
||||
public LibmsiResult IdFromStringUTF8(string str, out int id)
|
||||
{
|
||||
int low = 0, high = SortCount - 1;
|
||||
|
||||
while (low <= high)
|
||||
{
|
||||
int i = (low + high) / 2;
|
||||
int c = str.CompareTo(Strings[Sorted[i]].Str);
|
||||
|
||||
if (c < 0)
|
||||
{
|
||||
high = i - 1;
|
||||
}
|
||||
else if (c > 0)
|
||||
{
|
||||
low = i + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
id = Sorted[i];
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
id = 0;
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
public static StringTable InitStringTable(out int bytes_per_strref)
|
||||
{
|
||||
bytes_per_strref = sizeof(ushort);
|
||||
return InitStringTable(1, CP_ACP);
|
||||
}
|
||||
|
||||
public static StringTable LoadStringTable(GsfInfile stg, out int bytes_per_strref)
|
||||
{
|
||||
int len;
|
||||
ushort refs;
|
||||
|
||||
bytes_per_strref = 0;
|
||||
LibmsiResult r = ReadStreamData(stg, szStringPool, out byte[] poolBytes, out int poolsize);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return null;
|
||||
|
||||
r = ReadStreamData(stg, szStringData, out byte[] data, out int datasize);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return null;
|
||||
|
||||
if ((poolsize > 4) && (BitConverter.ToUInt16(poolBytes, 2) & 0x8000) != 0)
|
||||
bytes_per_strref = LONG_STR_BYTES;
|
||||
else
|
||||
bytes_per_strref = sizeof(ushort);
|
||||
|
||||
int count = poolsize / 4;
|
||||
|
||||
int codepage;
|
||||
if (poolsize > 4)
|
||||
codepage = BitConverter.ToUInt16(poolBytes, 0) | ((BitConverter.ToUInt16(poolBytes, 2) & ~0x8000) << 16);
|
||||
else
|
||||
codepage = CP_ACP;
|
||||
|
||||
StringTable st = InitStringTable(count, codepage);
|
||||
if (st == null)
|
||||
return null;
|
||||
|
||||
int offset = 0;
|
||||
int n = 1;
|
||||
int i = 1;
|
||||
while (i < count)
|
||||
{
|
||||
// The string reference count is always the second word
|
||||
refs = BitConverter.ToUInt16(poolBytes, ((i * 2 + 1) * 2));
|
||||
|
||||
// Empty entries have two zeros, still have a string id
|
||||
if (BitConverter.ToUInt16(poolBytes, ((i * 2) * 2)) == 0 && refs == 0)
|
||||
{
|
||||
i++;
|
||||
n++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If a string is over 64k, the previous string entry is made null
|
||||
// and the high word of the length is inserted in the null string's
|
||||
// reference count field.
|
||||
|
||||
if (BitConverter.ToUInt16(poolBytes, ((i * 2) * 2)) == 0)
|
||||
{
|
||||
len = (BitConverter.ToUInt16(poolBytes, ((i * 2 + 3 * 2))) << 16) + BitConverter.ToUInt16(poolBytes, ((i * 2 + 2) * 2));
|
||||
i += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
len = BitConverter.ToUInt16(poolBytes, ((i * 2) * 2));
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if ((offset + len) > datasize)
|
||||
{
|
||||
Console.Error.WriteLine("String table corrupt?");
|
||||
break;
|
||||
}
|
||||
|
||||
int s = st.AddString(n, data, offset, len, refs, StringPersistence.StringPersistent);
|
||||
if (s != n)
|
||||
Console.Error.WriteLine($"Failed to add string {n}");
|
||||
|
||||
n++;
|
||||
offset += len;
|
||||
}
|
||||
|
||||
if (datasize != offset)
|
||||
Console.Error.WriteLine($"String table load failed! ({datasize} != {offset}), please report");
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
public LibmsiResult SaveStringTable(LibmsiDatabase db, out int bytes_per_strref)
|
||||
{
|
||||
// Construct the new table in memory first
|
||||
StringTotalSize(out int datasize, out int poolsize);
|
||||
|
||||
byte[] data = new byte[datasize];
|
||||
byte[] pool = new byte[poolsize];
|
||||
|
||||
int used = 0;
|
||||
int codepage = CodePage;
|
||||
pool[0] = (byte)(codepage & 0xff);
|
||||
pool[1] = (byte)(codepage >> 8);
|
||||
pool[2] = (byte)(codepage >> 16);
|
||||
pool[3] = (byte)(codepage >> 24);
|
||||
|
||||
if (MaxCount > 0xffff)
|
||||
{
|
||||
pool[3] |= 0x80;
|
||||
bytes_per_strref = LONG_STR_BYTES;
|
||||
}
|
||||
else
|
||||
{
|
||||
bytes_per_strref = sizeof(ushort);
|
||||
}
|
||||
|
||||
int i = 1;
|
||||
for (int n = 1; n < MaxCount; n++)
|
||||
{
|
||||
if (Strings[n].PersistentRefCount == 0)
|
||||
{
|
||||
pool[i * 4 + 0] = 0;
|
||||
pool[i * 4 + 1] = 0;
|
||||
pool[i * 4 + 2] = 0;
|
||||
pool[i * 4 + 3] = 0;
|
||||
i++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
int sz = datasize - used;
|
||||
|
||||
|
||||
LibmsiResult s = StringId(n, ref data, ref used, ref sz);
|
||||
if (s != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
Console.Error.WriteLine("Failed to fetch string");
|
||||
sz = 0;
|
||||
}
|
||||
|
||||
if (sz == 0)
|
||||
{
|
||||
pool[i * 4 + 0] = 0;
|
||||
pool[i * 4 + 1] = 0;
|
||||
pool[i * 4 + 2] = 0;
|
||||
pool[i * 4 + 3] = 0;
|
||||
i++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sz >= 0x10000)
|
||||
{
|
||||
// Write a dummy entry, with the high part of the length
|
||||
// in the reference count.
|
||||
pool[i * 4 + 0] = 0;
|
||||
pool[i * 4 + 1] = 0;
|
||||
pool[i * 4 + 2] = (byte)(sz >> 16);
|
||||
pool[i * 4 + 3] = (byte)(sz >> 24);
|
||||
i++;
|
||||
}
|
||||
|
||||
pool[i * 4 + 0] = (byte)sz;
|
||||
pool[i * 4 + 1] = (byte)(sz >> 8);
|
||||
pool[i * 4 + 2] = (byte)Strings[n].PersistentRefCount;
|
||||
pool[i * 4 + 3] = (byte)(Strings[n].PersistentRefCount >> 8);
|
||||
i++;
|
||||
|
||||
used += sz;
|
||||
if (used > datasize)
|
||||
{
|
||||
Console.Error.WriteLine($"Oops overran {used} >= {datasize}");
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
if (used != datasize)
|
||||
{
|
||||
Console.Error.WriteLine($"Oops used {used} != datasize {datasize}");
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
// Write the streams
|
||||
LibmsiResult r = WriteStreamData(db, szStringData, data, datasize);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
r = WriteStreamData(db, szStringPool, pool, poolsize);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
public int GetCodePage() => CodePage;
|
||||
|
||||
public LibmsiResult SetCodePage(int codepage)
|
||||
{
|
||||
if (ValidateCodePage(codepage))
|
||||
{
|
||||
CodePage = codepage;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private static bool ValidateCodePage(int codepage)
|
||||
{
|
||||
switch (codepage)
|
||||
{
|
||||
case CP_ACP:
|
||||
case 37:
|
||||
case 424:
|
||||
case 437:
|
||||
case 500:
|
||||
case 737:
|
||||
case 775:
|
||||
case 850:
|
||||
case 852:
|
||||
case 855:
|
||||
case 856:
|
||||
case 857:
|
||||
case 860:
|
||||
case 861:
|
||||
case 862:
|
||||
case 863:
|
||||
case 864:
|
||||
case 865:
|
||||
case 866:
|
||||
case 869:
|
||||
case 874:
|
||||
case 875:
|
||||
case 878:
|
||||
case 932:
|
||||
case 936:
|
||||
case 949:
|
||||
case 950:
|
||||
case 1006:
|
||||
case 1026:
|
||||
case 1250:
|
||||
case 1251:
|
||||
case 1252:
|
||||
case 1253:
|
||||
case 1254:
|
||||
case 1255:
|
||||
case 1256:
|
||||
case 1257:
|
||||
case 1258:
|
||||
case 1361:
|
||||
case 10000:
|
||||
case 10006:
|
||||
case 10007:
|
||||
case 10029:
|
||||
case 10079:
|
||||
case 10081:
|
||||
case 20127:
|
||||
case 20866:
|
||||
case 20932:
|
||||
case 21866:
|
||||
case 28591:
|
||||
case 28592:
|
||||
case 28593:
|
||||
case 28594:
|
||||
case 28595:
|
||||
case 28596:
|
||||
case 28597:
|
||||
case 28598:
|
||||
case 28599:
|
||||
case 28600:
|
||||
case 28603:
|
||||
case 28604:
|
||||
case 28605:
|
||||
case 28606:
|
||||
case 65000:
|
||||
case 65001:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static StringTable InitStringTable(int entries, int codepage)
|
||||
{
|
||||
if (!ValidateCodePage(codepage))
|
||||
return null;
|
||||
|
||||
if (entries < 1)
|
||||
entries = 1;
|
||||
|
||||
return new StringTable
|
||||
{
|
||||
Strings = new msistring[entries],
|
||||
Sorted = new int[entries],
|
||||
MaxCount = entries,
|
||||
FreeSlot = 1,
|
||||
CodePage = codepage,
|
||||
SortCount = 0,
|
||||
};
|
||||
}
|
||||
|
||||
private int FindFreeEntry()
|
||||
{
|
||||
if (FreeSlot != 0)
|
||||
{
|
||||
for (int i = FreeSlot; i < MaxCount; i++)
|
||||
{
|
||||
if (Strings[i].PersistentRefCount == 0 && Strings[i].NonPersistentRefCount == 0)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 1; i < MaxCount; i++)
|
||||
{
|
||||
if (Strings[i].PersistentRefCount == 0 && Strings[i].NonPersistentRefCount == 0)
|
||||
return i;
|
||||
}
|
||||
|
||||
// Dynamically resize
|
||||
int sz = MaxCount + 1 + MaxCount / 2;
|
||||
|
||||
msistring[] p = Strings;
|
||||
Array.Resize(ref p, sz);
|
||||
Strings = p;
|
||||
|
||||
int[] s = Sorted;
|
||||
Array.Resize(ref s, sz);
|
||||
Sorted = s;
|
||||
|
||||
FreeSlot = MaxCount;
|
||||
MaxCount = sz;
|
||||
|
||||
if (Strings[FreeSlot].PersistentRefCount != 0 || Strings[FreeSlot].NonPersistentRefCount != 0)
|
||||
Console.Error.WriteLine("Oops. expected freeslot to be free...");
|
||||
|
||||
return FreeSlot;
|
||||
}
|
||||
|
||||
private int FindInsertIndex(int string_id)
|
||||
{
|
||||
int low = 0, high = SortCount - 1;
|
||||
|
||||
while (low <= high)
|
||||
{
|
||||
int i = (low + high) / 2;
|
||||
int c = Strings[string_id].Str.CompareTo(Strings[Sorted[i]].Str);
|
||||
|
||||
if (c < 0)
|
||||
high = i - 1;
|
||||
else if (c > 0)
|
||||
low = i + 1;
|
||||
else
|
||||
return -1; // Already exists
|
||||
}
|
||||
|
||||
return high + 1;
|
||||
}
|
||||
|
||||
private void InsertStringSorted(int string_id)
|
||||
{
|
||||
int i = FindInsertIndex(string_id);
|
||||
if (i == -1)
|
||||
return;
|
||||
|
||||
int[] temp = new int[SortCount - i];
|
||||
Array.Copy(Sorted, i, temp, 0, SortCount - i);
|
||||
Array.Copy(temp, 0, Sorted, i + 1, SortCount - i);
|
||||
Sorted[i] = string_id;
|
||||
SortCount++;
|
||||
}
|
||||
|
||||
private void SetEntry(int n, string str, ushort refcount, StringPersistence persistence)
|
||||
{
|
||||
if (str == null)
|
||||
return;
|
||||
|
||||
if (Strings[n] == null)
|
||||
Strings[n] = new msistring();
|
||||
|
||||
if (persistence == StringPersistence.StringPersistent)
|
||||
{
|
||||
Strings[n].PersistentRefCount = refcount;
|
||||
Strings[n].NonPersistentRefCount = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Strings[n].PersistentRefCount = 0;
|
||||
Strings[n].NonPersistentRefCount = refcount;
|
||||
}
|
||||
|
||||
Strings[n].Str = str;
|
||||
|
||||
InsertStringSorted(n);
|
||||
|
||||
if (n < MaxCount)
|
||||
FreeSlot = n + 1;
|
||||
}
|
||||
|
||||
private LibmsiResult IdFromString(byte[] buffer, int offset, out int id)
|
||||
{
|
||||
id = 0;
|
||||
if (buffer == null || buffer[offset] == 0)
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
|
||||
int codepage = CodePage != 0 ? CodePage : Encoding.Default.CodePage;
|
||||
Encoding cpconv = Encoding.GetEncoding(codepage);
|
||||
string str = cpconv.GetString(new ReadOnlySpan<byte>(buffer, offset, buffer.Length - offset).ToArray());
|
||||
if (str == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
LibmsiResult r = IdFromStringUTF8(str, out id);
|
||||
return r;
|
||||
}
|
||||
|
||||
private int AddString(int n, byte[] data, int offset, int len, ushort refcount, StringPersistence persistence)
|
||||
{
|
||||
if (data == null || data[offset] == '\0')
|
||||
return 0;
|
||||
|
||||
if (n > 0)
|
||||
{
|
||||
if (Strings[n] != null && (Strings[n].PersistentRefCount != 0 || Strings[n].NonPersistentRefCount != 0))
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IdFromString(data, offset, out n) == LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
if (Strings[n] == null)
|
||||
Strings[n] = new msistring();
|
||||
|
||||
if (persistence == StringPersistence.StringPersistent)
|
||||
Strings[n].PersistentRefCount += refcount;
|
||||
else
|
||||
Strings[n].NonPersistentRefCount += refcount;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
n = FindFreeEntry();
|
||||
if (n == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (n < 1)
|
||||
{
|
||||
Console.Error.WriteLine($"Invalid index adding {data} ({n})");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Allocate a new string
|
||||
int codepage = CodePage != 0 ? CodePage : Encoding.Default.CodePage;
|
||||
Encoding cpconv = Encoding.GetEncoding(codepage);
|
||||
string str = cpconv.GetString(data);
|
||||
SetEntry(n, str, refcount, persistence);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/// <param name="st">Pointer to the string table</param>
|
||||
/// <param name="id">Id of the string to retrieve</param>
|
||||
/// <param name="buffer">Destination of the UTF8 string</param>
|
||||
/// <param name="sz">
|
||||
/// Number of bytes available in the buffer on input
|
||||
/// Number of bytes used on output
|
||||
/// </param>
|
||||
/// <remarks>Returned string is not NUL-terminated.</remarks>
|
||||
private LibmsiResult StringId(int id, ref byte[] buffer, ref int offset, ref int sz)
|
||||
{
|
||||
string str_utf8 = LookupId(id);
|
||||
if (str_utf8 == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
int codepage = CodePage != 0 ? CodePage : Encoding.Default.CodePage;
|
||||
Encoding cpconv = Encoding.GetEncoding(codepage);
|
||||
byte[] str = Encoding.Convert(Encoding.UTF8, cpconv, Encoding.UTF8.GetBytes(str_utf8));
|
||||
int len = str.Length;
|
||||
|
||||
if (sz < len)
|
||||
{
|
||||
sz = len;
|
||||
return LibmsiResult.LIBMSI_RESULT_MORE_DATA;
|
||||
}
|
||||
|
||||
sz = len;
|
||||
Array.Copy(str, 0, buffer, offset, str.Length);
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
private void StringTotalSize(out int datasize, out int poolsize)
|
||||
{
|
||||
if (Strings[0].Str != null || Strings[0].PersistentRefCount != 0 || Strings[0].NonPersistentRefCount != 0)
|
||||
Console.Error.WriteLine("Oops. element 0 has a string");
|
||||
|
||||
int codepage = CodePage != 0 ? CodePage : Encoding.Default.CodePage;
|
||||
|
||||
poolsize = 4;
|
||||
datasize = 0;
|
||||
int holesize = 0;
|
||||
for (int i = 1; i < MaxCount; i++)
|
||||
{
|
||||
if (Strings[i].PersistentRefCount == 0)
|
||||
{
|
||||
poolsize += 4;
|
||||
}
|
||||
else if (Strings[i].Str != null)
|
||||
{
|
||||
Encoding cpconv = Encoding.GetEncoding(codepage);
|
||||
string str = cpconv.GetString(Encoding.UTF8.GetBytes(Strings[i].Str));
|
||||
|
||||
datasize += str.Length;
|
||||
if (str.Length > 0xffff)
|
||||
poolsize += 4;
|
||||
|
||||
poolsize += holesize + 4;
|
||||
holesize = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
holesize += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
320
BurnOutSharp/External/libmsi/Internal/Tokenize.cs
vendored
320
BurnOutSharp/External/libmsi/Internal/Tokenize.cs
vendored
@@ -1,320 +0,0 @@
|
||||
/*
|
||||
** 2001 September 15
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** A tokenizer for SQL
|
||||
**
|
||||
** This file contains C code that splits an SQL input string up into
|
||||
** individual tokens and sends those tokens one-by-one over to the
|
||||
** parser for analysis.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace LibMSI.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// The token value for this keyword
|
||||
/// </summary>
|
||||
internal class Keyword
|
||||
{
|
||||
/// <summary>
|
||||
/// All the keywords of the SQL language are stored as in a hash
|
||||
/// table composed of instances of the following structure.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The token value for this keyword
|
||||
/// </summary>
|
||||
public sql_tokentype TokenType { get; set; }
|
||||
}
|
||||
|
||||
internal class Tokenize
|
||||
{
|
||||
#region Constants
|
||||
|
||||
public const int MAX_TOKEN_LEN = 11;
|
||||
|
||||
/// <summary>
|
||||
/// These are the keywords
|
||||
/// They MUST be in alphabetical order
|
||||
/// </summary>
|
||||
public static readonly Keyword[] KeywordTable = new Keyword[]
|
||||
{
|
||||
new Keyword { Name = "ADD", TokenType = sql_tokentype.TK_ADD },
|
||||
new Keyword { Name = "ALTER", TokenType = sql_tokentype.TK_ALTER },
|
||||
new Keyword { Name = "AND", TokenType = sql_tokentype.TK_AND },
|
||||
new Keyword { Name = "BY", TokenType = sql_tokentype.TK_BY },
|
||||
new Keyword { Name = "CHAR", TokenType = sql_tokentype.TK_CHAR },
|
||||
new Keyword { Name = "CHARACTER", TokenType = sql_tokentype.TK_CHAR },
|
||||
new Keyword { Name = "CREATE", TokenType = sql_tokentype.TK_CREATE },
|
||||
new Keyword { Name = "DELETE", TokenType = sql_tokentype.TK_DELETE },
|
||||
new Keyword { Name = "DISTINCT", TokenType = sql_tokentype.TK_DISTINCT },
|
||||
new Keyword { Name = "DROP", TokenType = sql_tokentype.TK_DROP },
|
||||
new Keyword { Name = "FREE", TokenType = sql_tokentype.TK_FREE },
|
||||
new Keyword { Name = "FROM", TokenType = sql_tokentype.TK_FROM },
|
||||
new Keyword { Name = "HOLD", TokenType = sql_tokentype.TK_HOLD },
|
||||
new Keyword { Name = "INSERT", TokenType = sql_tokentype.TK_INSERT },
|
||||
new Keyword { Name = "INT", TokenType = sql_tokentype.TK_INT },
|
||||
new Keyword { Name = "INTEGER", TokenType = sql_tokentype.TK_INT },
|
||||
new Keyword { Name = "INTO", TokenType = sql_tokentype.TK_INTO },
|
||||
new Keyword { Name = "IS", TokenType = sql_tokentype.TK_IS },
|
||||
new Keyword { Name = "KEY", TokenType = sql_tokentype.TK_KEY },
|
||||
new Keyword { Name = "LIKE", TokenType = sql_tokentype.TK_LIKE },
|
||||
new Keyword { Name = "LOCALIZABLE", TokenType = sql_tokentype.TK_LOCALIZABLE },
|
||||
new Keyword { Name = "LONG", TokenType = sql_tokentype.TK_LONG },
|
||||
new Keyword { Name = "LONGCHAR", TokenType = sql_tokentype.TK_LONGCHAR },
|
||||
new Keyword { Name = "NOT", TokenType = sql_tokentype.TK_NOT },
|
||||
new Keyword { Name = "NULL", TokenType = sql_tokentype.TK_NULL },
|
||||
new Keyword { Name = "OBJECT", TokenType = sql_tokentype.TK_OBJECT },
|
||||
new Keyword { Name = "OR", TokenType = sql_tokentype.TK_OR },
|
||||
new Keyword { Name = "ORDER", TokenType = sql_tokentype.TK_ORDER },
|
||||
new Keyword { Name = "PRIMARY", TokenType = sql_tokentype.TK_PRIMARY },
|
||||
new Keyword { Name = "SELECT", TokenType = sql_tokentype.TK_SELECT },
|
||||
new Keyword { Name = "SET", TokenType = sql_tokentype.TK_SET },
|
||||
new Keyword { Name = "SHORT", TokenType = sql_tokentype.TK_SHORT },
|
||||
new Keyword { Name = "TABLE", TokenType = sql_tokentype.TK_TABLE },
|
||||
new Keyword { Name = "TEMPORARY", TokenType = sql_tokentype.TK_TEMPORARY },
|
||||
new Keyword { Name = "UPDATE", TokenType = sql_tokentype.TK_UPDATE },
|
||||
new Keyword { Name = "VALUES", TokenType = sql_tokentype.TK_VALUES },
|
||||
new Keyword { Name = "WHERE", TokenType = sql_tokentype.TK_WHERE },
|
||||
};
|
||||
|
||||
public static readonly int KEYWORD_COUNT = KeywordTable.Length;
|
||||
|
||||
/// <summary>
|
||||
/// If X is a character that can be used in an identifier then
|
||||
/// IsIdChar[X] will be 1. Otherwise IsIdChar[X] will be 0.
|
||||
///
|
||||
/// In this implementation, an identifier can be a string of
|
||||
/// alphabetic characters, digits, and "_" plus any character
|
||||
/// with the high-order bit set. The latter rule means that
|
||||
/// any sequence of UTF-8 characters or characters taken from
|
||||
/// an extended ISO8859 character set can form an identifier.
|
||||
/// </summary>
|
||||
public static readonly byte[] IsIdChar = new byte[]
|
||||
{
|
||||
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
|
||||
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1x */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, /* 2x */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 8x */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 9x */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Ax */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Bx */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Cx */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Dx */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Ex */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Fx */
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Comparison function for binary search.
|
||||
/// </summary>
|
||||
public static int CompareKeyword(string m1, Keyword m2) => string.Compare(m1, m2.Name, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// This function looks up an identifier to determine if it is a
|
||||
/// keyword. If it is a keyword, the token code of that keyword is
|
||||
/// returned. If the input is not a keyword, TK_ID is returned.
|
||||
/// </summary>
|
||||
public static sql_tokentype FindKeyword(string z, int n)
|
||||
{
|
||||
if (n > MAX_TOKEN_LEN)
|
||||
return sql_tokentype.TK_ID;
|
||||
|
||||
string str = z.Substring(0, n);
|
||||
Keyword r = KeywordTable.FirstOrDefault(k => CompareKeyword(str, k) == 0);
|
||||
if (r != default)
|
||||
return r.TokenType;
|
||||
|
||||
return sql_tokentype.TK_ID;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the length of the token that begins at <paramref name="zz"/>[0]. Return
|
||||
/// -1 if the token is (or might be) incomplete. Store the token
|
||||
/// type in *tokenType before returning.
|
||||
/// </summary>
|
||||
public static int GetToken(string zz, int ptr, out sql_tokentype tokenType, out int skip)
|
||||
{
|
||||
int z = ptr; // zz[0]
|
||||
int i = 0;
|
||||
|
||||
skip = 0;
|
||||
switch (zz[z])
|
||||
{
|
||||
// TK_SPACE
|
||||
case ' ': case '\t': case '\n': case '\f':
|
||||
for (i = 1; char.IsWhiteSpace(zz[z + i]) && zz[z + i] != '\r'; i++);
|
||||
tokenType = sql_tokentype.TK_SPACE;
|
||||
return i;
|
||||
|
||||
// TK_MINUS
|
||||
case '-':
|
||||
if (z + 1 >= zz.Length || zz[z + 1] == 0)
|
||||
{
|
||||
tokenType = sql_tokentype.TK_ILLEGAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
tokenType = sql_tokentype.TK_MINUS;
|
||||
return 1;
|
||||
|
||||
// TK_LP
|
||||
case '(':
|
||||
tokenType = sql_tokentype.TK_LP;
|
||||
return 1;
|
||||
|
||||
// TK_RP
|
||||
case ')':
|
||||
tokenType = sql_tokentype.TK_RP;
|
||||
return 1;
|
||||
|
||||
// TK_STAR
|
||||
case '*':
|
||||
tokenType = sql_tokentype.TK_STAR;
|
||||
return 1;
|
||||
|
||||
// TK_EQ
|
||||
case '=':
|
||||
tokenType = sql_tokentype.TK_EQ;
|
||||
return 1;
|
||||
|
||||
// TK_LT, TK_LE, TK_NE
|
||||
case '<':
|
||||
if (zz[z + 1] == '=')
|
||||
{
|
||||
tokenType = sql_tokentype.TK_LE;
|
||||
return 2;
|
||||
}
|
||||
else if (zz[z + 1] == '>')
|
||||
{
|
||||
tokenType = sql_tokentype.TK_NE;
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
tokenType = sql_tokentype.TK_LT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// TK_GT, TK_GE
|
||||
case '>':
|
||||
if (zz[z + 1] == '=')
|
||||
{
|
||||
tokenType = sql_tokentype.TK_GE;
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
tokenType = sql_tokentype.TK_GT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// TK_NE
|
||||
case '!':
|
||||
if (zz[z + 1] != '=')
|
||||
{
|
||||
tokenType = sql_tokentype.TK_ILLEGAL;
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
tokenType = sql_tokentype.TK_NE;
|
||||
return 2;
|
||||
}
|
||||
|
||||
// TK_WILDCARD
|
||||
case '?':
|
||||
tokenType = sql_tokentype.TK_WILDCARD;
|
||||
return 1;
|
||||
|
||||
// TK_COMMA
|
||||
case ',':
|
||||
tokenType = sql_tokentype.TK_COMMA;
|
||||
return 1;
|
||||
|
||||
// TK_ID, TK_STRING
|
||||
case '`': case '\'':
|
||||
{
|
||||
int delim = zz[z];
|
||||
for (i = 1; i < zz.Length && zz[z + i] != 0; i++)
|
||||
{
|
||||
if (zz[z + i] == delim)
|
||||
break;
|
||||
}
|
||||
|
||||
if (zz[z + i] != 0)
|
||||
i++;
|
||||
|
||||
if (delim == '`')
|
||||
tokenType = sql_tokentype.TK_ID;
|
||||
else
|
||||
tokenType = sql_tokentype.TK_STRING;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
// TK_DOT, TK_INTEGER
|
||||
case '.':
|
||||
if (!char.IsDigit(zz[z + 1]))
|
||||
{
|
||||
tokenType = sql_tokentype.TK_DOT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
tokenType = sql_tokentype.TK_INTEGER;
|
||||
for (i = 1; char.IsDigit(zz[z + i]); i++);
|
||||
return i;
|
||||
|
||||
// TK_INTEGER
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
tokenType = sql_tokentype.TK_INTEGER;
|
||||
for (i = 1; char.IsDigit(zz[z + i]); i++);
|
||||
return i;
|
||||
|
||||
// TK_ID
|
||||
case '[':
|
||||
for(i = 1; zz[z + i] != 0 && zz[z + i - 1] != ']'; i++);
|
||||
tokenType = sql_tokentype.TK_ID;
|
||||
return i;
|
||||
|
||||
default:
|
||||
if (IsIdChar[zz[z]] == 0)
|
||||
break;
|
||||
|
||||
for (i = 1; IsIdChar[zz[z + i]] != 0; i++);
|
||||
tokenType = FindKeyword(zz, z + i);
|
||||
if (tokenType == sql_tokentype.TK_ID && zz[z + i] == '`')
|
||||
skip = 1;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
tokenType = sql_tokentype.TK_ILLEGAL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
2108
BurnOutSharp/External/libmsi/LibmsiDatabase.cs
vendored
2108
BurnOutSharp/External/libmsi/LibmsiDatabase.cs
vendored
File diff suppressed because it is too large
Load Diff
511
BurnOutSharp/External/libmsi/LibmsiQuery.cs
vendored
511
BurnOutSharp/External/libmsi/LibmsiQuery.cs
vendored
@@ -1,511 +0,0 @@
|
||||
/*
|
||||
* Implementation of the Microsoft Installer (msi.dll)
|
||||
*
|
||||
* Copyright 2002-2005 Mike McCormack for CodeWeavers
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using LibGSF.Input;
|
||||
using LibMSI.Internal;
|
||||
using LibMSI.Views;
|
||||
using static LibMSI.Internal.MsiPriv;
|
||||
|
||||
namespace LibMSI
|
||||
{
|
||||
public class LibmsiQuery
|
||||
{
|
||||
#region Properties
|
||||
|
||||
internal LibmsiView View { get; set; }
|
||||
|
||||
internal int Row { get; set; }
|
||||
|
||||
internal LibmsiDatabase Database { get; set; }
|
||||
|
||||
internal string Query { get; set; }
|
||||
|
||||
internal LinkedList<object> Mem { get; set; } = new LinkedList<object>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private LibmsiQuery() { }
|
||||
|
||||
/// <summary>
|
||||
/// Create a SQL query for <paramref name="database"/>.
|
||||
/// </summary>
|
||||
/// <param name="database">A LibmsiDatabase</param>
|
||||
/// <param name="query">A SQL query</param>
|
||||
/// <param name="error">Exception to set on error, or null</param>
|
||||
/// <returns>A new LibmsiQuery on success, null on failure</returns>
|
||||
public static LibmsiQuery Create(LibmsiDatabase database, string query, ref Exception error)
|
||||
{
|
||||
if (database == null)
|
||||
return null;
|
||||
if (query == null)
|
||||
return null;
|
||||
if (error != null)
|
||||
return null;
|
||||
|
||||
LibmsiQuery self = new LibmsiQuery
|
||||
{
|
||||
Database = database,
|
||||
Query = query,
|
||||
};
|
||||
|
||||
if (!self.Init(ref error))
|
||||
return null;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~LibmsiQuery()
|
||||
{
|
||||
if (View != null)
|
||||
try { View.Delete(); } catch { }
|
||||
|
||||
Mem.Clear();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Return the next query result. null is returned when there
|
||||
/// is no more results.
|
||||
/// </summary>
|
||||
/// <param name="error">Return location for the error</param>
|
||||
/// <returns>A newly allocated LibmsiRecord or null when no results or failure.</returns>
|
||||
public LibmsiRecord Fetch(ref Exception error)
|
||||
{
|
||||
if (error != null)
|
||||
return null;
|
||||
|
||||
LibmsiResult ret = Fetch(out LibmsiRecord record);
|
||||
|
||||
// FIXME: raise error when it happens
|
||||
if (ret != LibmsiResult.LIBMSI_RESULT_SUCCESS && ret != LibmsiResult.NO_MORE_ITEMS)
|
||||
error = new Exception($"LIBMSI_RESULT_ERROR: {ret}");
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute the query with the arguments from <paramref name="rec"/>.
|
||||
/// </summary>
|
||||
/// <param name="rec">A LibmsiRecord containing query arguments, or null if no arguments needed</param>
|
||||
/// <param name="error">Return location for the error</param>
|
||||
/// <returns>True on success</returns>
|
||||
public bool Execute(LibmsiRecord rec, ref Exception error)
|
||||
{
|
||||
if (error != null)
|
||||
return false;
|
||||
|
||||
LibmsiResult ret = Execute(rec);
|
||||
|
||||
// FIXME: raise error when it happens
|
||||
if (ret != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
error = new Exception($"LIBMSI_RESULT_ERROR: {ret}");
|
||||
|
||||
return ret == LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release the current result set.
|
||||
/// </summary>
|
||||
/// <param name="error">Return location for the error</param>
|
||||
/// <returns>True on success</returns>
|
||||
public bool Close(ref Exception error)
|
||||
{
|
||||
if (error != null)
|
||||
return false;
|
||||
|
||||
if (View == null)
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
LibmsiResult ret = View.Close();
|
||||
|
||||
// FIXME: raise error when it happens
|
||||
if (ret != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
error = new Exception($"LIBMSI_RESULT_ERROR: {ret}");
|
||||
|
||||
return ret == LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this to get more information on the last query error.
|
||||
/// </summary>
|
||||
/// <param name="column">Location to store the allocated column name</param>
|
||||
/// <param name="error">Return location for the error</param>
|
||||
public void GetError(out string column, ref Exception error)
|
||||
{
|
||||
column = null;
|
||||
if (error != null)
|
||||
return;
|
||||
|
||||
if (View.Error != LibmsiDBError.LIBMSI_DB_ERROR_SUCCESS)
|
||||
{
|
||||
// FIXME: view could have a GError with message?
|
||||
error = new Exception($"LIBMSI_DB_ERROR: {View.Error}");
|
||||
column = View.ErrorColumn;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get column informations, returned as record string fields.
|
||||
/// </summary>
|
||||
/// <param name="info">A LibmsiColInfo specifying the type of information to return</param>
|
||||
/// <param name="error">Return location for the error</param>
|
||||
/// <returns>A LibmsiRecord containing informations, or null on error</returns>
|
||||
public LibmsiRecord GetColumnInfo(LibmsiColInfo info, ref Exception error)
|
||||
{
|
||||
if (info != LibmsiColInfo.LIBMSI_COL_INFO_NAMES && info != LibmsiColInfo.LIBMSI_COL_INFO_TYPES)
|
||||
return null;
|
||||
if (error != null)
|
||||
return null;
|
||||
|
||||
LibmsiResult r = GetColumnInfo(info, out LibmsiRecord rec);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
error = new Exception($"LIBMSI_RESULT_ERROR: {r}");
|
||||
|
||||
return rec;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Functions
|
||||
|
||||
// TODO: Move to LibmsiView
|
||||
internal static LibmsiResult ViewFindColumn(LibmsiView table, string name, string table_name, out int n)
|
||||
{
|
||||
n = 0;
|
||||
LibmsiResult r = table.GetDimensions(out _, out int count);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
for (int i = 1; i <= count; i++)
|
||||
{
|
||||
r = table.GetColumnInfo(i, out string col_name, out _, out _, out string haystack_table_name);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
bool x = name == col_name;
|
||||
if (table_name != null)
|
||||
x |= table_name == haystack_table_name;
|
||||
|
||||
if (!x)
|
||||
{
|
||||
n = i;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// TODO: Move to LibmsiDatabase
|
||||
internal static LibmsiResult DatabaseOpenQuery(LibmsiDatabase db, string szQuery, out LibmsiQuery pView)
|
||||
{
|
||||
Exception err = null;
|
||||
pView = LibmsiQuery.Create(db, szQuery, ref err);
|
||||
return pView != null ? LibmsiResult.LIBMSI_RESULT_SUCCESS : LibmsiResult.LIBMSI_RESULT_BAD_QUERY_SYNTAX;
|
||||
}
|
||||
|
||||
// TODO: Move to LibmsiDatabase
|
||||
internal static LibmsiResult QueryOpen(LibmsiDatabase db, out LibmsiQuery view, string query)
|
||||
{
|
||||
Exception err = null;
|
||||
view = Create(db, query, ref err);
|
||||
if (err != null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
internal LibmsiResult IterateRecords(ref int count, record_func func, object param)
|
||||
{
|
||||
LibmsiResult r = Execute(null);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
int max = 0;
|
||||
if (count != 0)
|
||||
max = count;
|
||||
|
||||
// Iterate a query
|
||||
int n = 0;
|
||||
for (; (max == 0) || (n < max); n++)
|
||||
{
|
||||
r = Fetch(out LibmsiRecord rec);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
break;
|
||||
|
||||
if (func != null)
|
||||
r = func(rec, param);
|
||||
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
break;
|
||||
}
|
||||
|
||||
Exception error = null; // FIXME: move error handling to caller
|
||||
Close(ref error);
|
||||
if (error != null)
|
||||
Console.Error.WriteLine(error);
|
||||
|
||||
count = n;
|
||||
if (r == LibmsiResult.NO_MORE_ITEMS)
|
||||
r = LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a single record from a query
|
||||
/// </summary>
|
||||
// TODO: Move to LibmsiDatabase
|
||||
internal static LibmsiRecord QueryGetRecord(LibmsiDatabase db, string query)
|
||||
{
|
||||
LibmsiResult r = LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
|
||||
Exception error = null; // FIXME: move error to caller
|
||||
LibmsiQuery view = Create(db, query, ref error);
|
||||
if (error != null)
|
||||
r = LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
error = null;
|
||||
if (r == LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
view.Execute(null);
|
||||
view.Fetch(out LibmsiRecord rec);
|
||||
view.Close(ref error);
|
||||
if (error != null)
|
||||
Console.Error.WriteLine(error);
|
||||
|
||||
return rec;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: Move to LibmsiDatabase
|
||||
internal static LibmsiResult MsiViewGetRow(LibmsiDatabase db, LibmsiView view, int row, out LibmsiRecord rec)
|
||||
{
|
||||
rec = null;
|
||||
LibmsiResult ret = view.GetDimensions(out int row_count, out int col_count);
|
||||
if (ret != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return ret;
|
||||
|
||||
if (col_count == 0)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
if (row >= row_count)
|
||||
return LibmsiResult.NO_MORE_ITEMS;
|
||||
|
||||
rec = LibmsiRecord.Create(col_count);
|
||||
if (rec == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
for (int i = 1; i <= col_count; i++)
|
||||
{
|
||||
ret = view.GetColumnInfo(i, out _, out int type, out _, out _);
|
||||
if (ret != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
Console.Error.WriteLine($"Error getting column type for {i}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (MSITYPE_IS_BINARY(type))
|
||||
{
|
||||
ret = view.FetchStream(row, i, out GsfInput stm);
|
||||
if ((ret == LibmsiResult.LIBMSI_RESULT_SUCCESS) && stm != null)
|
||||
rec.SetGsfInput(i, stm);
|
||||
else
|
||||
Console.Error.WriteLine("Failed to get stream");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = view.FetchInt(row, i, out int ival);
|
||||
if (ret != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
Console.Error.WriteLine($"Error fetching data for {i}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((type & MSITYPE_VALID) == 0)
|
||||
Console.Error.WriteLine("Invalid type!");
|
||||
|
||||
// Check if it's nul (0) - if so, don't set anything
|
||||
if (ival == 0)
|
||||
continue;
|
||||
|
||||
if ((type & MSITYPE_STRING) != 0)
|
||||
{
|
||||
string sval = db.Strings.LookupId(ival);
|
||||
rec.SetString(i, sval);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((type & MSI_DATASIZEMASK) == 2)
|
||||
rec.SetInt(i, ival - (1 << 15));
|
||||
else
|
||||
rec.SetInt(i, ival - (1 << 31));
|
||||
}
|
||||
}
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
internal LibmsiResult Fetch(out LibmsiRecord prec)
|
||||
{
|
||||
prec = null;
|
||||
if (View == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
LibmsiResult r = MsiViewGetRow(Database, View, Row, out prec);
|
||||
if (r == LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
Row++;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
internal LibmsiResult Execute(LibmsiRecord rec)
|
||||
{
|
||||
if (View == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
Row = 0;
|
||||
try
|
||||
{
|
||||
return View.Execute(rec);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
internal LibmsiResult GetColumnInfo(LibmsiColInfo info, out LibmsiRecord prec)
|
||||
{
|
||||
LibmsiResult r = LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
prec = null;
|
||||
if (View == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
int count;
|
||||
try
|
||||
{
|
||||
r = View.GetDimensions(out _, out count);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
LibmsiRecord rec = LibmsiRecord.Create(count);
|
||||
if (rec == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
r = View.GetColumnInfo(i + 1, out string name, out int type, out bool temporary, out _);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
continue;
|
||||
|
||||
if (info == LibmsiColInfo.LIBMSI_COL_INFO_NAMES)
|
||||
rec.SetString(i + 1, name);
|
||||
else
|
||||
SetRecordTypeString(rec, i + 1, type, temporary);
|
||||
}
|
||||
|
||||
prec = rec;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private bool Init(ref Exception error)
|
||||
{
|
||||
LibmsiResult r = LibmsiSQLInput.ParseSQL(Database, Query, out LibmsiView view, Mem);
|
||||
View = view;
|
||||
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
error = new Exception($"LIBMSI_RESULT_ERROR: {r}");
|
||||
|
||||
return r == LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
// TODO: Move to LibmsiRecord
|
||||
private static void SetRecordTypeString(LibmsiRecord rec, int field, int type, bool temporary)
|
||||
{
|
||||
string szType;
|
||||
if (MSITYPE_IS_BINARY(type))
|
||||
szType = "v";
|
||||
else if ((type & MSITYPE_LOCALIZABLE) != 0)
|
||||
szType = "l";
|
||||
else if ((type & MSITYPE_UNKNOWN) != 0)
|
||||
szType = "f";
|
||||
else if ((type & MSITYPE_STRING) != 0)
|
||||
{
|
||||
if (temporary)
|
||||
szType = "g";
|
||||
else
|
||||
szType = "s";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (temporary)
|
||||
szType = "j";
|
||||
else
|
||||
szType = "i";
|
||||
}
|
||||
|
||||
if ((type & MSITYPE_NULLABLE) != 0)
|
||||
szType = ((char)(szType[0] & ~0x20)).ToString();
|
||||
|
||||
szType += (type & 0xff).ToString();
|
||||
|
||||
rec.SetString(field, szType);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
645
BurnOutSharp/External/libmsi/LibmsiRecord.cs
vendored
645
BurnOutSharp/External/libmsi/LibmsiRecord.cs
vendored
@@ -1,645 +0,0 @@
|
||||
/*
|
||||
* Implementation of the Microsoft Installer (msi.dll)
|
||||
*
|
||||
* Copyright 2002-2004 Mike McCormack for CodeWeavers
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using LibGSF.Input;
|
||||
using LibMSI.Internal;
|
||||
using static LibMSI.LibmsiTypes;
|
||||
|
||||
namespace LibMSI
|
||||
{
|
||||
/* maybe we can use a Variant instead of doing it ourselves? */
|
||||
internal class LibmsiField
|
||||
{
|
||||
public int Type { get; set; }
|
||||
|
||||
#region Union (u)
|
||||
|
||||
public int iVal
|
||||
{
|
||||
get
|
||||
{
|
||||
try { return (int)u; }
|
||||
catch { return default; }
|
||||
}
|
||||
set => u = value;
|
||||
}
|
||||
|
||||
public string szVal
|
||||
{
|
||||
get => u as string;
|
||||
set => u = value;
|
||||
}
|
||||
|
||||
public GsfInput Stream
|
||||
{
|
||||
get => u as GsfInput;
|
||||
set => u = value;
|
||||
}
|
||||
|
||||
private object u = new object();
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class LibmsiRecord
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private const int LIBMSI_FIELD_TYPE_NULL = 0;
|
||||
private const int LIBMSI_FIELD_TYPE_INT = 1;
|
||||
private const int LIBMSI_FIELD_TYPE_STR = 3;
|
||||
private const int LIBMSI_FIELD_TYPE_STREAM = 4;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// as passed to libmsi_record_new
|
||||
/// </summary>
|
||||
internal int Count { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// nb. array size is count+1
|
||||
/// </summary>
|
||||
internal LibmsiField[] Fields { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private LibmsiRecord() { }
|
||||
|
||||
public static LibmsiRecord Create(int count)
|
||||
{
|
||||
if (count >= 65535)
|
||||
return null;
|
||||
|
||||
return new LibmsiRecord
|
||||
{
|
||||
Count = count,
|
||||
Fields = new LibmsiField[count],
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Clear record fields.
|
||||
/// <summary>
|
||||
/// <returns>True on success.</returns>
|
||||
public bool Clear()
|
||||
{
|
||||
for (int i = 0; i <= Count; i++)
|
||||
{
|
||||
Fields[i].Type = LIBMSI_FIELD_TYPE_NULL;
|
||||
Fields[i].iVal = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <returns>The number of record fields.</returns>
|
||||
public int GetFieldCount() => Count;
|
||||
|
||||
/// <param name="field">A field identifier</param>
|
||||
/// <returns>True if the field is null (or <paramref name="field"/> > record field count)</returns>
|
||||
public bool IsNull(int field) => (field > Count) || (Fields[field].Type == LIBMSI_FIELD_TYPE_NULL);
|
||||
|
||||
/// <summary>
|
||||
/// Set the <paramref name="field"/> to the integer value <paramref name="iVal"/>.
|
||||
/// <summary>
|
||||
/// <param name="field">A field identifier</param>
|
||||
/// <param name="iVal">Value to set field to</param>
|
||||
/// <returns>True on success.</returns>
|
||||
public bool SetInt(int field, int iVal)
|
||||
{
|
||||
if (field > Count)
|
||||
return false;
|
||||
|
||||
Fields[field].Type = LIBMSI_FIELD_TYPE_INT;
|
||||
Fields[field].iVal = iVal;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the integer value of <paramref name="field"/>. If the field is a string
|
||||
/// representing an integer, it will be converted to an integer value.
|
||||
/// Other values and types will return LIBMSI_NULL_INT.
|
||||
/// <summary>
|
||||
/// <param name="field">A field identifier</param>
|
||||
/// <returns>The integer value, or LIBMSI_NULL_INT if the field is not an integer.</returns>
|
||||
public int GetInt(int field)
|
||||
{
|
||||
if (field > Count)
|
||||
unchecked { return (int)LIBMSI_NULL_INT; }
|
||||
|
||||
switch (Fields[field].Type)
|
||||
{
|
||||
case LIBMSI_FIELD_TYPE_INT:
|
||||
return Fields[field].iVal;
|
||||
case LIBMSI_FIELD_TYPE_STR:
|
||||
if (ExprIntFromString(Fields[field].szVal, out int ret))
|
||||
return ret;
|
||||
|
||||
unchecked { return (int)LIBMSI_NULL_INT; }
|
||||
default:
|
||||
Console.Error.WriteLine($"Invalid field type {Fields[field].Type}");
|
||||
break;
|
||||
}
|
||||
|
||||
unchecked { return (int)LIBMSI_NULL_INT; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the <paramref name="field"/> value to <paramref name="szValue"/> string.
|
||||
/// <summary>
|
||||
/// <param name="field">A field identifier</param>
|
||||
/// <param name="szValue">A string or null</param>
|
||||
/// <returns>True on success.</returns>
|
||||
public bool SetString(int field, string szValue)
|
||||
{
|
||||
if (field > Count)
|
||||
return false;
|
||||
|
||||
if (szValue != null && szValue[0] != '\0')
|
||||
{
|
||||
Fields[field].Type = LIBMSI_FIELD_TYPE_STR;
|
||||
Fields[field].szVal = szValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
Fields[field].Type = LIBMSI_FIELD_TYPE_NULL;
|
||||
Fields[field].szVal = null;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a string representation of <paramref name="field"/>.
|
||||
/// <summary>
|
||||
/// <param name="field">A field identifier</param>
|
||||
/// <returns>A string, or null on error.</returns>
|
||||
public string GetString(int field)
|
||||
{
|
||||
if (field > Count)
|
||||
return string.Empty; // FIXME: really?
|
||||
|
||||
switch (Fields[field].Type)
|
||||
{
|
||||
case LIBMSI_FIELD_TYPE_INT:
|
||||
return Fields[field].iVal.ToString();
|
||||
case LIBMSI_FIELD_TYPE_STR:
|
||||
return Fields[field].szVal;
|
||||
case LIBMSI_FIELD_TYPE_NULL:
|
||||
return string.Empty;
|
||||
default:
|
||||
Console.Error.WriteLine($"Invalid type {Fields[field].Type}");
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the file content as a stream in <paramref name="field"/>.
|
||||
/// <summary>
|
||||
/// <param name="field">A field identifier</param>
|
||||
/// <param name="szFilename">A filename or null</param>
|
||||
/// <returns>True on success.</returns>
|
||||
public bool LoadStream(int field, string szFilename)
|
||||
{
|
||||
LibmsiResult ret = LoadStreamFromFile(field, szFilename);
|
||||
return ret == LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the stream content from <paramref name="input"/> stream.
|
||||
/// <summary>
|
||||
/// <param name="field">A field identifier</param>
|
||||
/// <param name="input">A GInputStream</param>
|
||||
/// <param name="count">The number of bytes to read from <paramref name="input"/></param>
|
||||
/// <param name="error">Exception to set on error, or null</param>
|
||||
/// <returns>True on success.</returns>
|
||||
public bool SetStream(int field, Stream input, int count, ref Exception error)
|
||||
{
|
||||
if (!input.CanRead)
|
||||
return false;
|
||||
if (field <= 0 || field > Count)
|
||||
return false;
|
||||
if (count <= 0)
|
||||
return false;
|
||||
if (error != null)
|
||||
return false;
|
||||
|
||||
byte[] data = new byte[count];
|
||||
|
||||
int bytes_read = input.Read(data, 0, count);
|
||||
if (bytes_read != count)
|
||||
return false;
|
||||
|
||||
GsfInput stm = GsfInputMemory.Create(data, count, true);
|
||||
if (LoadStream(field, stm) != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the stream associated with the given record <paramref name="field"/>.
|
||||
/// <summary>
|
||||
/// <param name="field">A field identifier</param>
|
||||
/// <returns>True on success.</returns>
|
||||
public Stream GetStream(int field)
|
||||
{
|
||||
Exception err = null;
|
||||
GsfInput stm = GetStream(field, ref err);
|
||||
if (stm == null)
|
||||
return null;
|
||||
|
||||
return LibmsiIStream.Create(stm);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Functions
|
||||
|
||||
internal LibmsiResult CopyField(int in_n, LibmsiRecord out_rec, int out_n)
|
||||
{
|
||||
LibmsiResult r = LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
|
||||
if (in_n > Count || out_n > out_rec.Count)
|
||||
{
|
||||
r = LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
else if (this != out_rec || in_n != out_n)
|
||||
{
|
||||
LibmsiField input = Fields[in_n];
|
||||
LibmsiField output = out_rec.Fields[out_n];
|
||||
|
||||
switch (input.Type)
|
||||
{
|
||||
case LIBMSI_FIELD_TYPE_NULL:
|
||||
break;
|
||||
case LIBMSI_FIELD_TYPE_INT:
|
||||
output.iVal = input.iVal;
|
||||
break;
|
||||
case LIBMSI_FIELD_TYPE_STR:
|
||||
output.szVal = input.szVal;
|
||||
break;
|
||||
case LIBMSI_FIELD_TYPE_STREAM:
|
||||
output.Stream = input.Stream;
|
||||
break;
|
||||
default:
|
||||
Console.Error.WriteLine($"Invalid field type {input.Type}");
|
||||
break;
|
||||
}
|
||||
|
||||
if (r == LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
output.Type = input.Type;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
internal string GetStringRaw(int field)
|
||||
{
|
||||
if (field > Count)
|
||||
return null;
|
||||
|
||||
if (Fields[field].Type != LIBMSI_FIELD_TYPE_STR)
|
||||
return null;
|
||||
|
||||
return Fields[field].szVal;
|
||||
}
|
||||
|
||||
internal LibmsiResult GetString(int field, out string szValue, ref int pcchValue)
|
||||
{
|
||||
if (field > Count)
|
||||
{
|
||||
if (pcchValue > 0)
|
||||
szValue = "\0";
|
||||
else
|
||||
szValue = null;
|
||||
|
||||
pcchValue = 0;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
int len;
|
||||
switch (Fields[field].Type)
|
||||
{
|
||||
case LIBMSI_FIELD_TYPE_INT:
|
||||
szValue = Fields[field].iVal.ToString();
|
||||
len = szValue.Length;
|
||||
break;
|
||||
case LIBMSI_FIELD_TYPE_STR:
|
||||
szValue = Fields[field].szVal;
|
||||
len = Fields[field].szVal.Length;
|
||||
break;
|
||||
case LIBMSI_FIELD_TYPE_NULL:
|
||||
if (pcchValue > 0)
|
||||
szValue = "\0";
|
||||
else
|
||||
szValue = null;
|
||||
|
||||
len = 0;
|
||||
break;
|
||||
default:
|
||||
szValue = null;
|
||||
len = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
LibmsiResult ret = LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
if (szValue != null && pcchValue <= len)
|
||||
ret = LibmsiResult.LIBMSI_RESULT_MORE_DATA;
|
||||
|
||||
pcchValue = len;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal LibmsiResult LoadStream(int field, GsfInput stream)
|
||||
{
|
||||
if ((field == 0) || (field > Count))
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
Fields[field].Type = LIBMSI_FIELD_TYPE_STREAM;
|
||||
Fields[field].Stream = stream;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
internal LibmsiResult LoadStreamFromFile(int field, string szFilename)
|
||||
{
|
||||
GsfInput stm;
|
||||
LibmsiResult r;
|
||||
|
||||
if ((field == 0) || (field > Count))
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
// No filename means we should seek back to the start of the stream
|
||||
if (szFilename == null)
|
||||
{
|
||||
if (Fields[field].Type != LIBMSI_FIELD_TYPE_STREAM)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_FIELD;
|
||||
|
||||
stm = Fields[field].Stream;
|
||||
if (stm == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_FIELD;
|
||||
|
||||
stm.Seek(0, SeekOrigin.Begin);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read the file into a stream and save the stream in the record
|
||||
r = AddStreamFromFile(szFilename, out stm);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
// If all's good, store it in the record
|
||||
LoadStream(field, stm);
|
||||
}
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
internal LibmsiResult SetGsfInput(int field, GsfInput stm)
|
||||
{
|
||||
if (field > Count)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_FIELD;
|
||||
|
||||
Fields[field].Type = LIBMSI_FIELD_TYPE_STREAM;
|
||||
Fields[field].Stream = stm;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
internal LibmsiResult GetGsfInput(int field, out GsfInput pstm)
|
||||
{
|
||||
pstm = null;
|
||||
if (field > Count)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_FIELD;
|
||||
|
||||
if (Fields[field].Type != LIBMSI_FIELD_TYPE_STREAM)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_FIELD;
|
||||
|
||||
pstm = Fields[field].Stream;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
internal LibmsiRecord Clone()
|
||||
{
|
||||
int count = GetFieldCount();
|
||||
LibmsiRecord clone = Create(count);
|
||||
if (clone == null)
|
||||
return null;
|
||||
|
||||
for (int i = 0; i <= count; i++)
|
||||
{
|
||||
if (Fields[i].Type == LIBMSI_FIELD_TYPE_STREAM)
|
||||
{
|
||||
Exception err = null;
|
||||
GsfInput stm = Fields[i].Stream.Duplicate(ref err);
|
||||
if (stm == null)
|
||||
return null;
|
||||
|
||||
clone.Fields[i].Stream = stm;
|
||||
clone.Fields[i].Type = LIBMSI_FIELD_TYPE_STREAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
LibmsiResult r = CopyField(i, clone, i);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
internal static bool RecordCompareFields(LibmsiRecord a, LibmsiRecord b, int field)
|
||||
{
|
||||
if (a.Fields[field].Type != b.Fields[field].Type)
|
||||
return false;
|
||||
|
||||
switch (a.Fields[field].Type)
|
||||
{
|
||||
case LIBMSI_FIELD_TYPE_NULL:
|
||||
return true;
|
||||
|
||||
case LIBMSI_FIELD_TYPE_INT:
|
||||
return (a.Fields[field].iVal == b.Fields[field].iVal);
|
||||
|
||||
case LIBMSI_FIELD_TYPE_STR:
|
||||
return (a.Fields[field].szVal == b.Fields[field].szVal);
|
||||
|
||||
case LIBMSI_FIELD_TYPE_STREAM:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool RecordCompare(LibmsiRecord a, LibmsiRecord b)
|
||||
{
|
||||
if (a.Count != b.Count)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i <= a.Count; i++)
|
||||
{
|
||||
if (!RecordCompareFields(a, b, i))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal string DupRecordField(int field)
|
||||
{
|
||||
if (IsNull(field))
|
||||
return null;
|
||||
|
||||
int sz = 0;
|
||||
LibmsiResult r = GetString(field, out _, ref sz);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return null;
|
||||
|
||||
sz++;
|
||||
r = GetString(field, out string str, ref sz);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
Console.Error.WriteLine("Failed to get string!");
|
||||
return null;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private static bool ExprIntFromString(string str, out int output)
|
||||
{
|
||||
output = 0;
|
||||
|
||||
int x = 0;
|
||||
int p = 0; // str[0]
|
||||
|
||||
// Skip the minus sign
|
||||
if (str[p] == '-')
|
||||
p++;
|
||||
|
||||
while (p < str.Length && str[p] != '\0')
|
||||
{
|
||||
if ((str[p] < '0') || (str[p] > '9'))
|
||||
return false;
|
||||
|
||||
x *= 10;
|
||||
x += (str[p] - '0');
|
||||
p++;
|
||||
}
|
||||
|
||||
// Check if it's negative
|
||||
if (str[0] == '-')
|
||||
x = -x;
|
||||
|
||||
output = x;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read the data in a file into a memory-backed GsfInput
|
||||
/// </summary>
|
||||
private static LibmsiResult AddStreamFromFile(string szFile, out GsfInput pstm)
|
||||
{
|
||||
pstm = null;
|
||||
|
||||
Exception err = null;
|
||||
GsfInput stm = GsfInputStdio.Create(szFile, ref err);
|
||||
if (stm == null)
|
||||
{
|
||||
Console.Error.WriteLine($"Open file failed for {szFile}");
|
||||
return LibmsiResult.LIBMSI_RESULT_OPEN_FAILED;
|
||||
}
|
||||
|
||||
long sz = stm.Size;
|
||||
byte[] data;
|
||||
if (sz == 0)
|
||||
{
|
||||
data = new byte[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
data = new byte[sz];
|
||||
if (stm.Read((int)sz, data) == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
pstm = GsfInputMemory.Create(data, sz, true);
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
private GsfInput GetStream(int field, ref Exception error)
|
||||
{
|
||||
if (field > Count)
|
||||
{
|
||||
error = new Exception($"LIBMSI_RESULT_ERROR: {LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER}");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Fields[field].Type == LIBMSI_FIELD_TYPE_NULL)
|
||||
{
|
||||
error = new Exception($"LIBMSI_RESULT_ERROR: {LibmsiResult.LIBMSI_RESULT_INVALID_DATA}");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Fields[field].Type != LIBMSI_FIELD_TYPE_STREAM)
|
||||
{
|
||||
error = new Exception($"LIBMSI_RESULT_ERROR: {LibmsiResult.LIBMSI_RESULT_INVALID_DATATYPE}");
|
||||
return null;
|
||||
}
|
||||
|
||||
GsfInput stm = Fields[field].Stream;
|
||||
if (stm == null)
|
||||
{
|
||||
error = new Exception($"LIBMSI_RESULT_ERROR: {LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER}");
|
||||
return null;
|
||||
}
|
||||
|
||||
return stm;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
958
BurnOutSharp/External/libmsi/LibmsiSummaryInfo.cs
vendored
958
BurnOutSharp/External/libmsi/LibmsiSummaryInfo.cs
vendored
@@ -1,958 +0,0 @@
|
||||
/*
|
||||
* Implementation of the Microsoft Installer (msi.dll)
|
||||
*
|
||||
* Copyright 2002, 2005 Mike McCormack for CodeWeavers
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using LibGSF.Input;
|
||||
using LibMSI.Internal;
|
||||
using static LibMSI.Internal.MsiPriv;
|
||||
|
||||
namespace LibMSI
|
||||
{
|
||||
// TODO: Make this a proper variant type OR split it meaningfully
|
||||
internal class LibmsiOLEVariant
|
||||
{
|
||||
public LibmsiOLEVariantType VariantType { get; set; }
|
||||
|
||||
#region Union ()
|
||||
|
||||
public int IntVal
|
||||
{
|
||||
get
|
||||
{
|
||||
try { return (int)o; }
|
||||
catch { return default; }
|
||||
}
|
||||
set => o = value;
|
||||
}
|
||||
|
||||
public string StrVal
|
||||
{
|
||||
get => o as string;
|
||||
set => o = value;
|
||||
}
|
||||
|
||||
public ulong FileTime
|
||||
{
|
||||
get
|
||||
{
|
||||
try { return (ulong)o; }
|
||||
catch { return default; }
|
||||
}
|
||||
set => o = value;
|
||||
}
|
||||
|
||||
private object o = new object();
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class LibmsiSummaryInfo
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private static readonly string szSumInfo = (char)0x05 + "SummaryInformation";
|
||||
|
||||
private static readonly byte[] fmtid_SummaryInformation = new byte[]
|
||||
{ 0xe0, 0x85, 0x9f, 0xf2, 0xf9, 0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9};
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
internal LibmsiDatabase Database { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of changes allowed
|
||||
/// </summary>
|
||||
internal int UpdateCount { get; set; }
|
||||
|
||||
internal LibmsiOLEVariant[] Properties { get; set; } = new LibmsiOLEVariant[MSI_MAX_PROPS];
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor and Destructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private LibmsiSummaryInfo() { }
|
||||
|
||||
// TODO: Investigate why Create isn't populating the properties
|
||||
|
||||
/// <summary>
|
||||
/// If @database is provided, the summary informations will be
|
||||
/// populated during creation, and the libmsi_summary_info_persist()
|
||||
/// function will save the properties to it. If @database is null, you
|
||||
/// may still populate properties and then save them to a particular
|
||||
/// database with the libmsi_summary_info_save() function.
|
||||
/// </summary>
|
||||
/// <param name="database">An optional associated LibmsiDatabase</param>
|
||||
/// <param name="update_count">Number of changes allowed</param>
|
||||
/// <param name="error">Exception to set on error, or null</param>
|
||||
/// <returns>A LibmsiSummaryInfo or null on failure</returns>
|
||||
public static LibmsiSummaryInfo Create(LibmsiDatabase database, int update_count, ref Exception error)
|
||||
{
|
||||
if (error != null)
|
||||
return null;
|
||||
|
||||
LibmsiSummaryInfo self = new LibmsiSummaryInfo
|
||||
{
|
||||
Database = database,
|
||||
UpdateCount = update_count,
|
||||
};
|
||||
|
||||
// Read the stream... if we fail, we'll start with an empty property set
|
||||
if (self.Database != null)
|
||||
{
|
||||
LibmsiResult r = self.Database.GetRawStream(szSumInfo, out GsfInput stm);
|
||||
if (r == LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
self.LoadSummaryInfo(stm);
|
||||
}
|
||||
|
||||
return self;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~LibmsiSummaryInfo()
|
||||
{
|
||||
for (int i = 0; i < MSI_MAX_PROPS; i++)
|
||||
{
|
||||
FreeProp(Properties[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <param name="prop">A LibmsiProperty to get</param>
|
||||
/// <param name="error">Exception to set on error, or null</param>
|
||||
/// <returns>The property type associated for <param name="prop"/>.</returns.
|
||||
public LibmsiPropertyType GetPropertyType(LibmsiProperty prop, ref Exception error)
|
||||
{
|
||||
if (error != null)
|
||||
return LibmsiPropertyType.LIBMSI_PROPERTY_TYPE_EMPTY;
|
||||
|
||||
if ((int)prop >= MSI_MAX_PROPS)
|
||||
{
|
||||
error = new Exception($"Unknown property: {prop}");
|
||||
return LibmsiPropertyType.LIBMSI_PROPERTY_TYPE_EMPTY;
|
||||
}
|
||||
|
||||
switch (Properties[(int)prop].VariantType)
|
||||
{
|
||||
case LibmsiOLEVariantType.OLEVT_I2:
|
||||
case LibmsiOLEVariantType.OLEVT_I4:
|
||||
return LibmsiPropertyType.LIBMSI_PROPERTY_TYPE_INT;
|
||||
case LibmsiOLEVariantType.OLEVT_LPSTR:
|
||||
return LibmsiPropertyType.LIBMSI_PROPERTY_TYPE_STRING;
|
||||
case LibmsiOLEVariantType.OLEVT_FILETIME:
|
||||
return LibmsiPropertyType.LIBMSI_PROPERTY_TYPE_FILETIME;
|
||||
case LibmsiOLEVariantType.OLEVT_EMPTY:
|
||||
return LibmsiPropertyType.LIBMSI_PROPERTY_TYPE_EMPTY;
|
||||
default:
|
||||
error = new Exception($"Unknown type: {Properties[(int)prop].VariantType}");
|
||||
return LibmsiPropertyType.LIBMSI_PROPERTY_TYPE_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
/// <param name="prop">A LibmsiProperty to get</param>
|
||||
/// <param name="error">Exception to set on error, or null</param>
|
||||
/// <returns>The property value or null on failure.</returns.
|
||||
public string GetString(LibmsiProperty prop, ref Exception error)
|
||||
{
|
||||
if (error != null)
|
||||
return null;
|
||||
|
||||
GetProperty(prop, out LibmsiPropertyType type, out _, out _, out _, out _, out string val, ref error);
|
||||
return val;
|
||||
}
|
||||
|
||||
/// <param name="prop">A LibmsiProperty to get</param>
|
||||
/// <param name="error">Exception to set on error, or null</param>
|
||||
/// <returns>The property value or -1 on failure.</returns.
|
||||
public int GetInt(LibmsiProperty prop, ref Exception error)
|
||||
{
|
||||
if (error != null)
|
||||
return -1;
|
||||
|
||||
GetProperty(prop, out LibmsiPropertyType type, out int val, out _, out _, out _, out _, ref error);
|
||||
return val;
|
||||
}
|
||||
|
||||
/// <param name="prop">A LibmsiProperty to get</param>
|
||||
/// <param name="error">Exception to set on error, or null</param>
|
||||
/// <returns>The property value or 0 on failure.</returns.
|
||||
public ulong GetFiletime(LibmsiProperty prop, ref Exception error)
|
||||
{
|
||||
if (error != null)
|
||||
return 0;
|
||||
|
||||
GetProperty(prop, out LibmsiPropertyType type, out _, out ulong val, out _, out _, out _, ref error);
|
||||
return val;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set string property <paramref name="prop"/>.
|
||||
/// </summary>
|
||||
/// <param name="prop">A LibmsiProperty to set</param>
|
||||
/// <param name="value">A string value</param>
|
||||
/// <param name="error">Exception to set on error, or null</param>
|
||||
/// <returns>True on success</returns>
|
||||
public bool SetString(LibmsiProperty prop, string value, ref Exception error)
|
||||
{
|
||||
if (error != null)
|
||||
return false;
|
||||
|
||||
if (GetType(prop) != LibmsiOLEVariantType.OLEVT_LPSTR)
|
||||
{
|
||||
error = new Exception($"LIBMSI_RESULT_ERROR: {LibmsiResult.LIBMSI_RESULT_DATATYPE_MISMATCH}");
|
||||
return false;
|
||||
}
|
||||
|
||||
LibmsiResult ret = SetProperty(prop, LibmsiOLEVariantType.OLEVT_LPSTR, 0, 0, value);
|
||||
if (ret != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
error = new Exception($"LIBMSI_RESULT_ERROR: {ret}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set integer property <paramref name="prop"/>.
|
||||
/// </summary>
|
||||
/// <param name="prop">A LibmsiProperty to set</param>
|
||||
/// <param name="value">A value</param>
|
||||
/// <param name="error">Exception to set on error, or null</param>
|
||||
/// <returns>True on success</returns>
|
||||
public bool SetInt(LibmsiProperty prop, int value, ref Exception error)
|
||||
{
|
||||
if (error != null)
|
||||
return false;
|
||||
|
||||
LibmsiOLEVariantType type = GetType(prop);
|
||||
if (type != LibmsiOLEVariantType.OLEVT_I2 && type != LibmsiOLEVariantType.OLEVT_I4)
|
||||
{
|
||||
error = new Exception($"LIBMSI_RESULT_ERROR: {LibmsiResult.LIBMSI_RESULT_DATATYPE_MISMATCH}");
|
||||
return false;
|
||||
}
|
||||
|
||||
LibmsiResult ret = SetProperty(prop, type, value, 0, null);
|
||||
if (ret != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
error = new Exception($"LIBMSI_RESULT_ERROR: {ret}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set integer property <paramref name="prop"/>.
|
||||
/// </summary>
|
||||
/// <param name="prop">A LibmsiProperty to set</param>
|
||||
/// <param name="value">A value</param>
|
||||
/// <param name="error">Exception to set on error, or null</param>
|
||||
/// <returns>True on success</returns>
|
||||
public bool SetFiletime(LibmsiProperty prop, ulong value, ref Exception error)
|
||||
{
|
||||
if (error != null)
|
||||
return false;
|
||||
|
||||
if (GetType(prop) != LibmsiOLEVariantType.OLEVT_FILETIME)
|
||||
{
|
||||
error = new Exception($"LIBMSI_RESULT_ERROR: {LibmsiResult.LIBMSI_RESULT_DATATYPE_MISMATCH}");
|
||||
return false;
|
||||
}
|
||||
|
||||
LibmsiResult ret = SetProperty(prop, LibmsiOLEVariantType.OLEVT_FILETIME, 0, value, null);
|
||||
if (ret != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
error = new Exception($"LIBMSI_RESULT_ERROR: {ret}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save summary informations to the associated database.
|
||||
/// </summary>
|
||||
/// <param name="error">Exception to set on error, or null</param>
|
||||
/// <returns>True on success</returns>
|
||||
public bool Persist(ref Exception error)
|
||||
{
|
||||
if (error != null)
|
||||
return false;
|
||||
|
||||
if (Database == null)
|
||||
{
|
||||
error = new Exception("No database associated");
|
||||
return false;
|
||||
}
|
||||
|
||||
LibmsiResult ret = Persist(Database);
|
||||
if (ret != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
error = new Exception($"LIBMSI_RESULT_ERROR: {ret}");
|
||||
|
||||
return ret == LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save summary informations to the associated database.
|
||||
/// </summary>
|
||||
/// <param name="db">A LibmsiDatabase to save to</param>
|
||||
/// <param name="error">Exception to set on error, or null</param>
|
||||
/// <returns>True on success</returns>
|
||||
public bool Save(LibmsiDatabase db, ref Exception error)
|
||||
{
|
||||
if (db == null)
|
||||
return false;
|
||||
if (error != null)
|
||||
return false;
|
||||
|
||||
LibmsiResult ret = Persist(db);
|
||||
if (ret != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
error = new Exception($"LIBMSI_RESULT_ERROR: {ret}");
|
||||
|
||||
return ret == LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <returns>A new list with the list of set properties</returns>
|
||||
public List<int> GetProperties()
|
||||
{
|
||||
List<int> props = new List<int>();
|
||||
for (int i = 0; i < MSI_MAX_PROPS; i++)
|
||||
{
|
||||
if (Properties[i].VariantType != LibmsiOLEVariantType.OLEVT_EMPTY)
|
||||
props.Add(i);
|
||||
}
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Functions
|
||||
|
||||
internal string SummaryInfoAsString(int uiProperty)
|
||||
{
|
||||
LibmsiOLEVariant prop = Properties[uiProperty];
|
||||
|
||||
switch (prop.VariantType)
|
||||
{
|
||||
case LibmsiOLEVariantType.OLEVT_I2:
|
||||
case LibmsiOLEVariantType.OLEVT_I4:
|
||||
return prop.IntVal.ToString();
|
||||
case LibmsiOLEVariantType.OLEVT_LPSTR:
|
||||
return prop.StrVal;
|
||||
case LibmsiOLEVariantType.OLEVT_FILETIME:
|
||||
return FileTimeToString(prop.FileTime);
|
||||
case LibmsiOLEVariantType.OLEVT_EMPTY:
|
||||
return string.Empty;
|
||||
default:
|
||||
Console.Error.WriteLine($"Unknown type {prop.VariantType}");
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: Move to LibmsiDatabase
|
||||
internal static LibmsiResult AddSummaryInfo(LibmsiDatabase db, string[][] records, int num_records, int num_columns)
|
||||
{
|
||||
Exception err = null;
|
||||
LibmsiSummaryInfo si = Create(db, num_records * (num_columns / 2), ref err);
|
||||
if (si == null)
|
||||
{
|
||||
Console.Error.WriteLine("No summary information!");
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_records; i++)
|
||||
{
|
||||
for (int j = 0; j < num_columns; j += 2)
|
||||
{
|
||||
LibmsiResult r = ParseProp(records[i][j], records[i][j + 1], out LibmsiProperty pid, out int int_value, out ulong ft_value, out string str_value);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
//assert(GetType(pid) != LibmsiOLEVariantType.OLEVT_EMPTY);
|
||||
r = si.SetProperty(pid, GetType(pid), int_value, ft_value, str_value);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return si.Persist(db);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
// TODO: Move to LibmsiOLEVariant
|
||||
private static void FreeProp(LibmsiOLEVariant prop)
|
||||
{
|
||||
prop.VariantType = LibmsiOLEVariantType.OLEVT_EMPTY;
|
||||
}
|
||||
|
||||
private static LibmsiOLEVariantType GetType(LibmsiProperty uiProperty)
|
||||
{
|
||||
switch (uiProperty)
|
||||
{
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_CODEPAGE:
|
||||
return LibmsiOLEVariantType.OLEVT_I2;
|
||||
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_SUBJECT:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_AUTHOR:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_KEYWORDS:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_COMMENTS:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_TEMPLATE:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_LASTAUTHOR:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_UUID:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_APPNAME:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_TITLE:
|
||||
return LibmsiOLEVariantType.OLEVT_LPSTR;
|
||||
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_EDITTIME:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_LASTPRINTED:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_CREATED_TM:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_LASTSAVED_TM:
|
||||
return LibmsiOLEVariantType.OLEVT_FILETIME;
|
||||
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_SOURCE:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_RESTRICT:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_SECURITY:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_VERSION:
|
||||
return LibmsiOLEVariantType.OLEVT_I4;
|
||||
|
||||
default:
|
||||
Console.Error.WriteLine($"Invalid type {uiProperty}");
|
||||
break;
|
||||
}
|
||||
|
||||
return LibmsiOLEVariantType.OLEVT_EMPTY;
|
||||
}
|
||||
|
||||
private static int GetPropertyCount(LibmsiOLEVariant[] property)
|
||||
{
|
||||
int n = 0;
|
||||
if (property == null)
|
||||
return n;
|
||||
|
||||
for (int i = 0; i < MSI_MAX_PROPS; i++)
|
||||
{
|
||||
if (property[i].VariantType != LibmsiOLEVariantType.OLEVT_EMPTY)
|
||||
n++;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
private static ushort ReadWORD(byte[] data, ref int ofs)
|
||||
{
|
||||
ushort val = 0;
|
||||
val = data[ofs];
|
||||
val |= (ushort)(data[ofs + 1] << 8);
|
||||
ofs += 2;
|
||||
return val;
|
||||
}
|
||||
|
||||
private static uint ReadDWORD(byte[] data, ref int ofs)
|
||||
{
|
||||
uint val = 0;
|
||||
val = data[ofs];
|
||||
val |= (uint)(data[ofs + 1] << 8);
|
||||
val |= (uint)(data[ofs + 2] << 16);
|
||||
val |= (uint)(data[ofs + 3] << 24);
|
||||
ofs += 4;
|
||||
return val;
|
||||
}
|
||||
|
||||
private static string FileTimeToString(ulong ft)
|
||||
{
|
||||
DateTimeOffset dt = DateTimeOffset.FromUnixTimeSeconds((long)ft);
|
||||
return dt.ToString("yyyy/MM/dd hh:mm:ss");
|
||||
}
|
||||
|
||||
private static void ParseFileTime(string str, out ulong ft)
|
||||
{
|
||||
ft = 0;
|
||||
if (!DateTimeOffset.TryParse(str, out DateTimeOffset dt))
|
||||
return;
|
||||
|
||||
ft = (ulong)dt.ToUnixTimeSeconds();
|
||||
}
|
||||
|
||||
// FIXME: doesn't deal with endian conversion
|
||||
private static void ReadPropertiesFromData(LibmsiOLEVariant[] prop, byte[] data, int sz, int cProperties)
|
||||
{
|
||||
int idofs = 8;
|
||||
|
||||
// Now set all the properties
|
||||
for (int i = 0; i < cProperties; i++)
|
||||
{
|
||||
bool valid = true;
|
||||
LibmsiProperty propid = (LibmsiProperty)ReadDWORD(data, ref idofs);
|
||||
int dwOffset = (int)ReadDWORD(data, ref idofs);
|
||||
|
||||
if ((int)propid >= MSI_MAX_PROPS)
|
||||
{
|
||||
Console.Error.WriteLine($"Unknown property ID {propid}");
|
||||
break;
|
||||
}
|
||||
|
||||
LibmsiOLEVariantType type = GetType(propid);
|
||||
if (type == LibmsiOLEVariantType.OLEVT_EMPTY)
|
||||
{
|
||||
Console.Error.WriteLine($"propid {propid} has unknown type");
|
||||
break;
|
||||
}
|
||||
|
||||
LibmsiOLEVariant property = prop[(int)propid];
|
||||
|
||||
if (dwOffset + 4 > sz)
|
||||
{
|
||||
Console.Error.WriteLine($"Not enough data for type {dwOffset} {sz}");
|
||||
break;
|
||||
}
|
||||
|
||||
LibmsiOLEVariantType proptype = (LibmsiOLEVariantType)ReadDWORD(data, ref dwOffset);
|
||||
if (dwOffset + 4 > sz)
|
||||
{
|
||||
Console.Error.WriteLine($"Not enough data for type {dwOffset} {sz}");
|
||||
break;
|
||||
}
|
||||
|
||||
string str = null;
|
||||
switch (proptype)
|
||||
{
|
||||
case LibmsiOLEVariantType.OLEVT_I2:
|
||||
case LibmsiOLEVariantType.OLEVT_I4:
|
||||
property.IntVal = (int)ReadDWORD(data, ref dwOffset);
|
||||
break;
|
||||
|
||||
case LibmsiOLEVariantType.OLEVT_FILETIME:
|
||||
if (dwOffset + 8 > sz)
|
||||
{
|
||||
Console.Error.WriteLine($"Not enough data for type {dwOffset} {sz}");
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
property.FileTime = (ulong)ReadDWORD(data, ref dwOffset);
|
||||
property.FileTime |= (ulong)ReadDWORD(data, ref dwOffset) << 32;
|
||||
break;
|
||||
|
||||
case LibmsiOLEVariantType.OLEVT_LPSTR:
|
||||
int len = (int)ReadDWORD(data, ref dwOffset);
|
||||
if (len == 0 || dwOffset + len > sz)
|
||||
{
|
||||
Console.Error.WriteLine($"Not enough data for type {dwOffset} {len} {sz}");
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
str = Encoding.ASCII.GetString(data, dwOffset, len - 1) + "\0";
|
||||
break;
|
||||
|
||||
default:
|
||||
Console.Error.WriteLine($"Invalid type {proptype}");
|
||||
break;
|
||||
}
|
||||
|
||||
if (valid == false)
|
||||
break;
|
||||
|
||||
// Check the type is the same as we expect
|
||||
if (type == LibmsiOLEVariantType.OLEVT_LPSTR && proptype == LibmsiOLEVariantType.OLEVT_LPSTR)
|
||||
{
|
||||
property.StrVal = str;
|
||||
}
|
||||
else if (type == proptype)
|
||||
{
|
||||
// No-op
|
||||
}
|
||||
else if (proptype == LibmsiOLEVariantType.OLEVT_LPSTR)
|
||||
{
|
||||
if (str == null)
|
||||
return;
|
||||
|
||||
if (type == LibmsiOLEVariantType.OLEVT_I2 || type == LibmsiOLEVariantType.OLEVT_I4)
|
||||
{
|
||||
property.IntVal = int.Parse(new string(str.SkipWhile(c => char.IsWhiteSpace(c)).TakeWhile(c => c == '+' || c == '-' || char.IsDigit(c)).ToArray()));
|
||||
}
|
||||
else if (type == LibmsiOLEVariantType.OLEVT_FILETIME)
|
||||
{
|
||||
ParseFileTime(str, out ulong ft);
|
||||
property.FileTime = ft;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error.WriteLine($"Invalid type, it can't be converted");
|
||||
break;
|
||||
}
|
||||
|
||||
proptype = type;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error.WriteLine("Invalid type");
|
||||
break;
|
||||
}
|
||||
|
||||
// Now we now the type is valid, store it
|
||||
property.VariantType = proptype;
|
||||
}
|
||||
}
|
||||
|
||||
private LibmsiResult LoadSummaryInfo(GsfInput stm)
|
||||
{
|
||||
long sz = stm.Size;
|
||||
if (sz == 0)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
byte[] data = new byte[stm.Size];
|
||||
if (stm.Read((int)sz, data) == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
// Process the set header
|
||||
int ofs = 0;
|
||||
if (ReadWORD(data, ref ofs) != 0xfffe)
|
||||
{
|
||||
Console.Error.WriteLine("Property set not little-endian");
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
// Process the format header
|
||||
|
||||
// Check the format id is correct
|
||||
ofs = 28;
|
||||
if (fmtid_SummaryInformation.SequenceEqual(new ReadOnlySpan<byte>(data, ofs, 16).ToArray()))
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
// Seek to the location of the section
|
||||
ofs += 16;
|
||||
int dwOffset = (int)ReadDWORD(data, ref ofs);
|
||||
|
||||
// Read the section itself
|
||||
ofs = dwOffset;
|
||||
int cbSection = (int)ReadDWORD(data, ref ofs);
|
||||
int cProperties = (int)ReadDWORD(data, ref ofs);
|
||||
|
||||
if (cProperties > MSI_MAX_PROPS)
|
||||
{
|
||||
Console.Error.WriteLine($"Too many properties {cProperties}");
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
// Read all the data in one go
|
||||
ReadPropertiesFromData(Properties, data.Skip(dwOffset).ToArray(), cbSection, cProperties);
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
private static int WriteWORD(byte[] data, int ofs, ushort val)
|
||||
{
|
||||
if (data != null)
|
||||
{
|
||||
data[ofs++] = (byte)(val & 0xff);
|
||||
data[ofs++] = (byte)((val >> 8) & 0xff);
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
private static int WriteDWORD(byte[] data, int ofs, uint val)
|
||||
{
|
||||
if (data != null)
|
||||
{
|
||||
data[ofs++] = (byte)(val & 0xff);
|
||||
data[ofs++] = (byte)((val >> 8) & 0xff);
|
||||
data[ofs++] = (byte)((val >> 16) & 0xff);
|
||||
data[ofs++] = (byte)((val >> 24) & 0xff);
|
||||
}
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
private static int WriteFileTime(byte[] data, int ofs, ulong ft)
|
||||
{
|
||||
WriteDWORD(data, ofs, (uint)(ft & 0xFFFFFFFFUL));
|
||||
WriteDWORD(data, ofs + 4, (uint)(ft >> 32));
|
||||
return 8;
|
||||
}
|
||||
|
||||
private static int WriteString(byte[] data, int ofs, string str)
|
||||
{
|
||||
int len = str.Length + 1;
|
||||
WriteDWORD(data, ofs, (uint)len);
|
||||
if (data != null)
|
||||
Array.Copy(Encoding.ASCII.GetBytes(str + "\0"), 0, data, ofs + 4, len);
|
||||
|
||||
return (7 + len) & ~3;
|
||||
}
|
||||
|
||||
// TODO: Move to LibmsiOLEVariant
|
||||
private static int WritePropertyToData(LibmsiOLEVariant prop, byte[] data, int ofs)
|
||||
{
|
||||
int sz = ofs;
|
||||
if (prop.VariantType == LibmsiOLEVariantType.OLEVT_EMPTY)
|
||||
return sz;
|
||||
|
||||
// Add the type
|
||||
sz += WriteDWORD(data, sz, (uint)prop.VariantType);
|
||||
switch (prop.VariantType)
|
||||
{
|
||||
case LibmsiOLEVariantType.OLEVT_I2:
|
||||
case LibmsiOLEVariantType.OLEVT_I4:
|
||||
sz += WriteDWORD(data, sz, (uint)prop.IntVal);
|
||||
break;
|
||||
|
||||
case LibmsiOLEVariantType.OLEVT_FILETIME:
|
||||
sz += WriteFileTime(data, sz, prop.FileTime);
|
||||
break;
|
||||
|
||||
case LibmsiOLEVariantType.OLEVT_LPSTR:
|
||||
sz += WriteString(data, sz, prop.StrVal);
|
||||
break;
|
||||
|
||||
default:
|
||||
Console.Error.WriteLine($"Invalid type {prop.VariantType}");
|
||||
break;
|
||||
}
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
private LibmsiResult Persist(LibmsiDatabase database)
|
||||
{
|
||||
// Add up how much space the data will take and calculate the offsets
|
||||
int cProperties = GetPropertyCount(Properties);
|
||||
int cbSection = 8 + cProperties * 8;
|
||||
for (int i = 0; i < MSI_MAX_PROPS; i++)
|
||||
{
|
||||
cbSection += WritePropertyToData(Properties[i], null, 0);
|
||||
}
|
||||
|
||||
int sz = 28 + 20 + cbSection;
|
||||
byte[] data = new byte[sz];
|
||||
|
||||
// Write the set header
|
||||
sz = 0;
|
||||
sz += WriteWORD(data, sz, 0xfffe); // wByteOrder
|
||||
sz += WriteWORD(data, sz, 0); // wFormat
|
||||
sz += WriteDWORD(data, sz, 0x00020005); // dwOSVer - build 5, platform id 2
|
||||
sz += 16; // clsID
|
||||
sz += WriteDWORD(data, sz, 1); // reserved
|
||||
|
||||
// Write the format header
|
||||
Array.Copy(fmtid_SummaryInformation, 0, data, sz, 16);
|
||||
sz += 16;
|
||||
|
||||
sz += WriteDWORD(data, sz, 28 + 20); // dwOffset
|
||||
//assert(sz == 28 + 20);
|
||||
|
||||
// Write the section header
|
||||
sz += WriteDWORD(data, sz, (uint)cbSection);
|
||||
sz += WriteDWORD(data, sz, (uint)cProperties);
|
||||
//assert(sz == 28 + 20 + 8);
|
||||
|
||||
int dwOffset = 8 + cProperties * 8;
|
||||
for (int i = 0; i < MSI_MAX_PROPS; i++)
|
||||
{
|
||||
int propsz = WritePropertyToData(Properties[i], null, 0);
|
||||
if (propsz == 0)
|
||||
continue;
|
||||
|
||||
sz += WriteDWORD(data, sz, (uint)i);
|
||||
sz += WriteDWORD(data, sz, (uint)dwOffset);
|
||||
dwOffset += propsz;
|
||||
}
|
||||
|
||||
//assert(dwOffset == cbSection);
|
||||
|
||||
// Write out the data
|
||||
for (int i = 0; i < MSI_MAX_PROPS; i++)
|
||||
{
|
||||
sz += WritePropertyToData(Properties[i], data, sz);
|
||||
}
|
||||
|
||||
//assert(sz == 28 + 20 + cbSection);
|
||||
|
||||
LibmsiResult r = database.WriteRawStreamData(szSumInfo, data, sz, out GsfInput stm);
|
||||
return r;
|
||||
}
|
||||
|
||||
private void GetProperty(
|
||||
LibmsiProperty uiProperty,
|
||||
out LibmsiPropertyType puiDataType,
|
||||
out int pintvalue,
|
||||
out ulong pftValue,
|
||||
out string szValueBuf,
|
||||
out int pcchValueBuf,
|
||||
out string str,
|
||||
ref Exception error)
|
||||
{
|
||||
puiDataType = 0; pintvalue = 0; pftValue = 0; szValueBuf = null; pcchValueBuf = 0; str = null;
|
||||
if ((int)uiProperty >= MSI_MAX_PROPS)
|
||||
{
|
||||
error = new Exception($"Unknown property: {uiProperty}");
|
||||
return;
|
||||
}
|
||||
|
||||
LibmsiOLEVariant prop = Properties[(int)uiProperty];
|
||||
LibmsiPropertyType type;
|
||||
switch (prop.VariantType)
|
||||
{
|
||||
case LibmsiOLEVariantType.OLEVT_I2:
|
||||
case LibmsiOLEVariantType.OLEVT_I4:
|
||||
type = LibmsiPropertyType.LIBMSI_PROPERTY_TYPE_INT;
|
||||
pintvalue = prop.IntVal;
|
||||
break;
|
||||
|
||||
case LibmsiOLEVariantType.OLEVT_LPSTR:
|
||||
type = LibmsiPropertyType.LIBMSI_PROPERTY_TYPE_STRING;
|
||||
str = prop.StrVal;
|
||||
|
||||
int len = prop.StrVal.Length;
|
||||
szValueBuf = prop.StrVal.Substring(Math.Min(len, pcchValueBuf));
|
||||
if (len >= pcchValueBuf)
|
||||
error = new Exception("The given string is too small");
|
||||
|
||||
pcchValueBuf = len;
|
||||
break;
|
||||
|
||||
case LibmsiOLEVariantType.OLEVT_FILETIME:
|
||||
type = LibmsiPropertyType.LIBMSI_PROPERTY_TYPE_FILETIME;
|
||||
pftValue = prop.FileTime;
|
||||
break;
|
||||
|
||||
case LibmsiOLEVariantType.OLEVT_EMPTY:
|
||||
// FIXME: should be replaced by a has_property() instead?
|
||||
error = new Exception("Empty property");
|
||||
type = LibmsiPropertyType.LIBMSI_PROPERTY_TYPE_EMPTY;
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
puiDataType = type;
|
||||
}
|
||||
|
||||
private LibmsiResult SetProperty(LibmsiProperty uiProperty, LibmsiOLEVariantType type, int intvalue, ulong pftValue, string szValue)
|
||||
{
|
||||
if (type == LibmsiOLEVariantType.OLEVT_LPSTR && szValue == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
if (type == LibmsiOLEVariantType.OLEVT_FILETIME && pftValue == 0)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
LibmsiOLEVariant prop = Properties[(int)uiProperty];
|
||||
|
||||
if (prop.VariantType == LibmsiOLEVariantType.OLEVT_EMPTY)
|
||||
{
|
||||
LibmsiResult ret = LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
if (UpdateCount == 0)
|
||||
return ret;
|
||||
|
||||
UpdateCount--;
|
||||
}
|
||||
else if (prop.VariantType != type)
|
||||
{
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
FreeProp(prop);
|
||||
prop.VariantType = type;
|
||||
switch (type)
|
||||
{
|
||||
case LibmsiOLEVariantType.OLEVT_I2:
|
||||
case LibmsiOLEVariantType.OLEVT_I4:
|
||||
prop.IntVal = intvalue;
|
||||
break;
|
||||
case LibmsiOLEVariantType.OLEVT_FILETIME:
|
||||
prop.FileTime = pftValue;
|
||||
break;
|
||||
case LibmsiOLEVariantType.OLEVT_LPSTR:
|
||||
prop.StrVal = szValue;
|
||||
break;
|
||||
default:
|
||||
Console.Error.WriteLine($"Invalid type {type}");
|
||||
break;
|
||||
}
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
private static LibmsiResult ParseProp(string prop, string value, out LibmsiProperty pid, out int int_value, out ulong ft_value, out string str_value)
|
||||
{
|
||||
pid = (LibmsiProperty)int.Parse(new string(prop.SkipWhile(c => char.IsWhiteSpace(c)).TakeWhile(c => c == '+' || c == '-' || char.IsDigit(c)).ToArray()));
|
||||
int_value = 0; ft_value = 0; str_value = null;
|
||||
|
||||
switch (pid)
|
||||
{
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_CODEPAGE:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_SOURCE:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_RESTRICT:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_SECURITY:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_VERSION:
|
||||
int_value = int.Parse(new string(value.SkipWhile(c => char.IsWhiteSpace(c)).TakeWhile(c => c == '+' || c == '-' || char.IsDigit(c)).ToArray()));
|
||||
break;
|
||||
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_EDITTIME:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_LASTPRINTED:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_CREATED_TM:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_LASTSAVED_TM:
|
||||
ParseFileTime(value, out ft_value);
|
||||
break;
|
||||
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_SUBJECT:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_AUTHOR:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_KEYWORDS:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_COMMENTS:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_TEMPLATE:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_LASTAUTHOR:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_UUID:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_APPNAME:
|
||||
case LibmsiProperty.LIBMSI_PROPERTY_TITLE:
|
||||
str_value = value;
|
||||
break;
|
||||
|
||||
default:
|
||||
Console.Error.WriteLine($"Unhandled prop id {pid}");
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
134
BurnOutSharp/External/libmsi/LibmsiTypes.cs
vendored
134
BurnOutSharp/External/libmsi/LibmsiTypes.cs
vendored
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2002,2003 Mike McCormack
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
namespace LibMSI
|
||||
{
|
||||
/// <remarks>Also known as LibmsiResultError</remarks>
|
||||
public enum LibmsiResult
|
||||
{
|
||||
LIBMSI_RESULT_SUCCESS, /* FIXME: remove me */
|
||||
LIBMSI_RESULT_ACCESS_DENIED,
|
||||
LIBMSI_RESULT_INVALID_HANDLE,
|
||||
LIBMSI_RESULT_NOT_ENOUGH_MEMORY,
|
||||
LIBMSI_RESULT_INVALID_DATA,
|
||||
LIBMSI_RESULT_OUTOFMEMORY,
|
||||
LIBMSI_RESULT_INVALID_PARAMETER,
|
||||
LIBMSI_RESULT_OPEN_FAILED,
|
||||
LIBMSI_RESULT_CALL_NOT_IMPLEMENTED,
|
||||
LIBMSI_RESULT_MORE_DATA,
|
||||
LIBMSI_RESULT_NOT_FOUND,
|
||||
LIBMSI_RESULT_CONTINUE,
|
||||
LIBMSI_RESULT_UNKNOWN_PROPERTY,
|
||||
LIBMSI_RESULT_BAD_QUERY_SYNTAX,
|
||||
LIBMSI_RESULT_INVALID_FIELD,
|
||||
LIBMSI_RESULT_FUNCTION_FAILED,
|
||||
LIBMSI_RESULT_INVALID_TABLE,
|
||||
LIBMSI_RESULT_DATATYPE_MISMATCH,
|
||||
LIBMSI_RESULT_INVALID_DATATYPE,
|
||||
|
||||
NO_MORE_ITEMS = int.MaxValue,
|
||||
}
|
||||
|
||||
public enum LibmsiPropertyType
|
||||
{
|
||||
LIBMSI_PROPERTY_TYPE_EMPTY = 0,
|
||||
LIBMSI_PROPERTY_TYPE_INT = 1,
|
||||
LIBMSI_PROPERTY_TYPE_STRING = 2,
|
||||
LIBMSI_PROPERTY_TYPE_FILETIME = 3,
|
||||
}
|
||||
|
||||
public enum LibmsiColInfo
|
||||
{
|
||||
LIBMSI_COL_INFO_NAMES = 0,
|
||||
LIBMSI_COL_INFO_TYPES = 1
|
||||
}
|
||||
|
||||
public enum LibmsiDbFlags
|
||||
{
|
||||
LIBMSI_DB_FLAGS_READONLY = 1 << 0,
|
||||
LIBMSI_DB_FLAGS_CREATE = 1 << 1,
|
||||
LIBMSI_DB_FLAGS_TRANSACT = 1 << 2,
|
||||
LIBMSI_DB_FLAGS_PATCH = 1 << 3,
|
||||
}
|
||||
|
||||
public enum LibmsiDBError
|
||||
{
|
||||
LIBMSI_DB_ERROR_SUCCESS, /* FIXME: remove me */
|
||||
LIBMSI_DB_ERROR_INVALIDARG,
|
||||
LIBMSI_DB_ERROR_MOREDATA,
|
||||
LIBMSI_DB_ERROR_FUNCTIONERROR,
|
||||
LIBMSI_DB_ERROR_DUPLICATEKEY,
|
||||
LIBMSI_DB_ERROR_REQUIRED,
|
||||
LIBMSI_DB_ERROR_BADLINK,
|
||||
LIBMSI_DB_ERROR_OVERFLOW,
|
||||
LIBMSI_DB_ERROR_UNDERFLOW,
|
||||
LIBMSI_DB_ERROR_NOTINSET,
|
||||
LIBMSI_DB_ERROR_BADVERSION,
|
||||
LIBMSI_DB_ERROR_BADCASE,
|
||||
LIBMSI_DB_ERROR_BADGUID,
|
||||
LIBMSI_DB_ERROR_BADWILDCARD,
|
||||
LIBMSI_DB_ERROR_BADIDENTIFIER,
|
||||
LIBMSI_DB_ERROR_BADLANGUAGE,
|
||||
LIBMSI_DB_ERROR_BADFILENAME,
|
||||
LIBMSI_DB_ERROR_BADPATH,
|
||||
LIBMSI_DB_ERROR_BADCONDITION,
|
||||
LIBMSI_DB_ERROR_BADFORMATTED,
|
||||
LIBMSI_DB_ERROR_BADTEMPLATE,
|
||||
LIBMSI_DB_ERROR_BADDEFAULTDIR,
|
||||
LIBMSI_DB_ERROR_BADREGPATH,
|
||||
LIBMSI_DB_ERROR_BADCUSTOMSOURCE,
|
||||
LIBMSI_DB_ERROR_BADPROPERTY,
|
||||
LIBMSI_DB_ERROR_MISSINGDATA,
|
||||
LIBMSI_DB_ERROR_BADCATEGORY,
|
||||
LIBMSI_DB_ERROR_BADKEYTABLE,
|
||||
LIBMSI_DB_ERROR_BADMAXMINVALUES,
|
||||
LIBMSI_DB_ERROR_BADCABINET,
|
||||
LIBMSI_DB_ERROR_BADSHORTCUT,
|
||||
LIBMSI_DB_ERROR_STRINGOVERFLOW,
|
||||
LIBMSI_DB_ERROR_BADLOCALIZEATTRIB
|
||||
}
|
||||
|
||||
public enum LibmsiProperty
|
||||
{
|
||||
LIBMSI_PROPERTY_DICTIONARY = 0,
|
||||
LIBMSI_PROPERTY_CODEPAGE = 1,
|
||||
LIBMSI_PROPERTY_TITLE = 2,
|
||||
LIBMSI_PROPERTY_SUBJECT = 3,
|
||||
LIBMSI_PROPERTY_AUTHOR = 4,
|
||||
LIBMSI_PROPERTY_KEYWORDS = 5,
|
||||
LIBMSI_PROPERTY_COMMENTS = 6,
|
||||
LIBMSI_PROPERTY_TEMPLATE = 7,
|
||||
LIBMSI_PROPERTY_LASTAUTHOR = 8,
|
||||
LIBMSI_PROPERTY_UUID = 9,
|
||||
LIBMSI_PROPERTY_EDITTIME = 10,
|
||||
LIBMSI_PROPERTY_LASTPRINTED = 11,
|
||||
LIBMSI_PROPERTY_CREATED_TM = 12,
|
||||
LIBMSI_PROPERTY_LASTSAVED_TM = 13,
|
||||
LIBMSI_PROPERTY_VERSION = 14,
|
||||
LIBMSI_PROPERTY_SOURCE = 15,
|
||||
LIBMSI_PROPERTY_RESTRICT = 16,
|
||||
LIBMSI_PROPERTY_THUMBNAIL = 17,
|
||||
LIBMSI_PROPERTY_APPNAME = 18,
|
||||
LIBMSI_PROPERTY_SECURITY = 19
|
||||
}
|
||||
|
||||
public static class LibmsiTypes
|
||||
{
|
||||
public const uint LIBMSI_NULL_INT = 0x80000000;
|
||||
}
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
/*
|
||||
* Implementation of the Microsoft Installer (msi.dll)
|
||||
*
|
||||
* Copyright 2006 Mike McCormack
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
using LibGSF.Input;
|
||||
using static LibMSI.LibmsiQuery;
|
||||
|
||||
namespace LibMSI.Views
|
||||
{
|
||||
internal class LibmsiAlterView : LibmsiView
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public LibmsiDatabase Database { get; set; }
|
||||
|
||||
public LibmsiView Table { get; set; }
|
||||
|
||||
public column_info ColInfo { get; set; }
|
||||
|
||||
public int Hold { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private LibmsiAlterView() { }
|
||||
|
||||
public static LibmsiResult Create(LibmsiDatabase db, out LibmsiView view, string name, column_info colinfo, int hold)
|
||||
{
|
||||
LibmsiAlterView av = new LibmsiAlterView();
|
||||
LibmsiResult r = LibmsiTableView.Create(db, name, out LibmsiView table);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
view = null;
|
||||
return r;
|
||||
}
|
||||
|
||||
av.Table = table;
|
||||
|
||||
if (colinfo != null)
|
||||
colinfo.Table = name;
|
||||
|
||||
// Fill the structure
|
||||
av.Database = db;
|
||||
av.Hold = hold;
|
||||
av.ColInfo = colinfo;
|
||||
|
||||
view = av;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult FetchInt(int row, int col, out int val)
|
||||
{
|
||||
val = default;
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult FetchStream(int row, int col, out GsfInput stm)
|
||||
{
|
||||
stm = null;
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetRow(int row, out LibmsiRecord rec) => Table.GetRow(row, out rec);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Execute(LibmsiRecord record)
|
||||
{
|
||||
if (Hold == 1)
|
||||
{
|
||||
Table.AddRef();
|
||||
}
|
||||
else if (Hold == -1)
|
||||
{
|
||||
int r = Table.Release();
|
||||
if (r == 0)
|
||||
Table = null;
|
||||
}
|
||||
|
||||
if (ColInfo != null)
|
||||
return AddColumn();
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Close() => LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetDimensions(out int rows, out int cols)
|
||||
{
|
||||
rows = 0; cols = 0;
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetColumnInfo(int n, out string name, out int type, out bool temporary, out string table_name)
|
||||
{
|
||||
name = null; type = 0; temporary = false; table_name = null;
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Delete()
|
||||
{
|
||||
if (Table != null)
|
||||
Table.Delete();
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult FindMatchingRows(int col, int val, out int row, ref LibmsiColumnHashEntry handle)
|
||||
{
|
||||
row = 0;
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private static LibmsiResult CountIter(LibmsiRecord row, object param)
|
||||
{
|
||||
if (param is int)
|
||||
param = (int)param + 1;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
private bool CheckColumnExists(LibmsiDatabase db, string table, string column)
|
||||
{
|
||||
string query = $"SELECT * FROM `_Columns` WHERE `Table`='{table}' AND `Name`='{column}'";
|
||||
LibmsiResult r = QueryOpen(db, out LibmsiQuery view, query);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return false;
|
||||
|
||||
r = view.Execute(null);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
goto done;
|
||||
|
||||
r = view.Fetch(out LibmsiRecord rec);
|
||||
if (r == LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
rec = null;
|
||||
|
||||
done:
|
||||
view = null;
|
||||
return (r == LibmsiResult.LIBMSI_RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
private LibmsiResult AddColumn()
|
||||
{
|
||||
int colnum = 1;
|
||||
|
||||
string szColumns = "_Columns";
|
||||
LibmsiResult r = LibmsiTableView.Create(Database, szColumns, out LibmsiView columns);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
if (CheckColumnExists(Database, ColInfo.Table, ColInfo.Column))
|
||||
{
|
||||
columns.Delete();
|
||||
return LibmsiResult.LIBMSI_RESULT_BAD_QUERY_SYNTAX;
|
||||
}
|
||||
|
||||
string query = $"SELECT * FROM `_Columns` WHERE `Table`='{ColInfo.Table}' ORDER BY `Number`";
|
||||
r = QueryOpen(Database, out LibmsiQuery view, query);
|
||||
if (r == LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
int count = 0;
|
||||
r = view.IterateRecords(ref count, CountIter, colnum);
|
||||
view = null;
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
columns.Delete();
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
r = columns.AddColumn(ColInfo.Table, colnum, ColInfo.Column, ColInfo.Type, (Hold == 1));
|
||||
|
||||
columns.Delete();
|
||||
return r;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* Implementation of the Microsoft Installer (msi.dll)
|
||||
*
|
||||
* Copyright 2002-2004 Mike McCormack for CodeWeavers
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
using LibMSI.Internal;
|
||||
using static LibMSI.Internal.LibmsiTable;
|
||||
using static LibMSI.Internal.MsiPriv;
|
||||
|
||||
namespace LibMSI.Views
|
||||
{
|
||||
internal class LibmsiCreateView : LibmsiView
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public LibmsiDatabase Database { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public bool IsTemp { get; set; }
|
||||
|
||||
public bool Hold { get; set; }
|
||||
|
||||
public column_info ColInfo { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private LibmsiCreateView() { }
|
||||
|
||||
public static LibmsiResult Create(LibmsiDatabase db, out LibmsiView view, string table, column_info col_info, bool hold)
|
||||
{
|
||||
view = null;
|
||||
|
||||
LibmsiResult r = CheckColumns(col_info);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
LibmsiCreateView cv = new LibmsiCreateView();
|
||||
|
||||
bool temp = true;
|
||||
bool tempprim = false;
|
||||
|
||||
column_info col;
|
||||
for (col = col_info; col != null; col = col.Next)
|
||||
{
|
||||
if (col.Table == null)
|
||||
col.Table = table;
|
||||
|
||||
if (!col.Temporary)
|
||||
temp = false;
|
||||
else if ((col.Type & MSITYPE_KEY) != 0)
|
||||
tempprim = true;
|
||||
}
|
||||
|
||||
if (!temp && tempprim)
|
||||
{
|
||||
cv = null;
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
// Fill the structure
|
||||
cv.Database = db;
|
||||
cv.Name = table;
|
||||
cv.ColInfo = col_info;
|
||||
cv.IsTemp = temp;
|
||||
cv.Hold = hold;
|
||||
|
||||
view = cv;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult FetchInt(int row, int col, out int val)
|
||||
{
|
||||
val = 0;
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Execute(LibmsiRecord record)
|
||||
{
|
||||
LibmsiCondition persist = (IsTemp) ? LibmsiCondition.LIBMSI_CONDITION_FALSE : LibmsiCondition.LIBMSI_CONDITION_TRUE;
|
||||
if (IsTemp && !Hold)
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
|
||||
return CreateTable(Database, Name, ColInfo, persist);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Close() => LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetDimensions(out int rows, out int cols)
|
||||
{
|
||||
rows = 0; cols = 0;
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetColumnInfo(int n, out string name, out int type, out bool temporary, out string table_name)
|
||||
{
|
||||
name = null; type = 0; temporary = false; table_name = null;
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Delete()
|
||||
{
|
||||
Database = null;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private static LibmsiResult CheckColumns(column_info col_info)
|
||||
{
|
||||
column_info c1, c2;
|
||||
|
||||
// Check for two columns with the same name
|
||||
for (c1 = col_info; c1 != null; c1 = c1.Next )
|
||||
{
|
||||
for (c2 = c1.Next; c2 != null; c2 = c2.Next)
|
||||
{
|
||||
if (c1.Column == c2.Column)
|
||||
return LibmsiResult.LIBMSI_RESULT_BAD_QUERY_SYNTAX;
|
||||
}
|
||||
}
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* Implementation of the Microsoft Installer (msi.dll)
|
||||
*
|
||||
* Copyright 2002-2005 Mike McCormack for CodeWeavers
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
using LibGSF.Input;
|
||||
|
||||
namespace LibMSI.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Code to delete rows from a table.
|
||||
/// <summary>
|
||||
/// <remarks>
|
||||
/// We delete rows by blanking them out rather than trying to remove the row.
|
||||
/// This appears to be what the native MSI does (or tries to do). For the query:
|
||||
///
|
||||
/// delete from Property
|
||||
///
|
||||
/// some non-zero entries are left in the table by native MSI. I'm not sure if
|
||||
/// that's a bug in the way I'm running the query, or a just a bug.
|
||||
internal class LibmsiDeleteView : LibmsiView
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public LibmsiDatabase Database { get; set; }
|
||||
|
||||
public LibmsiView Table { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private LibmsiDeleteView() { }
|
||||
|
||||
public static LibmsiResult Create(LibmsiDatabase db, out LibmsiView view, LibmsiView table)
|
||||
{
|
||||
LibmsiDeleteView dv = new LibmsiDeleteView
|
||||
{
|
||||
Database = db,
|
||||
Table = table,
|
||||
};
|
||||
|
||||
view = dv;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult FetchInt(int row, int col, out int val)
|
||||
{
|
||||
val = 0;
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult FetchStream(int row, int col, out GsfInput stm)
|
||||
{
|
||||
stm = null;
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Execute(LibmsiRecord record)
|
||||
{
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
LibmsiResult r = Table.Execute(record);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
r = Table.GetDimensions(out int rows, out int cols);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
// Blank out all the rows that match */
|
||||
for (int i = 0; i < rows; i++)
|
||||
{
|
||||
Table.DeleteRow(i);
|
||||
}
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Close()
|
||||
{
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
return Table.Close();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetDimensions(out int rows, out int cols)
|
||||
{
|
||||
rows = 0; cols = 0;
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
rows = 0;
|
||||
return Table.GetDimensions(out _, out cols);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetColumnInfo(int n, out string name, out int type, out bool temporary, out string table_name)
|
||||
{
|
||||
name = null; type = 0; temporary = false; table_name = null;
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
return Table.GetColumnInfo(n, out name, out type, out temporary, out table_name);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Delete()
|
||||
{
|
||||
if (Table != null)
|
||||
Table.Delete();
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult FindMatchingRows(int col, int val, out int row, ref LibmsiColumnHashEntry handle)
|
||||
{
|
||||
row = 0;
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,260 +0,0 @@
|
||||
/*
|
||||
* Implementation of the Microsoft Installer (msi.dll)
|
||||
*
|
||||
* Copyright 2002 Mike McCormack for CodeWeavers
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace LibMSI.Views
|
||||
{
|
||||
internal class LibmsiDistinctSet
|
||||
{
|
||||
public int Val { get; set; }
|
||||
|
||||
public int Count { get; set; }
|
||||
|
||||
public int Row { get; set; }
|
||||
|
||||
public LibmsiDistinctSet NextRow { get; set; }
|
||||
|
||||
public LibmsiDistinctSet NextCol { get; set; }
|
||||
}
|
||||
|
||||
internal class LibmsiDistinctView : LibmsiView
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public LibmsiDatabase Database { get; set; }
|
||||
|
||||
public LibmsiView Table { get; set; }
|
||||
|
||||
public int RowCount { get; set; }
|
||||
|
||||
public int[] Translation { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private LibmsiDistinctView() { }
|
||||
|
||||
public static LibmsiResult Create(LibmsiDatabase db, out LibmsiView view, LibmsiView table)
|
||||
{
|
||||
view = null;
|
||||
LibmsiResult r = table.GetDimensions(out _, out int count);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
Console.Error.WriteLine("Can't get table dimensions");
|
||||
return r;
|
||||
}
|
||||
|
||||
LibmsiDistinctView dv = new LibmsiDistinctView
|
||||
{
|
||||
Database = db,
|
||||
Table = table,
|
||||
Translation = null,
|
||||
RowCount = 0,
|
||||
};
|
||||
|
||||
view = dv;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult FetchInt(int row, int col, out int val)
|
||||
{
|
||||
val = 0;
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
if (row >= RowCount)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
row = Translation[row];
|
||||
|
||||
return Table.FetchInt(row, col, out val);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Execute(LibmsiRecord record)
|
||||
{
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
LibmsiResult r = Table.Execute(record);
|
||||
if( r != LibmsiResult.LIBMSI_RESULT_SUCCESS )
|
||||
return r;
|
||||
|
||||
r = Table.GetDimensions(out int r_count, out int c_count );
|
||||
if( r != LibmsiResult.LIBMSI_RESULT_SUCCESS )
|
||||
return r;
|
||||
|
||||
Translation = new int[r_count];
|
||||
|
||||
// Build it
|
||||
LibmsiDistinctSet rowset = null;
|
||||
for (int i = 0; i < r_count; i++ )
|
||||
{
|
||||
LibmsiDistinctSet x = rowset;
|
||||
|
||||
for (int j = 1; j <= c_count; j++)
|
||||
{
|
||||
r = Table.FetchInt(i, j, out int val);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to fetch int at {i} {j}");
|
||||
DistinctFree(rowset);
|
||||
return r;
|
||||
}
|
||||
|
||||
x = DistinctInsert(x, val, i);
|
||||
if(x == null)
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to insert at {i} {j}");
|
||||
DistinctFree(rowset);
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
if (j != c_count)
|
||||
x = x.NextCol;
|
||||
}
|
||||
|
||||
// Check if it was distinct and if so, include it
|
||||
if (x.Row == i )
|
||||
Translation[RowCount++] = i;
|
||||
}
|
||||
|
||||
DistinctFree( rowset );
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Close()
|
||||
{
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
Translation = null;
|
||||
RowCount = 0;
|
||||
|
||||
return Table.Close();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetDimensions(out int rows, out int cols)
|
||||
{
|
||||
rows = 0; cols = 0;
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
if (Translation == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
rows = RowCount;
|
||||
return Table.GetDimensions(out _, out cols);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetColumnInfo(int n, out string name, out int type, out bool temporary, out string table_name)
|
||||
{
|
||||
name = null; type = 0; temporary = false; table_name = null;
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
return Table.GetColumnInfo(n, out name, out type, out temporary, out table_name);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Delete()
|
||||
{
|
||||
if (Table != null)
|
||||
Table.Delete();
|
||||
|
||||
Translation = null;
|
||||
Database = null;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult FindMatchingRows(int col, int val, out int row, ref LibmsiColumnHashEntry handle)
|
||||
{
|
||||
row = 0;
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
LibmsiResult r = Table.FindMatchingRows(col, val, out row, ref handle);
|
||||
if (row > RowCount)
|
||||
return LibmsiResult.NO_MORE_ITEMS;
|
||||
|
||||
row = Translation[row];
|
||||
return r;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private static LibmsiDistinctSet DistinctInsert(LibmsiDistinctSet x, int val, int row)
|
||||
{
|
||||
// Horrible O(n) find
|
||||
while (x != null)
|
||||
{
|
||||
if (x.Val == val)
|
||||
{
|
||||
x.Count++;
|
||||
return x;
|
||||
}
|
||||
|
||||
x = x.NextRow;
|
||||
}
|
||||
|
||||
// Nothing found, so add one
|
||||
x = new LibmsiDistinctSet
|
||||
{
|
||||
Val = val,
|
||||
Count = 1,
|
||||
Row = row,
|
||||
NextRow = null,
|
||||
NextCol = null,
|
||||
};
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
private static void DistinctFree(LibmsiDistinctSet x)
|
||||
{
|
||||
while (x != null)
|
||||
{
|
||||
LibmsiDistinctSet next = x.NextRow;
|
||||
DistinctFree(x.NextCol);
|
||||
x = next;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
102
BurnOutSharp/External/libmsi/Views/LibmsiDropView.cs
vendored
102
BurnOutSharp/External/libmsi/Views/LibmsiDropView.cs
vendored
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Implementation of the Microsoft Installer (msi.dll)
|
||||
*
|
||||
* Copyright 2008 James Hawkins
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
namespace LibMSI.Views
|
||||
{
|
||||
internal class LibmsiDropView : LibmsiView
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public LibmsiDatabase Database { get; set; }
|
||||
|
||||
public LibmsiView Table { get; set; }
|
||||
|
||||
public column_info ColInfo { get; set; }
|
||||
|
||||
public int Hold { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary
|
||||
private LibmsiDropView() { }
|
||||
|
||||
public static LibmsiResult Create(LibmsiDatabase db, out LibmsiView view, string name)
|
||||
{
|
||||
view = null;
|
||||
|
||||
LibmsiDropView dv = new LibmsiDropView();
|
||||
LibmsiResult r = LibmsiTableView.Create(db, name, out LibmsiView table);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
dv = null;
|
||||
return r;
|
||||
}
|
||||
|
||||
dv.Table = table;
|
||||
dv.Database = db;
|
||||
|
||||
view = dv;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Execute(LibmsiRecord record)
|
||||
{
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
LibmsiResult r = Table.Execute(record);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
return Table.Drop();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Close() => LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetDimensions(out int rows, out int cols)
|
||||
{
|
||||
rows = 0; cols = 0;
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Delete()
|
||||
{
|
||||
if (Table != null)
|
||||
Table.Delete();
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,318 +0,0 @@
|
||||
/*
|
||||
* Implementation of the Microsoft Installer (msi.dll)
|
||||
*
|
||||
* Copyright 2004 Mike McCormack for CodeWeavers
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using static LibMSI.Internal.LibmsiSQLInput;
|
||||
using static LibMSI.Internal.MsiPriv;
|
||||
|
||||
namespace LibMSI.Views
|
||||
{
|
||||
internal class LibmsiInsertView : LibmsiView
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public LibmsiView Table { get; set; }
|
||||
|
||||
public LibmsiDatabase Database { get; set; }
|
||||
|
||||
public bool IsTemp { get; set; }
|
||||
|
||||
public LibmsiView View { get; set; }
|
||||
|
||||
public column_info Vals { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private LibmsiInsertView() { }
|
||||
|
||||
public static LibmsiResult Create(LibmsiDatabase db, out LibmsiView view, string table, column_info columns, column_info values, bool temp)
|
||||
{
|
||||
view = null;
|
||||
|
||||
// There should be one value for each column
|
||||
if (CountColumnInfo(columns) != CountColumnInfo(values))
|
||||
return LibmsiResult.LIBMSI_RESULT_BAD_QUERY_SYNTAX;
|
||||
|
||||
LibmsiResult r = LibmsiTableView.Create(db, table, out LibmsiView tv);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
r = LibmsiSelectView.Create(db, out LibmsiView sv, tv, columns);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
if (tv != null)
|
||||
tv.Delete();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
LibmsiInsertView iv = new LibmsiInsertView
|
||||
{
|
||||
Table = tv,
|
||||
Database = db,
|
||||
Vals = values,
|
||||
IsTemp = temp,
|
||||
View = sv,
|
||||
};
|
||||
|
||||
view = iv;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult FetchInt(int row, int col, out int val)
|
||||
{
|
||||
val = 0;
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Execute(LibmsiRecord record)
|
||||
{
|
||||
if (View == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
LibmsiResult r = View.Execute(null);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
r = View.GetDimensions(out _, out int col_count);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
// Merge the wildcard values into the list of values provided
|
||||
// in the query, and create a record containing both.
|
||||
LibmsiRecord values = MsiQueryMergeRecord(col_count, Vals, record);
|
||||
if (values == null)
|
||||
return r;
|
||||
|
||||
r = ArrangeRecord(ref values);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
// Rows with NULL primary keys are inserted at the beginning of the table
|
||||
int row = -1;
|
||||
if (RowHasNullPrimaryKeys(values))
|
||||
row = 0;
|
||||
|
||||
return Table.InsertRow(values, row, IsTemp);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Close()
|
||||
{
|
||||
if (View == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
return View.Close();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetDimensions(out int rows, out int cols)
|
||||
{
|
||||
rows = 0; cols = 0;
|
||||
if (View == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
return View.GetDimensions(out rows, out cols);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetColumnInfo(int n, out string name, out int type, out bool temporary, out string table_name)
|
||||
{
|
||||
name = null; type = 0; temporary = false; table_name = null;
|
||||
if (View == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
return View.GetColumnInfo(n, out name, out type, out temporary, out table_name);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Delete()
|
||||
{
|
||||
if (View != null)
|
||||
View.Delete();
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult FindMatchingRows(int col, int val, out int row, ref LibmsiColumnHashEntry handle)
|
||||
{
|
||||
row = 0;
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Functions
|
||||
|
||||
/// <summary>
|
||||
/// Merge a value_list and a record to create a second record.
|
||||
/// Replace wildcard entries in the valuelist with values from the record
|
||||
/// </summary>
|
||||
internal static LibmsiRecord MsiQueryMergeRecord(int fields, column_info vl, LibmsiRecord rec)
|
||||
{
|
||||
int wildcard_count = 1;
|
||||
|
||||
LibmsiRecord merged = LibmsiRecord.Create(fields);
|
||||
for (int i = 1; i <= fields; i++)
|
||||
{
|
||||
if (vl == null)
|
||||
return null;
|
||||
|
||||
switch (vl.Val.Type)
|
||||
{
|
||||
case EXPR_SVAL:
|
||||
merged.SetString(i, vl.Val.SVal);
|
||||
break;
|
||||
case EXPR_IVAL:
|
||||
merged.SetInt(i, vl.Val.IVal);
|
||||
break;
|
||||
case EXPR_WILDCARD:
|
||||
if (rec == null)
|
||||
return null;
|
||||
|
||||
rec.CopyField(wildcard_count, merged, i);
|
||||
wildcard_count++;
|
||||
break;
|
||||
default:
|
||||
Console.Error.WriteLine($"Unknown expression type {vl.Val.Type}");
|
||||
break;
|
||||
}
|
||||
|
||||
vl = vl.Next;
|
||||
}
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if the column order specified in the INSERT query
|
||||
/// matches the column order of the table
|
||||
/// </summary>
|
||||
private bool ColumnsInOrder(int col_count)
|
||||
{
|
||||
for (int i = 1; i <= col_count; i++)
|
||||
{
|
||||
View.GetColumnInfo(i, out string a, out _, out _, out _);
|
||||
Table.GetColumnInfo(i, out string b, out _, out _, out _);
|
||||
if (a != b)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rearranges the data in the record to be inserted based on column order,
|
||||
/// and pads the record for any missing columns in the INSERT query
|
||||
/// </summary>
|
||||
private LibmsiResult ArrangeRecord(ref LibmsiRecord values)
|
||||
{
|
||||
LibmsiResult r = Table.GetDimensions(out _, out int col_count);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
int val_count = values.GetFieldCount();
|
||||
|
||||
// Check to see if the columns are arranged already
|
||||
// to avoid unnecessary copying
|
||||
if (col_count == val_count && ColumnsInOrder(col_count))
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
|
||||
LibmsiRecord padded = LibmsiRecord.Create(col_count);
|
||||
if (padded == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_OUTOFMEMORY;
|
||||
|
||||
for (int colidx = 1; colidx <= val_count; colidx++)
|
||||
{
|
||||
r = View.GetColumnInfo(colidx, out string a, out _, out _, out _);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
for (int i = 1; i <= col_count; i++)
|
||||
{
|
||||
r = Table.GetColumnInfo(i, out string b, out _, out _, out _);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
if (a == b)
|
||||
{
|
||||
values.CopyField(colidx, padded, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
values = padded;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
private bool RowHasNullPrimaryKeys(LibmsiRecord row)
|
||||
{
|
||||
LibmsiResult r = Table.GetDimensions(out _, out int col_count );
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return false;
|
||||
|
||||
for (int i = 1; i <= col_count; i++)
|
||||
{
|
||||
r = Table.GetColumnInfo(i, out _, out int type, out _, out _);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return false;
|
||||
|
||||
if ((type & MSITYPE_KEY) == 0)
|
||||
continue;
|
||||
|
||||
if (row.IsNull(i))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int CountColumnInfo(column_info ci)
|
||||
{
|
||||
int n = 0;
|
||||
for (; ci != null; ci = ci.Next)
|
||||
{
|
||||
n++;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,323 +0,0 @@
|
||||
/*
|
||||
* Implementation of the Microsoft Installer (msi.dll)
|
||||
*
|
||||
* Copyright 2002-2004 Mike McCormack for CodeWeavers
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
using LibGSF.Input;
|
||||
using static LibMSI.LibmsiQuery;
|
||||
using static LibMSI.Internal.MsiPriv;
|
||||
|
||||
namespace LibMSI.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Below is the query interface to a table
|
||||
/// </summary>
|
||||
internal class LibmsiSelectView : LibmsiView
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public LibmsiDatabase Database { get; set; }
|
||||
|
||||
public LibmsiView Table { get; set; }
|
||||
|
||||
public int NumCols { get; set; }
|
||||
|
||||
public int MaxCols { get; set; }
|
||||
|
||||
public int[] Cols { get; set; } = new int[1];
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private LibmsiSelectView() { }
|
||||
|
||||
public static LibmsiResult Create(LibmsiDatabase db, out LibmsiView view, LibmsiView table, column_info columns)
|
||||
{
|
||||
int count = SelectCountColumns(columns);
|
||||
LibmsiSelectView sv = new LibmsiSelectView
|
||||
{
|
||||
Database = db,
|
||||
Table = table,
|
||||
NumCols = 0,
|
||||
MaxCols = count,
|
||||
};
|
||||
|
||||
LibmsiResult r = LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
while (columns != null)
|
||||
{
|
||||
r = sv.AddColumn(columns.Column, columns.Table);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
break;
|
||||
|
||||
columns = columns.Next;
|
||||
}
|
||||
|
||||
if (r == LibmsiResult.LIBMSI_RESULT_SUCCESS )
|
||||
view = sv;
|
||||
else
|
||||
view = null;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult FetchInt(int row, int col, out int val)
|
||||
{
|
||||
val = 0;
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
if (col == 0 || col > NumCols)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
col = Cols[col - 1];
|
||||
if (col == 0)
|
||||
{
|
||||
val = 0;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
return Table.FetchInt(row, col, out val);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult FetchStream(int row, int col, out GsfInput stm)
|
||||
{
|
||||
stm = null;
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
if (col == 0 || col > NumCols)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
col = Cols[col - 1];
|
||||
if (col == 0)
|
||||
{
|
||||
stm = null;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
return Table.FetchStream(row, col, out stm);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetRow(int row, out LibmsiRecord rec)
|
||||
{
|
||||
rec = null;
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
return MsiViewGetRow(Database, this, row, out rec);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult SetRow(int row, LibmsiRecord rec, int mask)
|
||||
{
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
// Test if any of the mask bits are invalid
|
||||
if (mask >= (1 << NumCols))
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
// Find the number of columns in the table below
|
||||
LibmsiResult r = Table.GetDimensions(out _, out int col_count);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
// Expand the record to the right size for the underlying table
|
||||
LibmsiRecord expanded = LibmsiRecord.Create(col_count);
|
||||
if (expanded == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
// Move the right fields across
|
||||
int expanded_mask = 0;
|
||||
for (int i = 0; i < NumCols; i++ )
|
||||
{
|
||||
r = rec.CopyField(i + 1, expanded, Cols[i]);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
break;
|
||||
|
||||
expanded_mask |= (1 << (Cols[i] - 1));
|
||||
}
|
||||
|
||||
// Set the row in the underlying table
|
||||
if (r == LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
r = Table.SetRow(row, expanded, expanded_mask);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult InsertRow(LibmsiRecord record, int row, bool temporary)
|
||||
{
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
// Rearrange the record to suit the table
|
||||
LibmsiResult r = Table.GetDimensions(out _, out int table_cols);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
LibmsiRecord outrec = LibmsiRecord.Create(table_cols + 1);
|
||||
|
||||
for (int i = 0; i < NumCols; i++)
|
||||
{
|
||||
r = record.CopyField(i + 1, outrec, Cols[i] );
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
}
|
||||
|
||||
return Table.InsertRow(outrec, row, temporary );
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Execute(LibmsiRecord record)
|
||||
{
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
return Table.Execute(record);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Close()
|
||||
{
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
return Table.Close();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetDimensions(out int rows, out int cols)
|
||||
{
|
||||
rows = 0; cols = 0;
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
cols = NumCols;
|
||||
return Table.GetDimensions(out rows, out _);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetColumnInfo(int n, out string name, out int type, out bool temporary, out string table_name)
|
||||
{
|
||||
name = null; type = 0; temporary = false; table_name = null;
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
if (n == 0 || n > NumCols)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
n = Cols[ n - 1 ];
|
||||
if (n != 0)
|
||||
{
|
||||
name = string.Empty;
|
||||
type = MSITYPE_UNKNOWN | MSITYPE_VALID;
|
||||
temporary = false;
|
||||
table_name = szEmpty;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
return Table.GetColumnInfo(n, out name, out type, out temporary, out table_name);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Delete()
|
||||
{
|
||||
if (Table != null)
|
||||
Table.Delete();
|
||||
|
||||
Table = null;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult FindMatchingRows(int col, int val, out int row, ref LibmsiColumnHashEntry handle)
|
||||
{
|
||||
row = 0;
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
if ((col == 0) || (col > NumCols))
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
col = Cols[ col - 1 ];
|
||||
|
||||
return Table.FindMatchingRows(col, val, out row, ref handle);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private LibmsiResult AddColumn(string name, string table_name)
|
||||
{
|
||||
LibmsiView table = Table;
|
||||
if (table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
try { table.GetDimensions(out _, out _); }
|
||||
catch { return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; }
|
||||
|
||||
try { table.GetColumnInfo(0, out _, out _, out _, out _); }
|
||||
catch { return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED; }
|
||||
|
||||
if (NumCols >= MaxCols)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
int n;
|
||||
if (name[0] == '\0')
|
||||
{
|
||||
n = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LibmsiResult r = ViewFindColumn(table, name, table_name, out n);
|
||||
if( r != LibmsiResult.LIBMSI_RESULT_SUCCESS )
|
||||
return r;
|
||||
}
|
||||
|
||||
Cols[NumCols++] = n;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
private static int SelectCountColumns(column_info col)
|
||||
{
|
||||
int n;
|
||||
for (n = 0; col != null; col = col.Next)
|
||||
{
|
||||
n++;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,320 +0,0 @@
|
||||
/*
|
||||
* Implementation of the Microsoft Installer (msi.dll)
|
||||
*
|
||||
* Copyright 2008 James Hawkins
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using LibGSF.Input;
|
||||
using LibMSI.Internal;
|
||||
using static LibMSI.Internal.MsiPriv;
|
||||
|
||||
namespace LibMSI.Views
|
||||
{
|
||||
internal class STORAGE
|
||||
{
|
||||
public int StrIndex { get; set; }
|
||||
}
|
||||
|
||||
internal class LibmsiStorageView : LibmsiView
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private const int NUM_STORAGES_COLS = 2;
|
||||
private const int MAX_STORAGES_NAME_LEN = 62;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public LibmsiDatabase Database { get; set; }
|
||||
|
||||
public STORAGE[] Storages { get; set; }
|
||||
|
||||
public int MaxStorages { get; set; }
|
||||
|
||||
public int NumRows { get; set; }
|
||||
|
||||
public int RowSize { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private LibmsiStorageView() { }
|
||||
|
||||
public static LibmsiResult Create(LibmsiDatabase db, out LibmsiView view)
|
||||
{
|
||||
view = null;
|
||||
LibmsiStorageView sv = new LibmsiStorageView
|
||||
{
|
||||
Database = db,
|
||||
};
|
||||
|
||||
LibmsiResult r = sv.AddStoragesToTable();
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
view = sv;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
private bool StoragesSetTableSize(int size)
|
||||
{
|
||||
if (size >= MaxStorages)
|
||||
{
|
||||
MaxStorages *= 2;
|
||||
STORAGE[] temp = new STORAGE[MaxStorages];
|
||||
Array.Copy(Storages, temp, Storages.Length);
|
||||
Storages = temp;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private STORAGE CreateStorage(string name)
|
||||
{
|
||||
return new STORAGE
|
||||
{
|
||||
StrIndex = Database.Strings.AddString(name, -1, 1, StringPersistence.StringNonPersistent)
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult FetchInt(int row, int col, out int val)
|
||||
{
|
||||
val = 0;
|
||||
if (col != 1)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
if (row >= NumRows)
|
||||
return LibmsiResult.NO_MORE_ITEMS;
|
||||
|
||||
val = Storages[row].StrIndex;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult FetchStream(int row, int col, out GsfInput stm)
|
||||
{
|
||||
stm = null;
|
||||
if (row >= NumRows)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_DATA;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetRow(int row, out LibmsiRecord rec)
|
||||
{
|
||||
rec = null;
|
||||
return LibmsiResult.LIBMSI_RESULT_CALL_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult SetRow(int row, LibmsiRecord rec, int mask)
|
||||
{
|
||||
STORAGE storage;
|
||||
string name = null;
|
||||
|
||||
if (row > NumRows)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
LibmsiResult r = rec.GetGsfInput(2, out GsfInput stm);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
if (Storages[row] != null)
|
||||
{
|
||||
if ((mask & 1) != 0)
|
||||
{
|
||||
Console.Error.WriteLine("FIXME: renaming storage via UPDATE on _Storages table");
|
||||
return r;
|
||||
}
|
||||
|
||||
storage = Storages[row];
|
||||
name = Database.Strings.LookupId(storage.StrIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
name = rec.GetStringRaw(1);
|
||||
}
|
||||
|
||||
if (name == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_OUTOFMEMORY;
|
||||
|
||||
Database.CreateStorage(name, stm);
|
||||
storage = CreateStorage(name);
|
||||
if (storage == null)
|
||||
r = LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
Storages[row] = storage;
|
||||
return r;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult InsertRow(LibmsiRecord record, int row, bool temporary)
|
||||
{
|
||||
if (!StoragesSetTableSize(++NumRows))
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
if (row == -1)
|
||||
row = NumRows - 1;
|
||||
|
||||
// FIXME have to readjust rows
|
||||
|
||||
return SetRow(row, record, 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult DeleteRow(int row)
|
||||
{
|
||||
if (row > NumRows)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
string name = Database.Strings.LookupId(Storages[row].StrIndex);
|
||||
if (name == null)
|
||||
{
|
||||
Console.Error.WriteLine("Failed to retrieve storage name");
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
Database.DestroyStorage(name);
|
||||
|
||||
// Shift the remaining rows
|
||||
for (int i = row + 1; i < NumRows; i++)
|
||||
{
|
||||
Storages[i - 1] = Storages[i];
|
||||
}
|
||||
|
||||
NumRows--;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Execute(LibmsiRecord record) => LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Close() => LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetDimensions(out int rows, out int cols)
|
||||
{
|
||||
cols = NUM_STORAGES_COLS;
|
||||
rows = NumRows;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetColumnInfo(int n, out string name, out int type, out bool temporary, out string table_name)
|
||||
{
|
||||
name = null; type = 0; temporary = false; table_name = null;
|
||||
if (n == 0 || n > NUM_STORAGES_COLS)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
switch (n)
|
||||
{
|
||||
case 1:
|
||||
name = szName;
|
||||
type = MSITYPE_STRING | MSITYPE_VALID | MAX_STORAGES_NAME_LEN;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
name = szData;
|
||||
type = MSITYPE_STRING | MSITYPE_VALID | MSITYPE_NULLABLE;
|
||||
break;
|
||||
|
||||
default:
|
||||
Console.Error.WriteLine($"Invalid case {n}");
|
||||
break;
|
||||
}
|
||||
|
||||
table_name = szStorages;
|
||||
temporary = false;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Delete()
|
||||
{
|
||||
Storages = null;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult FindMatchingRows(int col, int val, out int row, ref LibmsiColumnHashEntry handle)
|
||||
{
|
||||
row = 0;
|
||||
if (col == 0 || col > NUM_STORAGES_COLS)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
while (handle.Row < NumRows)
|
||||
{
|
||||
if (Storages[handle.Row].StrIndex == val)
|
||||
{
|
||||
row = handle.Row;
|
||||
break;
|
||||
}
|
||||
|
||||
handle = handle.Next;
|
||||
}
|
||||
|
||||
handle = handle.Next;
|
||||
if (handle.Row >= NumRows)
|
||||
return LibmsiResult.NO_MORE_ITEMS;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private static LibmsiResult AddStorageToTable(string name, GsfInfile stg, object opaque)
|
||||
{
|
||||
LibmsiStorageView sv = (LibmsiStorageView)opaque;
|
||||
STORAGE storage = sv.CreateStorage(name);
|
||||
if (storage == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_NOT_ENOUGH_MEMORY;
|
||||
|
||||
if (!sv.StoragesSetTableSize(++sv.NumRows))
|
||||
return LibmsiResult.LIBMSI_RESULT_NOT_ENOUGH_MEMORY;
|
||||
|
||||
sv.Storages[sv.NumRows - 1] = storage;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
private LibmsiResult AddStoragesToTable()
|
||||
{
|
||||
MaxStorages = 1;
|
||||
Storages = new STORAGE[1];
|
||||
return Database.EnumDbStorages(AddStorageToTable, this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,336 +0,0 @@
|
||||
/*
|
||||
* Implementation of the Microsoft Installer (msi.dll)
|
||||
*
|
||||
* Copyright 2007 James Hawkins
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using LibGSF.Input;
|
||||
using LibMSI.Internal;
|
||||
using static LibMSI.LibmsiQuery;
|
||||
using static LibMSI.Internal.LibmsiTable;
|
||||
using static LibMSI.Internal.MsiPriv;
|
||||
|
||||
namespace LibMSI.Views
|
||||
{
|
||||
internal class STREAM
|
||||
{
|
||||
public int StrIndex { get; set; }
|
||||
|
||||
public GsfInput Stream { get; set; }
|
||||
}
|
||||
|
||||
internal class LibmsiStreamsView : LibmsiView
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private const int NUM_STREAMS_COLS = 2;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public LibmsiDatabase Database { get; set; }
|
||||
|
||||
public STREAM[] Streams { get; set; }
|
||||
|
||||
public int MaxStreams { get; set; }
|
||||
|
||||
public int NumRows { get; set; }
|
||||
|
||||
public int RowSize { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private LibmsiStreamsView() { }
|
||||
|
||||
public static LibmsiResult Create(LibmsiDatabase db, out LibmsiView view)
|
||||
{
|
||||
view = null;
|
||||
LibmsiStreamsView sv = new LibmsiStreamsView
|
||||
{
|
||||
Database = db,
|
||||
};
|
||||
|
||||
LibmsiResult r = sv.AddStreamsToTable();
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
view = sv;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult FetchInt(int row, int col, out int val)
|
||||
{
|
||||
val = 0;
|
||||
if (col != 1)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
if (row >= NumRows)
|
||||
return LibmsiResult.NO_MORE_ITEMS;
|
||||
|
||||
val = Streams[row].StrIndex;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult FetchStream(int row, int col, out GsfInput stm)
|
||||
{
|
||||
stm = null;
|
||||
if (row >= NumRows)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
stm = Streams[row].Stream;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetRow(int row, out LibmsiRecord rec) => MsiViewGetRow(Database, this, row, out rec);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult SetRow(int row, LibmsiRecord rec, int mask)
|
||||
{
|
||||
if (row > NumRows)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
LibmsiResult r = rec.GetGsfInput(2, out GsfInput stm);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
STREAM stream = null;
|
||||
string name = null;
|
||||
if (Streams[row] != null)
|
||||
{
|
||||
if ((mask & 1) != 0)
|
||||
{
|
||||
Console.Error.WriteLine("FIXME: renaming stream via UPDATE on _Streams table");
|
||||
return r;
|
||||
}
|
||||
|
||||
stream = Streams[row];
|
||||
name = Database.Strings.LookupId(stream.StrIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
name = rec.GetStringRaw(1);
|
||||
if (name == null)
|
||||
{
|
||||
Console.Error.WriteLine("Failed to retrieve stream name");
|
||||
return r;
|
||||
}
|
||||
|
||||
stream = CreateStream(name, false, stm);
|
||||
}
|
||||
|
||||
if (stream == null)
|
||||
return r;
|
||||
|
||||
r = Database.CreateStream(name, stm);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to create stream: {r}");
|
||||
return r;
|
||||
}
|
||||
|
||||
Streams[row] = stream;
|
||||
return r;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult InsertRow(LibmsiRecord record, int row, bool temporary)
|
||||
{
|
||||
if (!StreamsSetTableSize(++NumRows))
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
if (row == -1)
|
||||
row = NumRows - 1;
|
||||
|
||||
// Shift the rows to make room for the new row
|
||||
for (int i = NumRows - 1; i > row; i--)
|
||||
{
|
||||
Streams[i] = Streams[i - 1];
|
||||
}
|
||||
|
||||
return SetRow(row, record, 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult DeleteRow(int row)
|
||||
{
|
||||
string name;
|
||||
string encname;
|
||||
|
||||
if (row > NumRows)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
name = Database.Strings.LookupId(Streams[row].StrIndex);
|
||||
if (name == null)
|
||||
{
|
||||
Console.Error.WriteLine("Failed to retrieve stream name");
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
encname = EncodeStreamName(false, name).TrimEnd('\0');
|
||||
Database.DestroyStream(encname);
|
||||
|
||||
// Shift the remaining rows
|
||||
for (int i = row + 1; i < NumRows; i++)
|
||||
{
|
||||
Streams[i - 1] = Streams[i];
|
||||
}
|
||||
|
||||
NumRows--;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Execute(LibmsiRecord record) => LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Close() => LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetDimensions(out int rows, out int cols)
|
||||
{
|
||||
cols = NUM_STREAMS_COLS;
|
||||
rows = NumRows;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetColumnInfo(int n, out string name, out int type, out bool temporary, out string table_name)
|
||||
{
|
||||
name = null; type = 0; temporary = false; table_name = null;
|
||||
if (n == 0 || n > NUM_STREAMS_COLS)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
switch (n)
|
||||
{
|
||||
case 1:
|
||||
name = szName;
|
||||
type = MSITYPE_STRING | MSITYPE_VALID | MAX_STREAM_NAME_LEN;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
name = szData;
|
||||
type = MSITYPE_STRING | MSITYPE_VALID | MSITYPE_NULLABLE;
|
||||
break;
|
||||
|
||||
default:
|
||||
Console.Error.WriteLine($"Invalid value: {n}");
|
||||
break;
|
||||
}
|
||||
|
||||
table_name = szStreams;
|
||||
temporary = false;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Delete() => LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
|
||||
public override LibmsiResult FindMatchingRows(int col, int val, out int row, ref LibmsiColumnHashEntry handle)
|
||||
{
|
||||
row = 0;
|
||||
if (col == 0 || col > NUM_STREAMS_COLS)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
while (handle.Row < NumRows)
|
||||
{
|
||||
if (Streams[handle.Row].StrIndex == val)
|
||||
{
|
||||
row = handle.Row;
|
||||
break;
|
||||
}
|
||||
|
||||
handle = handle.Next;
|
||||
}
|
||||
|
||||
handle = handle.Next;
|
||||
if (handle.Row >= NumRows)
|
||||
return LibmsiResult.NO_MORE_ITEMS;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
private bool StreamsSetTableSize(int size)
|
||||
{
|
||||
if (size >= MaxStreams)
|
||||
{
|
||||
STREAM[] temp = new STREAM[MaxStreams * 2];
|
||||
Array.Copy(Streams, temp, Streams.Length);
|
||||
Streams = temp;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private STREAM CreateStream(string name, bool encoded, GsfInput stm)
|
||||
{
|
||||
STREAM stream = new STREAM();
|
||||
string decoded = null;
|
||||
if (encoded)
|
||||
{
|
||||
decoded = DecodeStreamName(name);
|
||||
name = decoded;
|
||||
}
|
||||
|
||||
stream.StrIndex = Database.Strings.AddString(name, -1, 1, StringPersistence.StringNonPersistent);
|
||||
stream.Stream = stm;
|
||||
return stream;
|
||||
}
|
||||
|
||||
private static LibmsiResult AddStreamToTable(string name, GsfInput stm, object opaque)
|
||||
{
|
||||
LibmsiStreamsView sv = (LibmsiStreamsView)opaque;
|
||||
STREAM stream = sv.CreateStream(name, true, stm);
|
||||
if (stream == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_NOT_ENOUGH_MEMORY;
|
||||
|
||||
if (!sv.StreamsSetTableSize(++sv.NumRows))
|
||||
return LibmsiResult.LIBMSI_RESULT_NOT_ENOUGH_MEMORY;
|
||||
|
||||
sv.Streams[sv.NumRows - 1] = stream;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
private LibmsiResult AddStreamsToTable()
|
||||
{
|
||||
MaxStreams = 1;
|
||||
Streams = new STREAM[1];
|
||||
return Database.EnumDbStreams(AddStreamToTable, this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,850 +0,0 @@
|
||||
/*
|
||||
* Implementation of the Microsoft Installer (msi.dll)
|
||||
*
|
||||
* Copyright 2002-2005 Mike McCormack for CodeWeavers
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using LibGSF.Input;
|
||||
using LibMSI.Internal;
|
||||
using static LibMSI.LibmsiQuery;
|
||||
using static LibMSI.LibmsiTypes;
|
||||
using static LibMSI.Internal.LibmsiTable;
|
||||
using static LibMSI.Internal.MsiPriv;
|
||||
|
||||
namespace LibMSI.Views
|
||||
{
|
||||
internal class LibmsiColumnHashEntry
|
||||
{
|
||||
public LibmsiColumnHashEntry Next { get; set; }
|
||||
|
||||
public int Value { get; set; }
|
||||
|
||||
public int Row { get; set; }
|
||||
}
|
||||
|
||||
internal class LibmsiTableView : LibmsiView
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public LibmsiDatabase Database { get; set; }
|
||||
|
||||
public LibmsiTable Table { get; set; }
|
||||
|
||||
public LibmsiColumnInfo[] Columns { get; set; }
|
||||
|
||||
public int NumCols { get; set; }
|
||||
|
||||
public int RowSize { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private LibmsiTableView() { }
|
||||
|
||||
public static LibmsiResult Create(LibmsiDatabase db, string name, out LibmsiView view)
|
||||
{
|
||||
if (name == szStreams)
|
||||
return LibmsiStreamsView.Create(db, out view);
|
||||
else if (name == szStorages)
|
||||
return LibmsiStorageView.Create(db, out view);
|
||||
|
||||
LibmsiTableView tv = new LibmsiTableView();
|
||||
LibmsiResult r = GetTable(db, name, out LibmsiTable table);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
view = null;
|
||||
Console.Error.WriteLine("Table not found");
|
||||
return r;
|
||||
}
|
||||
|
||||
// Fill the structure
|
||||
tv.Table = table;
|
||||
tv.Database = db;
|
||||
tv.Columns = tv.Table.ColInfo;
|
||||
tv.NumCols = tv.Table.ColCount;
|
||||
tv.RowSize = GetRowSize(tv.Table.ColInfo, tv.Table.ColCount, LONG_STR_BYTES);
|
||||
tv.Name = name;
|
||||
|
||||
view = tv;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult FetchInt(int row, int col, out int val)
|
||||
{
|
||||
val = 0;
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
if ((col == 0) || (col > NumCols))
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
// How many rows are there?
|
||||
if (row >= Table.RowCount)
|
||||
return LibmsiResult.NO_MORE_ITEMS;
|
||||
|
||||
if (Columns[col - 1].Offset >= RowSize)
|
||||
{
|
||||
Console.Error.WriteLine($"Stuffed up {Columns[col - 1].Offset} >= {RowSize}");
|
||||
Console.Error.WriteLine($"{this} {Columns}");
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
int n = BytesPerColumn(Columns[col - 1], LONG_STR_BYTES);
|
||||
if (n != 2 && n != 3 && n != 4)
|
||||
{
|
||||
Console.Error.WriteLine("oops! what is %d bytes per column?\n", n );
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
int offset = Columns[col-1].Offset;
|
||||
val = ReadTableInt(Table.Data, row, offset, n);
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult FetchStream(int row, int col, out GsfInput stm)
|
||||
{
|
||||
stm = null;
|
||||
LibmsiResult r = MsiStreamName(row, out string full_name);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
Console.Error.WriteLine($"Fetching stream, error = {r}");
|
||||
return r;
|
||||
}
|
||||
|
||||
string encname = EncodeStreamName(false, full_name).TrimEnd('\0');
|
||||
r = Database.GetRawStream(encname, out stm);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
Console.Error.WriteLine($"Fetching stream {full_name}, error = {r}");
|
||||
|
||||
if (stm != null)
|
||||
stm.Name = full_name;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetRow(int row, out LibmsiRecord rec)
|
||||
{
|
||||
rec = null;
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
return MsiViewGetRow(Database, this, row, out rec);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult SetRow(int row, LibmsiRecord rec, int mask)
|
||||
{
|
||||
LibmsiResult r = LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
// Test if any of the mask bits are invalid
|
||||
if (mask >= (1 << NumCols))
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
for (int i = 0; i < NumCols; i++ )
|
||||
{
|
||||
// Only update the fields specified in the mask
|
||||
if ((mask & (1 << i)) == 0)
|
||||
continue;
|
||||
|
||||
bool persistent = (Table.Persistent != LibmsiCondition.LIBMSI_CONDITION_FALSE) && (Table.DataPersistent[row]);
|
||||
|
||||
// FIXME: should we allow updating keys?
|
||||
|
||||
int val = 0;
|
||||
if (rec.IsNull(i + 1))
|
||||
{
|
||||
r = GetTableValueFromRecord(rec, i + 1, out val);
|
||||
if (MSITYPE_IS_BINARY(Columns[i].Type))
|
||||
{
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
r = rec.GetGsfInput(i + 1, out GsfInput stm);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
r = MsiStreamName(row, out string stname);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
r = AddStream(Database, stname, stm);
|
||||
if ( r != LibmsiResult.LIBMSI_RESULT_SUCCESS )
|
||||
return r;
|
||||
}
|
||||
else if ((Columns[i].Type & MSITYPE_STRING) != 0)
|
||||
{
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
string sval = rec.GetStringRaw(i + 1);
|
||||
val = Database.Strings.AddString(sval, -1, 1, persistent ? StringPersistence.StringPersistent : StringPersistence.StringNonPersistent );
|
||||
}
|
||||
else
|
||||
{
|
||||
FetchInt(row, i + 1, out int x);
|
||||
if (val == x)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
r = SetInt(row, i + 1, val);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
break;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Execute(LibmsiRecord record) => LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Close() => LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetDimensions(out int rows, out int cols)
|
||||
{
|
||||
rows = 0;
|
||||
cols = NumCols;
|
||||
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
rows = Table.RowCount;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetColumnInfo(int n, out string name, out int type, out bool temporary, out string table_name)
|
||||
{
|
||||
name = null; type = 0; temporary = false; table_name = null;
|
||||
if (n == 0 || n > NumCols)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
name = Columns[n - 1].ColName;
|
||||
if (name == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
table_name = Columns[n - 1].TableName;
|
||||
if (table_name == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
type = Columns[n - 1].Type;
|
||||
temporary = Columns[n - 1].Temporary;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult InsertRow(LibmsiRecord rec, int row, bool temporary)
|
||||
{
|
||||
// Check that the key is unique - can we find a matching row?
|
||||
LibmsiResult r = TableValidateNew(rec, out _);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
if (row == -1)
|
||||
row = FindInsertIndex(rec);
|
||||
|
||||
r = TableCreateNewRow(ref row, temporary);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
// Shift the rows to make room for the new row
|
||||
for (int i = Table.RowCount - 1; i > row; i--)
|
||||
{
|
||||
byte[] temp = new byte[RowSize];
|
||||
Array.Copy(Table.Data[i - 1], Table.Data[i], RowSize);
|
||||
Table.Data[i - 1] = Enumerable.Repeat<byte>(0x00, RowSize).ToArray();
|
||||
Table.DataPersistent[i] = Table.DataPersistent[i - 1];
|
||||
}
|
||||
|
||||
// Re-set the persistence flag
|
||||
Table.DataPersistent[row] = !temporary;
|
||||
return SetRow(row, rec, (1 << NumCols) - 1);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult DeleteRow(int row)
|
||||
{
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
LibmsiResult r = GetDimensions(out int num_rows, out int num_cols);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
if (row >= num_rows)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
num_rows = Table.RowCount;
|
||||
Table.RowCount--;
|
||||
|
||||
// Reset the hash tables
|
||||
for (int i = 0; i < NumCols; i++)
|
||||
{
|
||||
Columns[i].HashTable = null;
|
||||
}
|
||||
|
||||
for (int i = row + 1; i < num_rows; i++)
|
||||
{
|
||||
Array.Copy(Table.Data[i], Table.Data[i - 1], RowSize);
|
||||
Table.DataPersistent[i - 1] = Table.DataPersistent[i];
|
||||
}
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Delete()
|
||||
{
|
||||
Table = null;
|
||||
Columns = null;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult FindMatchingRows(int col, int val, out int row, ref LibmsiColumnHashEntry handle)
|
||||
{
|
||||
row = 0;
|
||||
LibmsiColumnHashEntry entry;
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
if (col == 0 || col > NumCols)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
if (Columns[col - 1].HashTable == null)
|
||||
{
|
||||
int num_rows = Table.RowCount;
|
||||
if (Columns[col - 1].Offset >= RowSize)
|
||||
{
|
||||
Console.Error.WriteLine($"Stuffed up {Columns[col - 1].Offset} >= {RowSize}");
|
||||
Console.Error.WriteLine($"{this} {Columns}");
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
// Allocate contiguous memory for the table and its entries so we
|
||||
// don't have to do an expensive cleanup
|
||||
LibmsiColumnHashEntry[] hash_table = new LibmsiColumnHashEntry[num_rows];
|
||||
Columns[col - 1].HashTable = hash_table;
|
||||
|
||||
int new_entry = 0;
|
||||
for (int i = 0; i < num_rows; i++, new_entry++)
|
||||
{
|
||||
if (FetchInt(i, col, out int row_value) != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
continue;
|
||||
|
||||
hash_table[new_entry] = new LibmsiColumnHashEntry
|
||||
{
|
||||
Next = null,
|
||||
Value = row_value,
|
||||
Row = i,
|
||||
};
|
||||
|
||||
if (hash_table[row_value] != null)
|
||||
{
|
||||
LibmsiColumnHashEntry prev_entry = hash_table[row_value];
|
||||
while (prev_entry.Next != null)
|
||||
{
|
||||
prev_entry = prev_entry.Next;
|
||||
}
|
||||
|
||||
prev_entry.Next = hash_table[new_entry];
|
||||
}
|
||||
else
|
||||
{
|
||||
hash_table[row_value] = hash_table[new_entry];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (handle == null)
|
||||
entry = Columns[col - 1].HashTable[val];
|
||||
else
|
||||
entry = handle.Next;
|
||||
|
||||
while (entry != null && entry.Value != val)
|
||||
{
|
||||
entry = entry.Next;
|
||||
}
|
||||
|
||||
handle = entry;
|
||||
if (entry == null)
|
||||
return LibmsiResult.NO_MORE_ITEMS;
|
||||
|
||||
row = entry.Row;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int AddRef()
|
||||
{
|
||||
for (int i = 0; i < Table.ColCount; i++)
|
||||
{
|
||||
if ((Table.ColInfo[i].Type & MSITYPE_TEMPORARY) != 0)
|
||||
Table.ColInfo[i].RefCount++;
|
||||
}
|
||||
|
||||
return ++Table.RefCount;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult RemoveColumn(string table, int number)
|
||||
{
|
||||
LibmsiRecord rec = LibmsiRecord.Create(2);
|
||||
if (rec == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_OUTOFMEMORY;
|
||||
|
||||
rec.SetString(1, table);
|
||||
rec.SetInt(2, number);
|
||||
|
||||
LibmsiResult r = Create(Database, szColumns, out LibmsiView columns);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
r = FindRow(columns as LibmsiTableView, rec, out int row, out _);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
columns.Delete();
|
||||
return r;
|
||||
}
|
||||
|
||||
r = (columns as LibmsiTableView).DeleteRow(row);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
columns.Delete();
|
||||
return r;
|
||||
}
|
||||
|
||||
UpdateTableColumns(Database, table);
|
||||
columns.Delete();
|
||||
return r;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int Release()
|
||||
{
|
||||
int ref_count = Table.RefCount;
|
||||
for (int i = 0; i < Table.ColCount; i++)
|
||||
{
|
||||
if ((Table.ColInfo[i].Type & MSITYPE_TEMPORARY) != 0)
|
||||
{
|
||||
ref_count = --Table.ColInfo[i].RefCount;
|
||||
if (ref_count == 0)
|
||||
{
|
||||
LibmsiResult r = RemoveColumn(Table.ColInfo[i].TableName, Table.ColInfo[i].Number);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ref_count = --Table.RefCount;
|
||||
if (ref_count == 0)
|
||||
{
|
||||
if (Table.RowCount == 0)
|
||||
Delete();
|
||||
}
|
||||
|
||||
return ref_count;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult AddColumn(string table, int number, string column, int type, bool hold)
|
||||
{
|
||||
LibmsiRecord rec = LibmsiRecord.Create(4);
|
||||
if (rec == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_OUTOFMEMORY;
|
||||
|
||||
rec.SetString(1, table);
|
||||
rec.SetInt(2, number);
|
||||
rec.SetString(3, column);
|
||||
rec.SetInt(4, type);
|
||||
|
||||
LibmsiResult r = InsertRow(rec, -1, false);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
UpdateTableColumns(Database, table);
|
||||
|
||||
if (!hold)
|
||||
return r;
|
||||
|
||||
LibmsiTable msitable = FindCachedTable(Database, table);
|
||||
for (int i = 0; i < msitable.ColCount; i++)
|
||||
{
|
||||
if (msitable.ColInfo[i].ColName == column)
|
||||
{
|
||||
msitable.ColInfo[i].RefCount++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Drop()
|
||||
{
|
||||
LibmsiResult r;
|
||||
for (int i = Table.ColCount - 1; i >= 0; i--)
|
||||
{
|
||||
r = RemoveColumn(Table.ColInfo[i].TableName, Table.ColInfo[i].Number);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
}
|
||||
|
||||
LibmsiRecord rec = LibmsiRecord.Create(1);
|
||||
if (rec == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_OUTOFMEMORY;
|
||||
|
||||
rec.SetString(1, Name);
|
||||
|
||||
r = Create(Database, szTables, out LibmsiView tables);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
r = FindRow((tables as LibmsiTableView), rec, out int row, out _);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
tables.Delete();
|
||||
return r;
|
||||
}
|
||||
|
||||
r = tables.DeleteRow(row);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
tables.Delete();
|
||||
return r;
|
||||
}
|
||||
|
||||
tables.Delete();
|
||||
return r;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
private LibmsiResult MsiStreamName(int row, out string pstname)
|
||||
{
|
||||
int len = Name.Length + 1;
|
||||
string stname = Name;
|
||||
|
||||
LibmsiResult r;
|
||||
for (int i = 0; i < NumCols; i++)
|
||||
{
|
||||
int type = Columns[i].Type;
|
||||
if ((type & MSITYPE_KEY) != 0)
|
||||
{
|
||||
r = FetchInt(row, i + 1, out int ival);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
pstname = null;
|
||||
return r;
|
||||
}
|
||||
|
||||
string sval = string.Empty;
|
||||
if ((Columns[i].Type & MSITYPE_STRING) != 0)
|
||||
{
|
||||
sval = Database.Strings.LookupId(ival );
|
||||
if (sval == null)
|
||||
{
|
||||
pstname = null;
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int n = BytesPerColumn(Columns[i], LONG_STR_BYTES);
|
||||
switch (n)
|
||||
{
|
||||
case 2:
|
||||
sval = (ival - 0x8000).ToString();
|
||||
break;
|
||||
case 4:
|
||||
sval = (ival ^ 0x80000000).ToString();
|
||||
break;
|
||||
default:
|
||||
Console.Error.WriteLine($"Oops - unknown column width {n}");
|
||||
pstname = null;
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
len += szDot.Length + sval.Length;
|
||||
stname += szDot + sval;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
pstname = stname;
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
private LibmsiResult SetInt(int row, int col, int val)
|
||||
{
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
if (col == 0 || col > NumCols)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
if (row >= Table.RowCount)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
if (Columns[col - 1].Offset >= RowSize)
|
||||
{
|
||||
Console.Error.WriteLine($"Stuffed up {Columns[col - 1].Offset} >= {RowSize}");
|
||||
Console.Error.WriteLine($"{this} {Columns}");
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
Columns[col - 1].HashTable = null;
|
||||
|
||||
int n = BytesPerColumn(Columns[col - 1], LONG_STR_BYTES);
|
||||
if (n != 2 && n != 3 && n != 4)
|
||||
{
|
||||
Console.Error.WriteLine($"Oops! what is {n} bytes per column?");
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
int offset = Columns[col - 1].Offset;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
Table.Data[row][offset + i] = (byte)((val >> i * 8) & 0xff);
|
||||
}
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
private LibmsiResult GetTableValueFromRecord(LibmsiRecord rec, int iField, out int pvalue)
|
||||
{
|
||||
LibmsiResult r;
|
||||
|
||||
pvalue = 0;
|
||||
if (iField <= 0 || iField > NumCols || rec.IsNull(iField))
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
LibmsiColumnInfo columninfo = Columns[iField - 1];
|
||||
if (MSITYPE_IS_BINARY(columninfo.Type))
|
||||
{
|
||||
pvalue = 1; // Refers to the first key column
|
||||
}
|
||||
else if ((columninfo.Type & MSITYPE_STRING) != 0)
|
||||
{
|
||||
string sval = rec.GetStringRaw(iField);
|
||||
if (sval != null)
|
||||
{
|
||||
r = Database.Strings.IdFromStringUTF8(sval, out pvalue);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return LibmsiResult.LIBMSI_RESULT_NOT_FOUND;
|
||||
}
|
||||
else
|
||||
{
|
||||
pvalue = 0;
|
||||
}
|
||||
}
|
||||
else if (BytesPerColumn(columninfo, LONG_STR_BYTES) == 2)
|
||||
{
|
||||
pvalue = 0x8000 + rec.GetInt(iField);
|
||||
if ((pvalue & 0xffff0000) != 0)
|
||||
{
|
||||
Console.Error.WriteLine($"Field {iField} value {pvalue - 0x8000} out of range");
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int ival = rec.GetInt(iField);
|
||||
pvalue = (int)(ival ^ 0x80000000);
|
||||
}
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
private LibmsiResult TableCreateNewRow(ref int num, bool temporary)
|
||||
{
|
||||
if (Table == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_PARAMETER;
|
||||
|
||||
byte[] row = new byte[RowSize];
|
||||
int data_ptr = 0; // Table.Data[0][]
|
||||
int data_persist_ptr = 0; // Table.DataPersistent[0];
|
||||
if (num == -1)
|
||||
num = Table.RowCount;
|
||||
|
||||
int sz = Table.RowCount + 1;
|
||||
if (Table.Data[data_ptr] != null)
|
||||
{
|
||||
byte[] p = Table.Data[data_ptr];
|
||||
Array.Resize(ref p, sz);
|
||||
Table.Data[data_ptr] = p;
|
||||
}
|
||||
else
|
||||
{
|
||||
Table.Data[data_ptr] = new byte[sz];
|
||||
}
|
||||
|
||||
sz = Table.RowCount + 1;
|
||||
if (Table.DataPersistent != null)
|
||||
{
|
||||
bool[] b = Table.DataPersistent;
|
||||
Array.Resize(ref b, sz);
|
||||
Table.DataPersistent = b;
|
||||
}
|
||||
else
|
||||
{
|
||||
Table.DataPersistent = new bool[sz];
|
||||
}
|
||||
|
||||
Table.Data[data_ptr + Table.RowCount] = row;
|
||||
Table.DataPersistent[data_persist_ptr + Table.RowCount] = !temporary;
|
||||
|
||||
Table.RowCount++;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
private LibmsiResult TableValidateNew(LibmsiRecord rec, out int column)
|
||||
{
|
||||
// Check there's no null values where they're not allowed
|
||||
for (int i = 0; i < NumCols; i++ )
|
||||
{
|
||||
if ((Columns[i].Type & MSITYPE_NULLABLE) != 0)
|
||||
continue;
|
||||
|
||||
if (MSITYPE_IS_BINARY(Columns[i].Type))
|
||||
{
|
||||
// Skip binary columns
|
||||
}
|
||||
else if ((Columns[i].Type & MSITYPE_STRING) != 0)
|
||||
{
|
||||
string str = rec.GetStringRaw(i + 1);
|
||||
if (str == null || str[0] == 0)
|
||||
{
|
||||
column = i;
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_DATA;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int n = rec.GetInt(i + 1);
|
||||
if (n == LIBMSI_NULL_INT)
|
||||
{
|
||||
column = i;
|
||||
return LibmsiResult.LIBMSI_RESULT_INVALID_DATA;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check there's no duplicate keys
|
||||
LibmsiResult r = FindRow(this, rec, out int row, out column);
|
||||
if (r == LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
private int CompareRecord(int row, LibmsiRecord rec)
|
||||
{
|
||||
for (int i = 0; i < NumCols; i++ )
|
||||
{
|
||||
if ((Columns[i].Type & MSITYPE_KEY) == 0)
|
||||
continue;
|
||||
|
||||
LibmsiResult r = GetTableValueFromRecord(rec, i + 1, out int ivalue);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return 1;
|
||||
|
||||
r = FetchInt(row, i + 1, out int x);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
Console.Error.WriteLine($"FetchInt should not fail here {r}");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ivalue > x)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (ivalue == x)
|
||||
{
|
||||
if (i < NumCols - 1)
|
||||
continue;
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private int FindInsertIndex(LibmsiRecord rec)
|
||||
{
|
||||
int low = 0, high = Table.RowCount - 1;
|
||||
while (low <= high)
|
||||
{
|
||||
int idx = (low + high) / 2;
|
||||
int c = CompareRecord(idx, rec);
|
||||
|
||||
if (c < 0)
|
||||
high = idx - 1;
|
||||
else if (c > 0)
|
||||
low = idx + 1;
|
||||
else
|
||||
return idx;
|
||||
}
|
||||
|
||||
return high + 1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,196 +0,0 @@
|
||||
/*
|
||||
* Implementation of the Microsoft Installer (msi.dll)
|
||||
*
|
||||
* Copyright 2004 Mike McCormack for CodeWeavers
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
using LibMSI.Internal;
|
||||
|
||||
namespace LibMSI.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Below is the query interface to a table
|
||||
/// </summary>
|
||||
internal class LibmsiUpdateView : LibmsiView
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public LibmsiDatabase Database { get; set; }
|
||||
|
||||
public LibmsiView View { get; set; }
|
||||
|
||||
public column_info Vals { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private LibmsiUpdateView() { }
|
||||
|
||||
public static LibmsiResult Create(LibmsiDatabase db, out LibmsiView view, string table, column_info columns, expr expr)
|
||||
{
|
||||
view = null;
|
||||
LibmsiResult r;
|
||||
LibmsiView sv = null, wv = null;
|
||||
if (expr != null)
|
||||
r = LibmsiWhereView.Create(db, out wv, table, expr);
|
||||
else
|
||||
r = LibmsiTableView.Create(db, table, out wv);
|
||||
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
// Then select the columns we want
|
||||
r = LibmsiSelectView.Create(db, out sv, wv, columns);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
{
|
||||
wv.Delete();
|
||||
return r;
|
||||
}
|
||||
|
||||
LibmsiUpdateView uv = new LibmsiUpdateView
|
||||
{
|
||||
Database = db,
|
||||
Vals = columns,
|
||||
View = sv,
|
||||
};
|
||||
|
||||
view = uv;
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult FetchInt(int row, int col, out int val)
|
||||
{
|
||||
val = 0;
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Execute(LibmsiRecord record)
|
||||
{
|
||||
int i;
|
||||
LibmsiRecord where = null;
|
||||
int cols_count, where_count;
|
||||
column_info col = Vals;
|
||||
|
||||
// Extract the where markers from the record
|
||||
if (record != null)
|
||||
{
|
||||
int s = record.GetFieldCount();
|
||||
for (i = 0; col != null; col = col.Next)
|
||||
{
|
||||
i++;
|
||||
}
|
||||
|
||||
cols_count = i;
|
||||
where_count = s - i;
|
||||
|
||||
if (where_count > 0)
|
||||
{
|
||||
where = LibmsiRecord.Create(where_count);
|
||||
if (where != null)
|
||||
{
|
||||
for (i = 1; i <= where_count; i++)
|
||||
{
|
||||
record.CopyField(cols_count + i, where, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (View == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
LibmsiResult r = View.Execute(where);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
r = View.GetDimensions(out int row_count, out int col_count );
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
return r;
|
||||
|
||||
LibmsiRecord values = LibmsiInsertView.MsiQueryMergeRecord(col_count, Vals, record);
|
||||
if (values == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
for (i = 0; i < row_count; i++)
|
||||
{
|
||||
r = View.SetRow(i, values, (1 << col_count) - 1);
|
||||
if (r != LibmsiResult.LIBMSI_RESULT_SUCCESS)
|
||||
break;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Close()
|
||||
{
|
||||
if (View == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
return View.Close();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetDimensions(out int rows, out int cols)
|
||||
{
|
||||
rows = 0; cols = 0;
|
||||
if (View == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
return View.GetDimensions(out rows, out cols);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult GetColumnInfo(int n, out string name, out int type, out bool temporary, out string table_name)
|
||||
{
|
||||
name = null; type = 0; temporary = false; table_name = null;
|
||||
if (View == null)
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
|
||||
return View.GetColumnInfo(n, out name, out type, out temporary, out table_name);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult Delete()
|
||||
{
|
||||
if (View != null)
|
||||
View.Delete();
|
||||
|
||||
return LibmsiResult.LIBMSI_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override LibmsiResult FindMatchingRows(int col, int val, out int row, ref LibmsiColumnHashEntry handle)
|
||||
{
|
||||
row = 0;
|
||||
return LibmsiResult.LIBMSI_RESULT_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
173
BurnOutSharp/External/libmsi/Views/LibmsiView.cs
vendored
173
BurnOutSharp/External/libmsi/Views/LibmsiView.cs
vendored
@@ -1,173 +0,0 @@
|
||||
/*
|
||||
* Implementation of the Microsoft Installer (msi.dll)
|
||||
*
|
||||
* Copyright 2002-2005 Mike McCormack for CodeWeavers
|
||||
* Copyright 2005 Aric Stewart for CodeWeavers
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using LibGSF.Input;
|
||||
using LibMSI.Internal;
|
||||
|
||||
namespace LibMSI.Views
|
||||
{
|
||||
internal class column_info
|
||||
{
|
||||
public string Table { get; set; }
|
||||
|
||||
public string Column { get; set; }
|
||||
|
||||
public int Type { get; set; }
|
||||
|
||||
public bool Temporary { get; set; }
|
||||
|
||||
public expr Val { get; set; }
|
||||
|
||||
public column_info Next { get; set; }
|
||||
}
|
||||
|
||||
internal abstract class LibmsiView
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public LibmsiDBError Error { get; set; }
|
||||
|
||||
public string ErrorColumn { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Virtual Functions
|
||||
|
||||
/// <summary>
|
||||
/// Reads one integer from {row,col} in the table
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This function should be called after the execute method.
|
||||
/// Data returned by the function should not change until
|
||||
/// close or delete is called.
|
||||
/// To get a string value, query the database's string table with
|
||||
/// the integer value returned from this function.
|
||||
/// </remarks>
|
||||
public virtual LibmsiResult FetchInt(int row, int col, out int val) => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a stream from {row,col} in the table
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This function is similar to FetchInt, except fetches a
|
||||
/// stream instead of an integer.
|
||||
/// </remarks>
|
||||
public virtual LibmsiResult FetchStream(int row, int col, out GsfInput stm) => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Gets values from a row
|
||||
/// </summary>
|
||||
public virtual LibmsiResult GetRow(int row, out LibmsiRecord rec) => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Sets values in a row as specified by mask
|
||||
/// </summary>
|
||||
/// <remarks>Similar semantics to fetch_int</remarks>
|
||||
public virtual LibmsiResult SetRow(int row, LibmsiRecord rec, int mask) => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new row into the database from the records contents
|
||||
/// </summary>
|
||||
public virtual LibmsiResult InsertRow(LibmsiRecord record, int row, bool temporary) => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a row from the database
|
||||
/// </summary>
|
||||
public virtual LibmsiResult DeleteRow(int row) => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Loads the underlying data into memory so it can be read
|
||||
/// </summary>
|
||||
public virtual LibmsiResult Execute(LibmsiRecord record) => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Clears the data read by execute from memory
|
||||
/// </summary>
|
||||
public virtual LibmsiResult Close() => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of rows or columns in a table.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The number of rows can only be queried after the execute method
|
||||
/// is called. The number of columns can be queried at any time.
|
||||
/// </remarks>
|
||||
public virtual LibmsiResult GetDimensions(out int rows, out int cols) => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the name and type of a specific column
|
||||
/// </summary>
|
||||
/// <remarks>The column information can be queried at any time.</remarks>
|
||||
public virtual LibmsiResult GetColumnInfo(int n, out string name, out int type, out bool temporary, out string table_name) => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Destroys the structure completely
|
||||
/// </summary>
|
||||
public virtual LibmsiResult Delete() => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Iterates through rows that match a value
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the column type is a string then a string ID should be passed in.
|
||||
/// If the value to be looked up is an integer then no transformation of
|
||||
/// the input value is required, except if the column is a string, in which
|
||||
/// case a string ID should be passed in.
|
||||
/// The handle is an input/output parameter that keeps track of the current
|
||||
/// position in the iteration. It must be initialised to zero before the
|
||||
/// first call and continued to be passed in to subsequent calls.
|
||||
/// </remarks>
|
||||
public virtual LibmsiResult FindMatchingRows(int col, int val, out int row, ref LibmsiColumnHashEntry handle) => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Increases the reference count of the table
|
||||
/// </summary>
|
||||
public virtual int AddRef() => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Decreases the reference count of the table
|
||||
/// </summary>
|
||||
public virtual int Release() => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a column to the table
|
||||
/// </summary>
|
||||
public virtual LibmsiResult AddColumn(string table, int number, string column, int type, bool hold) => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Removes the column represented by table name and column number from the table
|
||||
/// </summary>
|
||||
public virtual LibmsiResult RemoveColumn(string table, int number) => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Orders the table by columns
|
||||
/// </summary>
|
||||
public virtual LibmsiResult Sort(column_info columns) => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Drops the table from the database
|
||||
/// </summary>
|
||||
public virtual LibmsiResult Drop() => throw new NotImplementedException();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
1105
BurnOutSharp/External/libmsi/Views/LibmsiWhereView.cs
vendored
1105
BurnOutSharp/External/libmsi/Views/LibmsiWhereView.cs
vendored
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user